T09 · Insecure Skill Coding Practices
- Location
- listener/clawcall-listener.js:217
- Finding
- Caller-Controlled Command Injection Through Windows Shell Invocation<![CDATA[ ## Vulnerability Details **File Location**: `listener/clawcall-listener.js:217-225` and `listener/clawcall-listener.js:258-262` **Vulnerability Type**: OS command injection **Risk Level**: Critical ### Vulnerable Code ```js const { call_sid, message } = res; console.log(`[ClawCall] ↓ call_sid=${call_sid} message="${message}"`); const t0 = Date.now(); const { reply, end_call } = await runAgentTurn(message, call_sid); ``` ```js // On Windows, shell:true is required to resolve openclaw.cmd/.ps1 from PATH. // On Mac/Linux, shell:false is sufficient and avoids an extra shell layer. const proc = spawn( "openclaw", ["agent", "--session-id", callSid, "--message", message, "--json"], { shell: process.platform === "win32", windowsHide: true, stdio: ["pipe", "pipe", "pipe"], } ); ``` ### Technical Analysis The `call_sid` and transcribed `message` values originate from the remote ClawCall API and are passed as command arguments to `child_process.spawn`. On Windows, the code enables `shell: true`. This causes Node.js to construct a command line that is interpreted by the Windows command shell instead of invoking the target executable directly. Node.js explicitly warns against passing unsanitized input to a shell-backed child process. A malicious caller may include Windows shell metacharacters, quoting sequences, variable expansions, or command separators in transcribed speech. If those characters survive transcription and command-line construction, they can alter the intended command and cause additional commands to execute. ### Attack Path 1. An attacker places or participates in a call routed to the listener. 2. The attacker supplies speech that is transcribed into shell-significant characters or syntax. 3. The ClawCall API returns the transcript as `message`. 4. The listener passes `message` to `runAgentTurn`. 5. On Windows, `spawn` invokes OpenClaw through a command shell because `shell: true`. 6. The shell interprets ...[truncated 681 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Never use `shell: true` when any argument contains remote or otherwise untrusted data. - Resolve the trusted `openclaw.cmd` or executable path explicitly and invoke it with `execFile` or `spawn` using `shell: false`. - Strictly validate `callSid` against the exact expected identifier format, such as a conservative alphanumeric regular expression and maximum length. - Apply a maximum length and control-character policy to call transcripts before process invocation. - Prefer passing large prompts or messages through standard input rather than command-line arguments. - Run the listener under a dedicated, unprivileged operating-system account. - Add Windows security tests containing characters such as `&`, `|`, `^`, `%`, quotes, parentheses, newlines, and redirection operators. ]]>
