Blog

min read

Zero Click Unauthenticated RCE in n8n: A Contact Form That Executes Shell Commands

By

Eilon Cohen

and

March 11, 2026

min read

Executive Summary

Pillar Research team found a zero-click, unauthenticated RCE in n8n. Anyone who can reach a public multi-step form with an HTML rendering can execute shell commands on the server. We worked with the n8n team to fix it. If you use n8n Cloud, you're already protected. If you're self-hosting, update to 2.10.1 / 2.9.3 / 1.123.22 now.

This is CVE-2026-27493: an unauthenticated, zero-click RCE affecting every n8n instance that exposes a multi-step form with an HTML rendering step that displays user input back to the submitter. We scanned for publicly accessible n8n form endpoints and identified over 50,000 potentially vulnerable forms exposed to the internet. The attack requires nothing more than a browser.

n8n is a workflow automation platform used by 230,000+ organizations with over 100 million Docker pulls (per n8n's public metrics). It stores credentials for every system it connects to: AWS keys, database passwords, OAuth tokens, API keys. A single RCE exposes the entire credential vault and every connected system.

In December 2025, we disclosed two sandbox escape vulnerabilities (previous post). n8n responded quickly, shipping nine targeted security fixes across multiple sanitizers in v2.4.0 (including some bypasses reported by other folks in the industry). Those fixes were correct for the bypasses they addressed. They were also irrelevant to what we found next, because the new bypass operates at a different stage of the pipeline entirely, before any of those fixes execute.

We're publishing two new findings. CVE-2026-27577 is a sandbox escape in the expression compiler: a missing case in the AST rewriter lets process slip through untransformed, giving any authenticated expression full RCE. CVE-2026-27493 takes it further. A double-evaluation bug in n8n's Form nodes turns any multi-step form that displays user input back into an expression injection point. The form endpoints are public by design. No authentication. No n8n account. No workflow access. A public "Contact Us" form will run arbitrary shell commands if you type a payload into the Name field.

Both self-hosted and n8n Cloud deployments are affected. Our previous research demonstrated that sandbox escapes on n8n Cloud grant access to shared infrastructure, creating cross-tenant risk. The same expression engine is shared between self-hosted and cloud; these findings carry the same implications.

CVE Advisory Description CVSS v4.0 Auth Required
CVE-2026-27493 GHSA-75g8-rv7v-32f7 Form Node double-evaluation expression injection 9.5 Critical No
CVE-2026-27577 GHSA-vpcf-gvg4-6qwr SpreadElement sandbox escape in
@n8n/tournament
9.4 Critical Yes

CVSS v4.0 vectors:

Recommendations

Update immediately. Patch to n8n 2.10.1, 2.9.3, or 1.123.22 (depending on your release channel).

Audit Form nodes. Any multi-step form that displays user input back (greetings by name, submission summaries, confirmation pages) is a candidate for the double-evaluation vulnerability. Public-facing forms are the priority: they require no authentication to exploit.

Rotate all stored credentials if a vulnerable workflow found in your environment. Any instance running an affected version could have exposed N8N_ENCRYPTION_KEY, which decrypts every credential stored in the platform.

The Unauthenticated Endpoint: Form Node Double-Evaluation (CVE-2026-27493)

n8n's Form nodes are unauthenticated by design. They create public-facing endpoints at /form/*, and the product is explicitly built so that anyone on the internet can submit data to a form workflow.

Here's the scenario: a business user builds a multi-step "Contact Us" form. Step 1 collects a name and email. Step 2 displays a confirmation: "Thank you, {{Name}}!" No code required, no code is added, so no code is run, right? Nope.

What you just saw: an attacker visits a public n8n form, types a payload into the Name field, submits, and gets back the output of a shell command executed on the server.

How it works at a high level

The form is designed to display user input back: "Thank you, Alice!" But when the server renders that confirmation page, it processes the content twice. The first pass substitutes the user's input into the page template. The second pass scans the result for anything that looks like an expression and evaluates it. If the user typed an expression instead of a name, the second pass treats it as code and runs it.

Technical breakdown

The vulnerability is a double-evaluation: two expression evaluation passes, with user input flowing between them unsanitized.

Pass 1: getNodeParameter resolves configured expressions. The HTML field ={{ $input.first().json["Name"] }} evaluates to whatever the user submitted. If the user typed a payload containing {{ }} expression syntax, it passes through intact as a literal string value.

Pass 2: prepareFormFields scans the already-resolved HTML for {{ }} patterns using getResolvables() and evaluates each one via evaluateExpression(). It finds the attacker's payload embedded in the resolved HTML. It evaluates it as a new expression.

The attack flow:

  1. Attacker visits /form/contact-us (no authentication)
  2. Submits the RCE payload in the Name field
  3. Step 2 loads, the "Thank You" page renders
  4. Pass 1 injects the payload string into the HTML
  5. Pass 2 finds the {{ }} pattern and evaluates it
  6. The SpreadElement bypass fires. RCE achieved.
Attacker submits form → Pass 1 injects payload into HTML → Pass 2 evaluates it → RCE

In practice, the exploit is a single form submission followed by one GET request:

POST /form/contact-us HTTP/1.1
Content-Type: application/x-www-form-urlencoded

Name={{ PAYLOAD }}&Email=test@test.com

-> GET /form-waiting/<executionId>

-> uid=1000(node) gid=1000(node)

Where PAYLOAD is a one-liner that uses the SpreadElement bypass (see CVE-2026-27577 technicals below) to access Node.js builtins and run shell commands.

The double-evaluation stands alone

The double-evaluation is a logic bug in the Form node. Even if the SpreadElement bypass is patched, the expression injection persists. An attacker can still:

  • Read environment variables accessible to expressions ($env.*)
  • Exfiltrate workflow data, node outputs, and execution context
  • Escalate to full RCE via any future sandbox escape

Patching the sandbox does not fix the injection. The relationship is the same as between SQL injection and a specific database function: fixing the function leaves the injection intact.

The Sandbox Escape: SpreadElement Bypass (CVE-2026-27577)

n8n's expression engine compiles user expressions through @n8n/tournament before executing them. The compiler's VariablePolyfill transformer rewrites dangerous global identifiers (like process) into safe sandbox lookups by checking each identifier's parent AST node type against a switch statement with cases for over 130 AST parent node types (about 30 with active rewriting logic, plus over 100 explicit no-ops).

SpreadElement wasn't in the list.

={{ ((g) => g.getBuiltinModule('child_process').execSync('id').toString())({...pr
ocess}) }}

One line. {...process} places the process identifier inside a SpreadElement node. The rewriter's switch statement has no case for it, falls through to default, and performs no rewriting. The bare process resolves to the real Node.js global. From there: getBuiltinModule('child_process') (available on n8n's required Node.js ≥ 22.16) returns the child_process module, and the attacker has full RCE.

Why this bypass is different

Our previous bypasses (template literals, Object.defineProperty) exploited gaps in the runtime sanitizers: the PrototypeSanitizer, FunctionThisSanitizer, and related checks in packages/workflow/src/expression-sandboxing.ts. All fixes in v2.4.0 hardened those runtime layers.

The SpreadElement bypass operates at the compilation stage in @n8n/tournament, a separate package that runs before any runtime sanitizer. The identifier escapes during AST rewriting. By the time the expression reaches the runtime sanitizers, process is already a bare global reference in the compiled code. The PrototypeSanitizer, the FunctionThisSanitizer, the expanded blocklists, the immutable __sanitize function, the destructuring protections: none of them run on the identifier because it was never rewritten.

TypeScript didn't catch the gap either. In ast-types, SpreadElement is a standalone node type not included in any of the union types that the switch statement's type narrowing covers. TypeScript considered the switch exhaustive. At runtime, SpreadElement parents silently fell through to the no-op default.

Patch Details

Based on PR #26214 (Bundle 2026-W7, merged February 25, 2026):

  • CVE-2026-27493: n8n removed the second expression evaluation pass from prepareFormFields entirely. The function no longer accepts a context parameter or calls evaluateExpression. The getResolvables + evaluateExpression loop in utils.ts was deleted. Single-pass rendering eliminates the injection.
  • CVE-2026-27577: n8n added process, require, module, Buffer, and other Node.js globals to the expression sandbox's blocked identifier list in expression.ts and hardened AST-aware identifier analysis in expression-sandboxing.ts. This is defense-in-depth at the runtime layer, complementing any changes in @n8n/tournament.

Impact

Both vulnerabilities affect all n8n versions prior to 2.10.1 / 2.9.3 / 1.123.22, including both self-hosted and n8n Cloud deployments.

Post-exploitation is straightforward. The attacker reads the N8N_ENCRYPTION_KEY environment variable and uses it to decrypt every credential stored in n8n's database: AWS keys, database passwords, OAuth tokens, API keys. n8n is a credential vault by function. It stores keys to every system it connects to. A single sandbox escape exposes the n8n instance and every connected system.

The unauthenticated path (CVE-2026-27493) is worse. Multi-step forms that display user input back ("Thank you, [Name]!") are a common, natural pattern. The form endpoints are public by design and internet-facing. The attacker controls the entire flow with a single form submission and a GET request.

For n8n Cloud and multi-tenant deployments, the impact extends beyond the individual instance. As demonstrated previously, sandbox escapes on n8n Cloud grant access to shared infrastructure, creating cross-tenant risk: a single public form on one tenant's workflow could serve as the entry point. We assess the same cross-tenant risk applies based on the shared expression engine and infrastructure architecture confirmed during our earlier research.

Disclosure Timeline

Date Event
December 21, 2025 Round 1: Template literal + arrow function + prepareStackTrace chain discovered
December 21, 2025 Reported to n8n security team
December 22, 2025 n8n confirms vulnerability and cloud impact
December 23, 2025 n8n deploys initial patch (PR #23560)
December 24, 2025 Round 1.5: Object.defineProperty bypass discovered and reported
January 2026 n8n ships v2.4.0 with 9 security fixes
February 3, 2026 Coordinated public disclosure of Round 1
February 7, 2026 Round 2: SpreadElement bypass of all v2.4.0 fixes discovered
February 2026 Form Node double-evaluation discovered
February 2026 Round 2 reported to n8n
February 25, 2026 n8n publishes patches (2.10.1, 2.9.3, 1.123.22) and advisories

Credits

Advisories:

Previous blog post: Critical Vulnerabilities in n8n Expose Hundreds of Thousands of Enterprise AI Systems to Complete Takeover

Acknowledgments: The n8n security team responded to every disclosure, including over the holidays, with speed and professionalism. The structural issues we describe are industry-wide. n8n's willingness to engage seriously with each finding made this research possible.

This research was conducted by Pillar Security's research team. For questions about this research, contact research@pillar.security or eilon@pillar.security.

Subscribe and get the latest security updates

Back to blog

MAYBE YOU WILL FIND THIS INTERSTING AS WELL

AI Coding Tools Under Fire: Mapping the Malvertising Campaigns Targeting the Vibe Coding Ecosystem

By

Eilon Cohen

and

March 10, 2026

Research
Hackerbot-Claw: Adversarial Agent Targets Top GitHub Repos

By

Eilon Cohen

and

March 3, 2026

Research
Your AI Agent Will Run Untrusted Code. Now What?

By

Eilon Cohen

and

Ariel Fogel

February 25, 2026

Research