← Back to Feed CACHED · 2026-09-23 10:49:42 · CACHE_KEY CVE-2026-94545
CVE-2026-94545 · CWE-116 · Disclosed 2026-09-22

Remote Code Execution in Next.js next/og Node.js ImageResponse via Satori SVG Escaping

ASSESSED — NOISGATE
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

Your social-preview image generator is a trojanized photocopier — it faithfully reproduces whatever the attacker slips into the feed tray, including their malware

CVE-2026-94545 is an unauthenticated remote code execution vulnerability in the Node.js ImageResponse implementation within next/og, the Next.js module responsible for generating Open Graph social-preview images. The root cause lives upstream in Satori (versions ≥0.0.27 <0.33.5), Vercel's JSX-to-SVG rendering library, which fails to properly escape certain values before embedding them in generated SVG output. When a Next.js 16.2.0–16.3.5 application passes attacker-controlled data — such as URL search parameters — into SVG content, attributes, or styles rendered by ImageResponse, the unescaped values are interpreted as live SVG markup. This injected markup then triggers code execution through *other upstream dependencies* in the Node.js SVG processing pipeline. The Edge runtime variant of ImageResponse is not affected, and Next.js 15.x is not vulnerable to the RCE (though 15.5.26 adds related hardening). Fixed in Next.js 16.3.6 and Satori 0.33.5.

Vercel's own advisory (GHSA-vcvr-r3jv-pc5j) rates this CRITICAL at CVSS 4.0 9.5, while the underlying Satori advisory (GHSA-wx4j-mvgx-mqwp) is rated only Moderate at 5.3. The gap reveals the real story: Satori's SVG injection alone is a moderate output-escaping flaw, but *in the specific context of Next.js Node.js ImageResponse and its dependency chain*, the injection escalates to full RCE. The 9.5 score is defensible for the subset of apps that wire untrusted input into ImageResponse, but it overstates the risk for the broader Next.js installed base. Many deployments never use ImageResponse; many that do use static content or the Edge runtime. The affected version window is narrow — only Next.js 16.2.0 through 16.3.5, released over roughly four months in mid-2026. Enterprise teams still on 14.x or 15.x are entirely unaffected. The CRITICAL label will cause unnecessary panic for the majority of Next.js operators while being absolutely appropriate for the minority running vulnerable OG-image endpoints with dynamic user input.

"Unauthenticated RCE via Next.js OG image generation, but only 16.2.x-16.3.5 on Node runtime"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Identify a Next.js 16.2.x–16.3.5 endpoint using ImageResponse

The attacker identifies a target web application running Next.js 16.2.0–16.3.5. Common indicators include the x-powered-by: Next.js header (if not suppressed), characteristic /_next/ asset paths, and the presence of dynamic OG image endpoints (e.g., /api/og, /opengraph-image). The attacker confirms the endpoint accepts user-controlled parameters that influence the rendered image.
Conditions required:
  • Target runs Next.js 16.2.0–16.3.5
  • Target uses Node.js runtime (not Edge)
  • Target exposes an ImageResponse endpoint that accepts user input
Where this breaks in practice:
  • Next.js 16.x adoption is still growing; many enterprises remain on 14.x/15.x
  • Many Next.js deployments on Vercel or Cloudflare use Edge runtime by default, which is not affected
  • Not all Next.js apps use ImageResponse at all
Detection/coverage: Wappalyzer, Shodan http.component:next.js, or HTTP header fingerprinting can identify Next.js version ranges. No specific scanner plugin for this CVE yet (1 day old).
STEP 02

Craft malicious SVG injection payload

The attacker constructs input values designed to break out of the SVG text context and inject arbitrary SVG markup. Because Satori <0.33.5 does not properly escape embedded values, carefully crafted strings in parameters like ?title=<PAYLOAD> will be rendered as live SVG elements rather than escaped text content. The payload must be structured to exploit the downstream SVG processing dependencies in the Node.js pipeline.
Conditions required:
  • Understanding of Satori's SVG generation internals
  • Knowledge of which upstream dependency in the Node.js pipeline converts SVG injection to code execution
Where this breaks in practice:
  • No public PoC exploit code exists as of 2026-09-23
  • The exact payload structure and upstream dependency exploitation path have not been disclosed
  • Satori's escaping failure pattern is known, but weaponizing it to RCE requires chaining through undisclosed upstream deps
Detection/coverage: WAF rules matching SVG injection patterns (<svg, <foreignObject>, javascript:, <script>) in query parameters could catch naive payloads.
STEP 03

Deliver payload via HTTP request to OG image endpoint

The attacker sends an unauthenticated HTTP request to the target's ImageResponse endpoint with the crafted payload embedded in a query parameter, path segment, or other user-controlled input vector. No authentication, session, or CSRF token is required — these endpoints are designed to be publicly accessible for social media crawlers. The Node.js ImageResponse handler receives the request and passes the attacker's input into Satori's SVG rendering pipeline.
Conditions required:
  • Network access to the target endpoint (typically internet-facing)
  • The application code passes the attacker-controlled value into SVG content/attributes/styles
Where this breaks in practice:
  • The application must actually interpolate user input into ImageResponse — static OG images are not vulnerable
  • Some applications may validate or sanitize input before passing to ImageResponse
Detection/coverage: HTTP request logs showing unusual characters or SVG-like markup in query parameters to OG image endpoints.
STEP 04

Achieve remote code execution on the server

The injected SVG markup is processed by Satori and passed to downstream Node.js dependencies for PNG rendering. The crafted SVG content exploits vulnerabilities in these upstream dependencies, resulting in arbitrary code execution in the Node.js server process. The attacker gains the privileges of the Node.js process, which typically has access to environment variables (containing API keys and database credentials), the filesystem, and network access for lateral movement.
Conditions required:
  • Steps 1-3 succeeded
  • Vulnerable upstream dependencies are present (bundled with affected Next.js versions)
Where this breaks in practice:
  • Container-based deployments may limit post-exploitation blast radius
  • Serverless/function-based architectures (Netlify, Vercel) isolate per-request, limiting persistence
  • Network segmentation may constrain lateral movement
Detection/coverage: Runtime application self-protection (RASP), process execution monitoring, unexpected child process spawning from Node.js workers.
03 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNo known exploitation in the wild as of 2026-09-23. Security teams have confirmed no active campaigns. Disclosed only 1 day ago (2026-09-22).
Proof-of-ConceptNo public PoC exploit code. The HORKimhab/CVE-2026-94545 GitHub repo is an empty template placeholder. Sploitus lists a reserved candidate entry with no functional exploit. The specific upstream dependency exploitation path remains undisclosed.
EPSS ScoreNot yet scored — CVE is <24 hours old. Expect EPSS to populate within 1-2 weeks. Given no PoC and no ITW exploitation, initial EPSS will likely be low-moderate.
KEV StatusNot listed in CISA Known Exploited Vulnerabilities catalog.
CVSS VectorVercel GHSA: CVSS 4.0 9.5 CriticalCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H. Satori GHSA: CVSS 4.0 5.3 ModerateCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N. The delta shows RCE impact comes from the Next.js dependency chain, not Satori alone.
Affected VersionsNext.js ≥16.2.0 <16.3.6. Satori ≥0.0.27 <0.33.5. Next.js 15.x and earlier are not affected by the RCE. Edge ImageResponse runtime is not affected.
Fixed VersionsNext.js 16.3.6 (Active LTS), Next.js 15.5.26 (hardening only, not vulnerable to RCE). Satori 0.33.5. Nuxt OG Image module also affected via Satori — see nuxt-modules/og-image#692.
Scanning / ExposurePer W3Techs (Sep 2026), Next.js powers 3.4% of all websites (~444K servers per Shodan). However, the vulnerable subset is narrow: only 16.2.x–16.3.5, only Node.js runtime, only those passing user input to ImageResponse. Realistic vulnerable population is a small fraction of the total Next.js install base.
Disclosure Date2026-09-22 — coordinated disclosure by Vercel with same-day patch release (Next.js 16.3.6).
ReportersRaghavMaheshwari124 and rafabd1 via Vercel's Open Source Bug Bounty on HackerOne.
04 · The Call

Final Verdict
DOWNGRADED to HIGH (8.0/10)

Why this verdict

  • Unauthenticated RCE is real but conditional: When the chain succeeds — Next.js 16.2.x–16.3.5, Node.js runtime, user input wired to ImageResponse — it is full unauthenticated RCE from the internet with no user interaction. This baseline impact is severe and anchors the assessment at HIGH minimum.
  • Narrow version window compresses exposure: Only Next.js 16.2.0–16.3.5 is affected (~4 months of releases in mid-2026). Enterprise Next.js deployments skew toward stable LTS tracks; many remain on 14.x or 15.x. This significantly reduces the reachable population compared to a vuln affecting all Next.js versions.
  • Feature-gated attack surface: The vulnerability requires the application to (a) use ImageResponse at all and (b) pass attacker-controlled input into SVG rendering. While this is the documented/standard pattern for dynamic OG images, it is still an opt-in feature. Apps with static OG images, no OG images, or Edge-runtime ImageResponse are unaffected.
  • Edge runtime exclusion removes major deployment class: Vercel's own platform and Cloudflare Workers use the Edge ImageResponse implementation, which is not vulnerable. This excludes a large share of production Next.js deployments from the vulnerable population.
  • No PoC, no ITW, 1-day-old disclosure: As of 2026-09-23, no public exploit code exists and no active exploitation has been observed. The specific upstream dependency exploitation path remains undisclosed. This buys defenders time but should not be relied upon — the escaping flaw in Satori is well-characterized and weaponization is a matter of when, not if.
  • Role multiplier: Next.js is an application-tier web framework. (a) *Low-value role*: dev/staging instances — RCE yields dev credentials, limited blast radius. (b) *Typical role*: production web application server — RCE yields application secrets (env vars, DB creds), customer data access, host-level compromise. Blast radius is host-to-tenant. (c) *High-value role*: Next.js could serve admin dashboards or internal tools, but it is not canonically a domain controller, identity provider, hypervisor, CI/CD engine, or backup system. The component itself does not occupy the high-value-role catalog at ≥10% of its install base. Blast radius caps at tenant/application level, not fleet or domain. No floor override applies.

Why not higher?

This does not reach CRITICAL because the vulnerable population is a narrow subset of the already-subset of Next.js 16.x deployments: only those on 16.2.0–16.3.5, only using Node.js runtime (not Edge), only passing user-controlled input to ImageResponse. Next.js is an application-tier component, not a canonical high-value infrastructure role (not a DC, hypervisor, IdP, or network edge appliance). The blast radius of successful exploitation is host-to-tenant, not fleet or domain scale. Additionally, no public PoC or ITW exploitation exists 1 day after disclosure.

Why not lower?

This cannot be MEDIUM because when the prerequisites are met, the result is unauthenticated, zero-interaction RCE from the internet — the most severe impact class. The vulnerable code pattern (interpolating searchParams into ImageResponse) is the documented standard usage shown in Next.js's own examples, so the friction of 'specific code pattern required' is lower than it appears. Next.js powers 3.4% of all websites with major enterprise adopters (GitHub, Netflix, OpenAI), so even the narrow vulnerable subset represents a meaningful number of production endpoints. A weaponized exploit is likely to emerge within weeks given the Satori escaping flaw is well-characterized.

05 · Compensating Control

What to do — in priority order.

  1. Upgrade to Next.js 16.3.6 immediately — This is the definitive fix. The upgrade patches both the Satori SVG escaping flaw and the vulnerable upstream dependencies. Deploy within 30 days per the noisgate remediation SLA for HIGH severity. Given the RCE impact and likelihood of PoC emergence, prioritize this over the 30-day window if possible.
  2. Audit all ImageResponse handlers for user-controlled input — Search your codebase for ImageResponse usage and trace whether any searchParams, params, headers, cookies, or other request-derived values flow into SVG content, attributes, or styles. If they do and you cannot immediately upgrade, remove the user-controlled interpolation or replace it with a static/allow-listed value. This is Vercel's official workaround.
  3. Switch affected ImageResponse endpoints to Edge runtime — Add export const runtime = 'edge' to route files containing ImageResponse handlers. The Edge ImageResponse implementation is not vulnerable to this RCE. This provides immediate risk elimination while you plan the version upgrade. Deploy within 30 days per noisgate mitigation SLA.
  4. Deploy WAF rules to block SVG injection in OG image endpoint parameters — Add WAF rules on your edge/CDN layer (Cloudflare, AWS WAF, Akamai) to detect and block SVG-like markup (<svg, <foreignObject>, <script>, javascript:, xmlns) in query parameters targeting your OG image endpoints (e.g., /api/og, /opengraph-image). This is defense-in-depth — it may not catch all payloads but raises the bar. Deploy within 30 days.
  5. Enforce least-privilege for Node.js application processes — Ensure Next.js processes run as non-root, with minimal filesystem permissions and no access to host-level secrets beyond what the application requires. Use container isolation (read-only root filesystem, no-new-privileges, dropped capabilities) to limit post-exploitation impact.
What doesn't work
  • Network-level IP blocking — OG image endpoints must be accessible to social media crawlers (Facebook, Twitter, LinkedIn, Slack) from diverse IP ranges, so IP allow-listing is impractical without breaking social preview functionality.
  • CSP headers — Content Security Policy is a browser-side control that has no effect on server-side SVG processing and RCE. The vulnerability executes in the Node.js process, not in a browser.
  • Upgrading Satori alone without upgrading Next.js — Next.js bundles a compiled copy of @vercel/og including Satori. Simply updating Satori in your package.json will not override the bundled version. You must upgrade Next.js itself to 16.3.6.
  • Rate limiting on OG endpoints — While rate limiting reduces brute-force attempts, a single well-crafted request is sufficient for exploitation. Rate limiting provides no meaningful protection against this vulnerability.
06 · Verification

Crowdsourced verification payload.

Run this script on any machine with access to the target application's source directory (CI runner, build server, or developer workstation). Invoke with the path to the Next.js project root: bash check_cve_2026_94545.sh /path/to/nextjs-project. No elevated privileges required — it reads package.json and node_modules only.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_94545.sh — Detect CVE-2026-94545 (Next.js ImageResponse RCE)
# Usage: bash check_cve_2026_94545.sh /path/to/nextjs-project
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -euo pipefail

PROJECT_DIR="${1:-.}"
RESULT="UNKNOWN"

if [ ! -d "$PROJECT_DIR" ]; then
  echo "[!] Directory not found: $PROJECT_DIR"
  echo "UNKNOWN"
  exit 2
fi

# Determine Next.js installed version
NEXT_VERSION=""
if [ -f "$PROJECT_DIR/node_modules/next/package.json" ]; then
  NEXT_VERSION=$(grep '"version"' "$PROJECT_DIR/node_modules/next/package.json" | head -1 | sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
elif [ -f "$PROJECT_DIR/package-lock.json" ]; then
  NEXT_VERSION=$(python3 -c "
import json, sys
with open('$PROJECT_DIR/package-lock.json') as f:
    lock = json.load(f)
    pkgs = lock.get('packages', lock.get('dependencies', {}))
    for key in pkgs:
        if key.endswith('/next') or key == 'next':
            print(pkgs[key].get('version', '')); break
" 2>/dev/null || true)
fi

if [ -z "$NEXT_VERSION" ]; then
  echo "[!] Could not determine Next.js version in $PROJECT_DIR"
  echo "UNKNOWN"
  exit 2
fi

echo "[*] Detected Next.js version: $NEXT_VERSION"

# Parse major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$(echo "$NEXT_VERSION" | sed 's/-.*//')"
MAJOR=${MAJOR:-0}; MINOR=${MINOR:-0}; PATCH=${PATCH:-0}

# Check if version is in vulnerable range: >=16.2.0 and <16.3.6
if [ "$MAJOR" -lt 16 ]; then
  echo "[+] Next.js $NEXT_VERSION is below 16.x — not affected by CVE-2026-94545."
  RESULT="PATCHED"
elif [ "$MAJOR" -gt 16 ]; then
  echo "[+] Next.js $NEXT_VERSION is above 16.x — likely patched."
  RESULT="PATCHED"
elif [ "$MAJOR" -eq 16 ]; then
  if [ "$MINOR" -lt 2 ]; then
    echo "[+] Next.js $NEXT_VERSION is below 16.2.0 — not affected."
    RESULT="PATCHED"
  elif [ "$MINOR" -eq 2 ]; then
    echo "[!] Next.js $NEXT_VERSION is in vulnerable range (16.2.x)."
    RESULT="VULNERABLE"
  elif [ "$MINOR" -eq 3 ] && [ "$PATCH" -lt 6 ]; then
    echo "[!] Next.js $NEXT_VERSION is in vulnerable range (16.3.0-16.3.5)."
    RESULT="VULNERABLE"
  else
    echo "[+] Next.js $NEXT_VERSION is >=16.3.6 — patched."
    RESULT="PATCHED"
  fi
fi

# If vulnerable, check for ImageResponse usage
if [ "$RESULT" = "VULNERABLE" ]; then
  echo "[*] Checking for ImageResponse usage in source files..."
  OG_USAGE=$(grep -rl 'ImageResponse\|next/og\|@vercel/og' "$PROJECT_DIR/src" "$PROJECT_DIR/app" "$PROJECT_DIR/pages" 2>/dev/null | head -20 || true)
  if [ -n "$OG_USAGE" ]; then
    echo "[!] ImageResponse / next/og usage found in:"
    echo "$OG_USAGE" | while read -r f; do echo "    $f"; done
    echo ""
    echo "[!] Check whether these files pass user-controlled input (searchParams, headers, etc.) into ImageResponse SVG content."
  else
    echo "[*] No ImageResponse / next/og usage detected in src/, app/, or pages/ directories."
    echo "[*] If ImageResponse is not used, this deployment is not exploitable (but upgrading is still recommended)."
  fi
fi

echo ""
echo "$RESULT"
exit $([ "$RESULT" = "PATCHED" ] && echo 0 || ([ "$RESULT" = "VULNERABLE" ] && echo 1 || echo 2))
07 · Sources

Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.