T09 · Insecure Skill Coding Practices
Error
- Location
- realtime-bot.js:43
- Finding
- Shell Command Injection Through Position Market Data<![CDATA[ ## Vulnerability Details **File Location**: `realtime-bot.js`, lines 43-50 and 116-164 **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```js function askGLM(prompt) { try { const result = execSync( `cd ${__dirname}/../zai && ./ask.sh "${prompt.replace(/"/g, '\\"')}" glm-4.7`, { encoding: 'utf8', timeout: 30000 } ); return result.trim(); } catch (err) { log(`GLM 호출 실패: ${err.message}`); return null; } } ``` The prompt passed to this function incorporates data read from `positions.json`: ```js const data = loadJSON(POSITIONS_FILE, { positions: [] }); const positions = data.positions || []; const openPositions = positions.filter(p => p.status === 'open'); for (const pos of openPositions) { const currentPrice = await getPrice(pos.market); const entryPrice = pos.entryPrice || pos.avgPrice; const pnlPercent = (currentPrice - entryPrice) / entryPrice; const prompt = `당신은 암호화폐 트레이딩 봇입니다. 포지션: ${pos.market} 진입가: ${entryPrice}원 현재가: ${currentPrice}원 손익: ${(pnlPercent * 100).toFixed(2)}% 목표: +5%, 손절: -5% 다음 중 하나만 답하세요: 1. HOLD - 유지 2. SELL_NOW - 즉시 매도 (목표/손절 전이라도) 3. ADJUST:새목표,새손절 - 목표/손절 조정 응답 형식: HOLD, SELL_NOW, 또는 ADJUST:5,-3`; const response = askGLM(prompt); ``` ### Technical Analysis `execSync()` receives a dynamically constructed command string and consequently invokes a shell. The code attempts to protect the prompt by escaping double quotes, but this does not make content safe inside a shell double-quoted argument. Shell constructs including command substitution remain active within double quotes. For example, `$(command)` and backtick substitutions can still be evaluated. Because `pos.market` is loaded from the locally writable `positions.json` file and interpolated into the prompt without strict validation, a malicious market value can introduce shell syntax. This is an injection flaw rather than merely malformed argument handling: attacker-controlled te ...[truncated 1597 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Eliminate shell command construction. Use `execFileSync()` or `spawnSync()` with an argument array: ```js const { execFileSync } = require('child_process'); function askGLM(prompt) { try { return execFileSync( path.join(__dirname, '..', 'zai', 'ask.sh'), [prompt, 'glm-4.7'], { encoding: 'utf8', timeout: 30000, shell: false } ).trim(); } catch (err) { log(`GLM invocation failed: ${err.message}`); return null; } } ``` 2. Avoid using `cd` in a command string. Set the child process `cwd` option instead. 3. Validate every market identifier before using it: ```js const MARKET_PATTERN = /^KRW-[A-Z0-9]+$/; if (!MARKET_PATTERN.test(pos.market)) { log('Rejected invalid market identifier'); continue; } ``` 4. Validate the complete structure and data types of `positions.json` against a schema. 5. Run the bot under a dedicated, minimally privileged operating-system account. 6. Restrict read and write permissions on `.env`, `positions.json`, and executable integration files. 7. Add regression tests using values containing `$()`, backticks, quotes, semicolons, newlines, and shell metacharacters to confirm that they are passed only as literal arguments. ]]>
