Jun 17, 2026·6 min read·10 visits
Authenticated users can bypass n8n's Python Code Node AST validator, escaping the execution sandbox to access host environment variables and process namespaces.
An authenticated security-bypass vulnerability in n8n allows users with workflow creation or modification privileges to bypass the Python AST security validator. By circumventing AST validation logic, attackers can execute arbitrary statements, access the task executor's root module namespace, and disclose sensitive host environment variables on self-hosted instances.
The n8n workflow automation platform exposes an extensive attack surface through its Code node, which permits administrators and authorized editors to run custom JavaScript or Python scripts. To support Python execution safely, n8n invokes a separate helper runtime called the Python Task Runner. The platform relies on a static analysis protection mechanism to prevent users from executing arbitrary system actions or escaping their local task environment.
This protection mechanism is implemented as an Abstract Syntax Tree (AST) security validator. The validator parses the incoming script structure and prevents execution if it encounters blacklisted nodes, references to dangerous methods, or access to sensitive global fields. This control aims to keep the user script isolated within a limited virtual execution environment.
GHSA-jwm3-qcfw-c5pp describes a critical logic flaw in this validator that allows authenticated workflow creators to evade detection rules. Successful evasion allows the execution of restricted statements that bypass standard execution limits. This escape grants immediate access to the module namespace of the task executor process, risking system-wide credential exposure.
The root cause of this vulnerability lies in the structural incompleteness of the AST verification routines within the n8n Python parsing logic. The security validator utilizes a static checking framework that parses code into an AST and iterates over specific node types to check for prohibited patterns. Specifically, it searches for direct node classifications like ast.Import or standard attribute lookups (ast.Attribute) that match sensitive strings.
However, this implementation fails to recursively and securely analyze nested expressions inside complex AST nodes, such as formatted string literals represented by ast.JoinedStr or dynamic functions like getattr. An f-string containing nested expressions evaluates dynamically during run-time, but its internal variables can easily escape simple static keyword matching systems. By embedding sensitive references within the curly brackets of formatted strings, an attacker forces evaluation at runtime without triggering static parser blocks.
Furthermore, static checks for specific string literals (such as globals or subclasses) are trivial to circumvent using dynamic key retrieval. If the validator permits calls to the built-in getattr() function, an attacker can dynamically piece together forbidden attribute strings through string concatenation. The AST analyzer only sees normal string variables and benign functions, missing the conversion of these inputs into restricted attribute calls.
To analyze this flaw, we can observe the difference between standard static checks and the dynamic bypass paths. In a vulnerable validator implementation, the checking function often matches attribute strings directly against a set of known forbidden attributes.
# Vulnerable logic representation
def validate_node(node):
if isinstance(node, ast.Attribute):
if node.attr in ['__globals__', '__subclasses__', '__builtins__']:
raise SecurityError("Forbidden attribute accessed")The code above fails to catch attempts where the restricted properties are retrieved indirectly. The following representation demonstrates how dynamic access circumvents the static filter entirely.
# Dynamic evasion pattern bypasses ast.Attribute check
class_ref = _get_data.__class__
# getattr accepts a constructed string which is not flagged as ast.Attribute
globals_dict = getattr(class_ref, '__glo' + 'bals__')The fix introduced in n8n enforces robust AST validation paths that intercept dynamic attribute resolution and prohibit hazardous functions such as getattr when executed under the sandbox context. Additionally, nested structures in ast.JoinedStr are subject to strict recursive checking to ensure no execution boundaries are bypassed.
Exploitation of this vulnerability requires the attacker to possess authenticated access to an n8n instance with permissions to create or modify workflows. This requirement is standard for users with Owner or Member privileges on self-hosted deployments. The attack is entirely remote and does not require user interaction from other operators on the system.
The attacker begins by creating a workflow and inserting a Code node configured to execute Python. The attacker then writes a script designed to reconstruct forbidden attributes at runtime. The script leverages built-in objects like _get_data or _context which are automatically injected into the task context by n8n.
# Conceptual payload extracting system environment
def exploit():
g_attr = "{0}{1}{0}".format("__", "globals")
globals_dict = getattr(_get_data, g_attr)
sys_module = globals_dict["sys"]
os_module = sys_module.modules["os"]
return [{"leak": dict(os_module.environ)}]
result = exploit()The payload executes string manipulation to yield the target namespace. Once the attacker obtains the dictionary of globals, they extract the sys module, access the system modules cache, and retrieve the original os module. Finally, the payload extracts host environment variables and outputs them directly as a workflow result, allowing simple extraction via the n8n execution interface.
The impact of this security bypass is classified under unauthorized information disclosure. By gaining access to the root process namespace, an attacker defeats the execution isolation rules designed to restrict the task runner process. On self-hosted environments where environment access is configured, this escape allows the disclosure of sensitive host parameters.
In environments where the configuration flag N8N_BLOCK_RUNNER_ENV_ACCESS is set to true, the wrapper process executes logic to obscure the active environment variables. However, because the AST bypass provides direct access to the parent namespace and raw system module arrays, the attacker can traverse memory structures and recover sensitive configuration items. This bypass negates the protection provided by the isolation flag.
The disclosed information can contain database credentials, server API keys, connection strings, and third-party configuration details used by n8n. These secrets can then be used to pivot deeper into internal network environments. The CVSS score of 5.1 represents a medium severity impact due to the prerequisite of low-privilege authentication.
Remediating this vulnerability requires immediate updates to the underlying n8n deployment. Systems running versions prior to 2.25.7 must be upgraded directly to 2.25.7 or later. Deployments utilizing the 2.26.x branch must be updated to version 2.26.2 or subsequent releases to ensure all parser gaps are fully patched.
When direct upgrades are delayed due to operational requirements, administrators should apply environment controls to mitigate risk. Setting the environment variable NODES_EXCLUDE=["n8n-nodes-base.code"] disables the Code node globally, effectively removing the execution vector. Administrators can also restrict workflow creation privileges strictly to trusted security roles.
Finally, organizations should establish robust detection and log-hunting routines. Analyze workflow execution logs and inspect the n8n database for code structures containing obfuscated attribute lookups or unusual dynamic string functions. Monitor system executions to ensure the Python runner process does not spawn unexpected shell processes or engage in outbound external network traffic.
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
n8n n8n-io | < 2.25.7 | 2.25.7 |
n8n n8n-io | >= 2.26.0, < 2.26.2 | 2.26.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-184 / CWE-265 |
| Attack Vector | Network (Low Privileges) |
| CVSS Score | 5.1 (Medium) |
| EPSS Score | N/A (No CVE Assigned) |
| Impact | Information Disclosure / Sandbox Escape |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
The program checks input values against an incomplete list of disallowed values, allowing attackers to construct alternative representations that bypass filters.
An authenticated file upload validation bypass vulnerability exists in the REDAXO CMS Mediapool addon in versions 5.18.2 through 5.21.0. Under permissive web server configurations, this allows authenticated users with media upload privileges to achieve remote code execution via multi-segment extension file uploads.
A critical SQL injection vulnerability exists in the @nocobase/plugin-notification-in-app-message plugin of NocoBase prior to version 2.0.61. The flaw is caused by direct string interpolation of user-controlled input into a Sequelize.literal() query, allowing authenticated users to execute stacked PostgreSQL queries and achieve remote code execution on the underlying database server.
An incomplete default configuration vulnerability in sanitize-html prior to version 2.17.5 allows remote attackers to execute arbitrary JavaScript code via crafted HTML payloads containing neglected URI-bearing attributes (e.g., action, formaction, data, xlink:href) that bypass input validation logic.
A critical server-side prototype pollution vulnerability in ApostropheCMS versions up to and including 4.30.0 allows authenticated editors to write arbitrary properties to the global Object.prototype via patch operators. Exploiting a confirmed gadget in publicApiCheck() bypasses authorization on all piece-type REST API endpoints framework-wide, persisting for the lifetime of the Node.js process.
An unauthenticated Server-Side Request Forgery (SSRF) vulnerability exists in ApostropheCMS versions up to and including 4.30.0. When the prettyUrls option is enabled in the @apostrophecms/file module, the server constructs internal self-requests using the client-provided HTTP Host header, allowing remote attackers to coerce the server into initiating outbound requests to arbitrary internal or external hosts.
A stored Cross-Site Scripting (XSS) vulnerability exists in the @apostrophecms/seo package of the ApostropheCMS ecosystem up to and including version 1.4.2. Unsanitized user inputs for Google Analytics and Google Tag Manager IDs are injected directly into script elements within the document header, enabling authenticated editors to execute arbitrary JavaScript in the context of all site visitors.