T09 · Insecure Skill Coding Practices
Error
- Location
- server-gemini.js:101
- Finding
- Unauthenticated Remote Shell Command Injection Through Chat Messages<![CDATA[ ## Vulnerability Details **File Location**: `server-gemini.js:101-117`; equivalent vulnerable implementation in `server.js:151-201` **Vulnerability Type**: OS command injection **Risk Level**: Critical ### Vulnerable Code ```javascript app.post('/api/agents/:id/chat', async (req, res) => { try { const { id } = req.params; const { message, imageData } = req.body; if (!message && !imageData) { return res.status(400).json({ success: false, error: 'Message or image is required' }); } const agentId = id.replace('agent-', ''); const workspace = `workspace-${agentId}`; const workspacePath = path.join(CONFIG.workspaceDir, workspace); let taskMessage = message || 'Analyze this image'; if (imageData) { taskMessage = `${message || ''} [image uploaded]`.trim(); } const safeMessage = taskMessage.replace(/"/g, '\\"').replace(/\n/g, ' '); const { stdout } = await execCmd( `cd "${workspacePath}" && openclaw agent --agent ${agentId} --message "${safeMessage}" --json 2>&1` ); ``` The command is executed through a shell: ```javascript async function execCmd(cmd) { return new Promise((resolve, reject) => { exec(cmd, { shell: 'zsh' }, (error, stdout, stderr) => { if (error) reject(error); else resolve({ stdout, stderr }); }); }); } ``` ### Technical Analysis The request-controlled `message` is embedded directly into a command string passed to `child_process.exec()` with Z shell enabled. The attempted sanitization only escapes double quotation marks and replaces newline characters. It does not prevent command substitution or other shell interpretation. For example, command substitutions using `$(...)` or backticks remain active inside double-quoted strings. The request-controlled agent identifier is also interpolated into the command without being passed as a separately quoted process argument. The endpoint has no authentication or authorization. Consequently, this is ...[truncated 1122 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Remove `exec()` and all string-based shell command construction. - Invoke OpenClaw with `execFile()` or `spawn()` and a fixed argument array: ```javascript const { spawn } = require('child_process'); const child = spawn( 'openclaw', ['agent', '--agent', agentId, '--message', taskMessage, '--json'], { cwd: workspacePath, shell: false, stdio: ['ignore', 'pipe', 'pipe'] } ); ``` - Validate agent IDs with a strict allowlist such as `^[a-z0-9][a-z0-9_-]{0,63}$`. - Resolve the agent from a trusted server-side registry rather than deriving command arguments directly from URL parameters. - Add authentication and per-operation authorization to every API endpoint. - Bind the service explicitly to `127.0.0.1` unless remote access is intentionally secured. - Add request rate limits, process timeouts, and output-size limits. - Add regression tests containing quotes, backticks, dollar signs, command substitutions, and shell metacharacters. ]]>
