Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-32096

CVE-2026-32096: Useplunk Plunk SSRF Vulnerability

CVE-2026-32096 is an SSRF flaw in Useplunk Plunk that allows unauthenticated attackers to force the server to make arbitrary HTTP requests. This article covers the technical details, affected versions, and mitigation.

Updated:

CVE-2026-32096 Overview

Plunk is an open-source email platform built on top of AWS Simple Email Service (SES). Versions prior to 0.7.0 contain a Server-Side Request Forgery (SSRF) vulnerability [CWE-918] in the Simple Notification Service (SNS) webhook handler. An unauthenticated attacker can send a crafted request that forces the server to issue an arbitrary outbound HTTP GET request to any host reachable from the server. This enables attackers to probe internal networks, access cloud metadata services, or interact with internal services that trust the application host. The vulnerability is fixed in Plunk version 0.7.0.

Critical Impact

Unauthenticated remote attackers can force the Plunk server to send outbound HTTP GET requests to arbitrary hosts, enabling internal network reconnaissance and potential exposure of cloud metadata or internal services.

Affected Products

  • Useplunk Plunk versions prior to 0.7.0
  • Self-hosted Plunk deployments using the SNS webhook handler
  • Containerized Plunk instances accessible to untrusted network sources

Discovery Timeline

  • 2026-03-11 - CVE-2026-32096 published to NVD
  • 2026-03-16 - Last updated in NVD database

Technical Details for CVE-2026-32096

Vulnerability Analysis

The vulnerability resides in the SNS webhook controller at apps/api/src/controllers/Webhooks.ts. Plunk integrates with AWS SES through SNS notifications, which deliver subscription confirmations and event callbacks to a webhook endpoint. When SNS sends a SubscriptionConfirmation message, it includes a SubscribeURL field that the receiver must fetch to confirm the subscription.

The vulnerable code accepted the SubscribeURL value from the request body without validation and used it directly in a fetch call. Because the webhook endpoint does not require authentication, any remote attacker could submit a forged SNS payload containing an attacker-controlled URL. The server would then issue an outbound HTTP GET request to that destination, returning the response context to the attacker through timing or error signals.

Root Cause

The root cause is missing input validation on the SubscribeURL parameter in the SNS subscription confirmation flow. The handler trusted the URL string supplied in the webhook body and did not verify the protocol scheme or hostname before initiating an outbound request. This is a classic SSRF pattern where user-controlled input flows into a server-side network client.

Attack Vector

An unauthenticated attacker sends an HTTP POST request to the Plunk SNS webhook endpoint with a JSON body containing Type: SubscriptionConfirmation and a SubscribeURL pointing to an internal target. Targets of interest include http://169.254.169.254/ for cloud instance metadata, http://localhost for internal admin services, or private RFC1918 addresses for lateral reconnaissance. The server-initiated request originates from the trusted application host and may bypass network-level access controls.

typescript
// Patch from apps/api/src/controllers/Webhooks.ts
// Source: https://github.com/useplunk/plunk/commit/b8f1ad9ab53c78f8ef063fdc125f397c8bfc7652

// Handle SNS subscription confirmation FIRST (before parsing Message field)
if (req.body.Type === 'SubscriptionConfirmation') {
  signale.info('SNS Subscription Confirmation received');

  // Validate SubscribeURL to prevent SSRF: must be HTTPS and from an official AWS SNS host.
  // Legitimate URLs look like:
  //   https://sns.<region>.amazonaws.com/?Action=ConfirmSubscription&...
  const subscribeURL: unknown = req.body.SubscribeURL;
  if (typeof subscribeURL !== 'string') {
    signale.warn('SNS SubscriptionConfirmation missing SubscribeURL');
    return res.status(400).json({success: false, message: 'Invalid SubscribeURL'});
  }

  let parsedURL: URL;
  try {
    parsedURL = new URL(subscribeURL);
  } catch {
    return res.status(400).json({success: false, message: 'Invalid SubscribeURL'});
  }

  // Only allow HTTPS requests to official AWS SNS endpoints.
  const SNS_HOST_RE = /^sns\.[a-z0-9-]+\.amazonaws\.com$/;
  if (parsedURL.protocol !== 'https:' || !SNS_HOST_RE.test(parsedURL.hostname)) {
    return res.status(400).json({success: false, message: 'Invalid SubscribeURL'});
  }
}

The fix enforces two checks: the URL protocol must be https:, and the hostname must match the regex ^sns\.[a-z0-9-]+\.amazonaws\.com$, restricting outbound calls to legitimate AWS SNS endpoints.

Detection Methods for CVE-2026-32096

Indicators of Compromise

  • Inbound POST requests to the Plunk SNS webhook endpoint with Type: SubscriptionConfirmation and SubscribeURL values that do not match https://sns.<region>.amazonaws.com/
  • Outbound HTTP GET requests from the Plunk application host to internal IP ranges (RFC1918), 169.254.169.254, or localhost
  • Webhook payloads containing URL schemes other than https: in the SubscribeURL field

Detection Strategies

  • Inspect web server and reverse proxy logs for requests to the SNS webhook path containing non-AWS hostnames in the body
  • Correlate inbound webhook events with subsequent outbound connections from the application host within a short time window
  • Apply egress filtering rules that alert on Plunk hosts initiating connections to metadata services or private network ranges

Monitoring Recommendations

  • Enable detailed application logs for the Plunk Webhooks controller to capture every SubscribeURL value processed
  • Monitor cloud provider VPC flow logs for unexpected egress destinations from the Plunk workload
  • Alert on access attempts to 169.254.169.254 originating from any application container or pod

How to Mitigate CVE-2026-32096

Immediate Actions Required

  • Upgrade Plunk to version 0.7.0 or later, which contains the validated SubscribeURL check
  • Audit application and proxy logs for prior unauthorized webhook submissions targeting internal hosts
  • Rotate any credentials accessible from the application host that may have been exposed through cloud metadata SSRF

Patch Information

The fix is committed in b8f1ad9ab53c78f8ef063fdc125f397c8bfc7652 and shipped in Plunk 0.7.0. Review the GitHub Security Advisory GHSA-xpqg-p8mp-7g44 and the upstream commit for full diff context.

Workarounds

  • Place the Plunk SNS webhook endpoint behind an authenticating reverse proxy that validates SNS message signatures before forwarding
  • Apply strict egress firewall rules that limit outbound traffic from the Plunk host to known AWS SNS endpoints only
  • Block outbound access from the application host to 169.254.169.254 and RFC1918 ranges where not required
bash
# Example egress restriction using iptables on the Plunk host
# Block access to cloud instance metadata service
iptables -A OUTPUT -d 169.254.169.254 -j DROP

# Restrict outbound HTTPS to AWS SNS hostnames only (resolved IPs)
# Combine with a DNS-aware egress proxy for hostname-based control
iptables -A OUTPUT -p tcp --dport 80 -j DROP

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.