CVE-2025-68613: n8n Expression Sandbox Escape to Unauthenticated RCE
Updated July 28, 2026 · Written by PWNMI — see About.
n8n is a popular open-source workflow automation platform — the kind of tool that ends up with real credentials and real API access wired into it, since its entire purpose is connecting services together. CVE-2025-68613 is a sandbox escape in its expression evaluator, and while the bug itself needs authentication, this lab reproduces the version CISA lists as actively exploited: chained with a second, unrelated CVE to become fully unauthenticated remote code execution.
Affects n8n 0.211.0 through 1.120.3 for the sandbox-escape RCE. CVSS 9.9. Environment and exploit script from vulhub's own n8n lab, tracing to Chocapikk/CVE-2026-21858.
What you'll practice: understanding how a scripting sandbox can be escaped by reaching for language internals (this, global objects) the sandbox never actually restricted, seeing how two unrelated CVEs in the same application can combine into something worse than either alone, and — the recurring theme across every lab on this site — not trusting a tool's own success message.
Set up the lab
New to Docker or git, or unsure what the commands below are actually doing? See Docker for Security Labs and Git for Security Work first.
Get the lab files from pwnmihq/resources:
git clone --filter=blob:none --sparse https://github.com/pwnmihq/resources
cd resources
git sparse-checkout set cve-labs/CVE-2025-68613
cd cve-labs/CVE-2025-68613
Kali (and any modern Debian-based distro) blocks pip install straight into system Python by default — a venv is the fix, not --break-system-packages:
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
docker compose up -d
This starts n8n 1.65.0, pre-configured with a document-submission workflow exposing a form at /form/vulnerable-form. No ports are published — reach it on its own IP:
CONTAINER=$(docker compose ps -q n8n)
IP=$(docker inspect $CONTAINER --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
Understanding the vulnerability
n8n lets workflow node parameters contain JavaScript expressions wrapped in {{ }}, evaluated server-side at runtime — this is core to how the platform works, not an obscure feature. The evaluation happens inside a sandbox meant to restrict what that expression can reach. The sandbox escape here is almost elegant in how simple it is:
{{ (function(){ return this.process.mainModule.require('child_process').execSync('id').toString() })() }}
Wrapping the payload in a function and using this inside it reaches a context the sandbox didn't actually restrict — from there, process.mainModule.require('child_process') pulls in Node's own child-process module, and execSync() runs whatever command you hand it, with the privileges of the n8n server process itself.
On its own, this requires an authenticated session to create the malicious workflow. What makes this lab's target genuinely dangerous is a second, completely unrelated bug — a Content-Type confusion that lets an unauthenticated request read arbitrary files off the server's disk. Chained together: read n8n's SQLite database and its encryption key straight off disk with zero authentication, use the key to forge a valid signed session token for the admin account, then use that forged session to create and trigger the sandbox-escape workflow. Two independently unremarkable-sounding bugs — an auth bypass here, a sandbox gap there — combine into full unauthenticated remote code execution.
Exploitation
The lab includes exploit.py (from the public research this CVE's disclosure references), which automates every stage of the chain:
python3 exploit.py http://$IP:5678 /form/vulnerable-form --cmd "id > /tmp/pwnmi-verify-success; hostname >> /tmp/pwnmi-verify-success"
Watch the output — it walks through each stage explicitly: reading the encryption key and database unauthenticated, forging an admin token, confirming "Admin access: GRANTED!", creating and triggering the malicious workflow, and finally reporting RCE: OK.
That last line is a claim, not proof. Confirm the real effect directly:
docker exec $CONTAINER cat /tmp/pwnmi-verify-success
Genuine output — uid=0(root) gid=0(root) groups=0(root) and a hostname matching the container's own short ID — is what actually confirms it. The tool's own log is a helpful narrative of what it attempted; the file on disk is what proves what actually happened.
Cleanup
Look back at exploit.py's own output: it includes a line reading Cleanup: OK, meaning the tool claims to delete the temporary workflow it created to trigger the sandbox escape. Treat that exactly the same way you'd treat RCE: OK — as a claim, not a verified fact. This lab's own verification never independently confirmed the workflow was actually removed (checking n8n's workflow list before and after would settle it); worth doing that check yourself before assuming the tool's self-reported cleanup is accurate. The forged admin session itself needs no cleanup of its own — it's a signed token, not a stored object, so there's nothing in n8n's database left behind by the authentication bypass itself. The one confirmed artifact is whatever the injected command did — here, /tmp/pwnmi-verify-success:
docker exec $CONTAINER rm -f /tmp/pwnmi-verify-success
Tear the lab down when you're done:
docker compose down -v
Lessons worth keeping
- A sandbox is only as strong as its weakest boundary. This escape didn't need an exotic technique — it needed one context (
thisinside a function call) that the sandbox's designers didn't anticipate would leak access to Node's globals. Every sandbox implementation is a list of things it restricts; the interesting question is always what's missing from that list. - Two mediocre bugs can combine into a severe one. Neither the authenticated sandbox escape nor the unauthenticated file-read bug, considered alone, is as dangerous as the two of them together. When you find one vulnerability, it's worth asking what else in the same application might remove the precondition that's currently limiting its impact.
- A workflow-automation platform is a high-value target precisely because of what it's designed to do. Tools built to connect services together tend to accumulate real credentials and real API access as a natural consequence of being useful — which is exactly why RCE against one is rarely just "one server compromised."
Next step
For another real, KEV-listed CVE verified the same rigorous way, see CVE-2026-63030 (WordPress core) or CVE-2025-54068 (Laravel Livewire). For the concepts behind the isolation pattern used here, see Docker for Security Labs.
Get new write-ups in your inbox
New roadmaps, tool walkthroughs, and lab write-ups. No spam. Unsubscribe anytime.