T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/remind.js:66
- Finding
- Shell Command Injection Through Reminder Arguments and Calendar Event Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/remind.js:66-71` **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```js // Discord 전송 if (argv.channel) { try { execSync(`openclaw message send --target "${argv.channel}" --message "${message.replace(/"/g, '\\"')}"`, { stdio: 'inherit', }); console.log('\n✅ Discord 전송 완료'); ``` ### Technical Analysis The Skill constructs a shell command by interpolating both `argv.channel` and `message` into a string passed to `execSync()`. Because `execSync()` executes string commands through a shell, shell metacharacters in either value may be interpreted as commands. Escaping only double quotation marks is insufficient. Shell substitutions such as `$(command)` and backtick substitutions remain active inside double-quoted shell arguments. The generated `message` includes Google Calendar event summaries and locations, which are externally sourced and may be controlled through shared calendars, invitations, or compromised calendar accounts. Consequently, calendar data is incorrectly treated as trusted shell input. The documented cron execution makes this especially dangerous because a malicious event could trigger command execution automatically at the scheduled reminder time. ### Attack Path 1. The attacker obtains the ability to create or modify an event visible on the calendar processed by the Skill, such as through a shared calendar or calendar invitation. 2. The attacker places shell substitution syntax in the event summary or location. 3. The scheduled cron job invokes `scripts/remind.js` with a Discord channel. 4. The Skill retrieves the malicious event and appends its summary or location to `message`. 5. The interpolated message is passed to `execSync()` as part of a shell command. 6. The local shell evaluates the injected substitution and executes the attacker-selected command. A local caller can also exploit the same flaw by supplying shell ...[truncated 717 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Avoid invoking a shell. Replace `execSync()` with `execFileSync()` or `spawnSync()` and pass every argument separately: ```js const { execFileSync } = require('child_process'); execFileSync('openclaw', [ 'message', 'send', '--target', argv.channel, '--message', message, ], { stdio: 'inherit', }); ``` Additional hardening should include: 1. Validate Discord channel identifiers against the exact expected format, such as `/^\d+$/`. 2. Treat all calendar fields as untrusted input. 3. Do not attempt to solve shell injection through manual escaping; eliminate shell interpretation instead. 4. Run scheduled reminders under a dedicated, minimally privileged account where practical. 5. Add regression tests using event titles and locations containing quotes, semicolons, backticks, dollar signs, newlines, and command substitutions. ]]>
