T09 · Insecure Skill Coding Practices
- Location
extract.js:36- Finding
Arbitrary Command Execution Through URL Shell Injection
- Content
View full analysis
Vulnerability Details
File Location:
extract.js, lines 36-54
Vulnerability Type: OS command injection
Risk Level: HighVulnerable Code
js async function main() { const url = process.argv[2]; const outputName = process.argv[3] || 'output'; if (!url) { console.log('用法: node extract.js <URL> [输出文件名]'); process.exit(1); } console.log(`正在提取: ${url}`); // 创建临时目录 if (!fs.existsSync(TEMP_DIR)) { fs.mkdirSync(TEMP_DIR, { recursive: true }); } // 获取网页内容 console.log('正在获取网页内容...'); const html = execSync(`curl -sL -A "Mozilla/5.0" "${url}"`, { encoding: 'utf8' });Technical Analysis
The URL is read directly from the command-line arguments and interpolated into a command string passed to
execSync. By default,execSyncexecutes the string through a system shell.Wrapping the URL in double quotes does not make the command safe. POSIX-compatible shells still process command substitutions such as
$(command)and backtick expressions inside double-quoted strings. Consequently, an attacker-controlled URL can cause arbitrary local commands to execute.No URL validation, shell metacharacter rejection, or argument-safe process invocation separates the untrusted input from the shell command.
Attack Path
- An attacker supplies or persuades an operator or Agent to process a crafted URL containing shell command substitution.
- For example, the script could be invoked with a URL argument containing:
text https://example.com/$(touch /tmp/skill-command-executed) - The value is inserted into the
curlcommand string. execSynclaunches a shell to interpret that string.- The shell evaluates
$(touch /tmp/skill-command-executed)before invokingcurl. - The injected command executes with the privileges and environment of the user running the Skill. ...[truncated 744 chars]
- Remediation
View remediation
Remediation Suggestions
Eliminate shell interpretation entirely. Prefer Node.js HTTP APIs already imported by the script, or invoke
curlwith an argument array throughexecFileSyncorspawn.js const { execFileSync } = require('child_process'); let parsedUrl; try { parsedUrl = new URL(url); } catch { throw new Error('Invalid URL'); } if (!['http:', 'https:'].includes(parsedUrl.protocol)) { throw new Error('Only HTTP and HTTPS URLs are allowed'); } const html = execFileSync( 'curl', ['-sL', '-A', 'Mozilla/5.0', parsedUrl.href], { encoding: 'utf8', timeout: 30000, maxBuffer: 10 * 1024 * 1024 } );Using an argument-array API ensures the URL is passed as one literal process argument rather than interpreted as shell syntax. Do not attempt to solve the issue solely by adding quotation marks or manually escaping a small set of metacharacters.
Additional hardening should include:
- Restricting destination hosts if this Skill is intended only for specific image providers.
- Permitting only
http:andhttps:schemes. - Applying request timeouts and response-size limits.
- Rejecting embedded credentials and malformed URLs.
- Running the Skill as an unprivileged account with access only to necessary files.
- Adding regression tests using URLs containing
$(), backticks, semicolons, quotes, and newline characters.
