T09 · Insecure Skill Coding Practices
Error
- Location
- handler.js:11
- Finding
- Host Shell Command Injection Through Unsafe tmux Invocation<![CDATA[ ## Vulnerability Details **File Location**: `handler.js`, lines 11-14 **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```js function sendCommand(cmd) { const escaped = cmd.replace(/"/g, '\\"'); execSync(`tmux send-keys -t claw "${escaped}" C-m`); } ``` ### Technical Analysis The user-controlled `cmd` value is interpolated into a command string passed to `execSync`. Node.js consequently invokes a host shell to interpret the resulting string. Escaping only double quotes is insufficient because content inside double quotes still supports shell command substitution through `$(...)` and backticks. Shell metacharacters can therefore cause commands to execute in the host shell before `tmux send-keys` runs. This violates the documented security boundary that commands are executed only inside the tmux session named `claw`. The command substitution also occurs after the dangerous-command filter has inspected the original text, so payloads can combine this injection flaw with filter evasion. ### Attack Path 1. An attacker or untrusted agent supplies a tool input such as: ```json { "command": "$(id > /tmp/claw-host-execution)" } ``` 2. `isDangerous` does not identify this payload as prohibited. 3. `sendCommand` places the input inside a double-quoted shell command. 4. The host shell evaluates `$(id > /tmp/claw-host-execution)` before invoking tmux. 5. The injected command runs directly under the account hosting the skill, outside the intended tmux execution context. 6. The attacker can replace `id` with other commands available to that account to access files, invoke local programs, or modify user-owned resources. ### Impact Assessment Successful exploitation provides arbitrary command execution with the operating-system privileges of the Node.js skill process. The attacker can read or modify files accessible to that account, access environment-dependent secrets, execute installed tools, and al ...[truncated 249 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Do not construct a shell command by interpolating user input. Invoke tmux directly with an argument array so that the command is passed as data rather than interpreted by a host shell: ```js const { execFileSync } = require("node:child_process"); function sendCommand(cmd) { execFileSync("tmux", ["send-keys", "-t", "claw", "--", cmd, "C-m"]); } ``` Apply the same no-shell argument-array pattern to all tmux operations: ```js execFileSync("tmux", ["has-session", "-t", "claw"], { stdio: "ignore" }); execFileSync("tmux", ["new-session", "-s", "claw", "-d"]); const buf = execFileSync( "tmux", ["capture-pane", "-t", "claw", "-p", "-S", "-200"] ); ``` Additional hardening should include: - Run the skill under a dedicated, minimally privileged operating-system account. - Use a controlled environment and restricted `PATH`. - Apply process sandboxing and filesystem restrictions where available. - Add regression tests containing `$()`, backticks, quotes, newlines, semicolons, and other shell metacharacters. - Avoid relying on character escaping as an alternative to eliminating shell interpretation. ]]>
