T09 · Insecure Skill Coding Practices
- Location
scripts/eliza-adapter.mjs:27- Finding
OS Command Injection Through Agent-Controlled Token Input
- Content
View full analysis
Vulnerability Details
File Location:
scripts/eliza-adapter.mjs, lines 27–33
Vulnerability Type: OS command injection
Risk Level: HighVulnerable Code
js const { execSync } = await import('child_process'); const skillDir = new URL('.', import.meta.url).pathname; const result = execSync( `node ${skillDir}scripts/buzz-scan.mjs --token "${message.content.text}" --json`, { encoding: 'utf-8', timeout: 30000 } ); return { text: result };Technical Analysis
The
BUZZ_TOKEN_INTELLIGENCEaction interpolatesmessage.content.textdirectly into a command string passed tochild_process.execSync().execSync()executes the string through a system shell. Wrapping attacker-controlled input in double quotes does not make it safe: shell command substitutions such as$(command)and backtick substitutions remain active inside double-quoted strings. Shell metacharacters that interact with the surrounding command may likewise become exploitable depending on the supplied text and shell.Because action messages may originate from untrusted or indirectly attacker-controlled agent input, the token parameter crosses a trust boundary before reaching a command shell. There is no address-format validation, length restriction, escaping, or argument separation.
The computed
skillDiris also inserted without shell-safe argument separation. In addition, becauseimport.meta.urlalready points to a file underscripts/, appendingscripts/buzz-scan.mjsappears likely to produce a duplicatedscripts/scripts/path. That path issue may impair functionality but does not mitigate the injection because shell substitutions occur while the shell evaluates the command.Attack Path
- An attacker provides token-analysis text containing a shell substitution, such as a token-like value with
$(attacker_command). - The agent passes that text to
BUZZ_TOKEN_INTELLIGENCE.handlerasmessage.content.text. - The handler embeds the text in the co ...[truncated 1254 chars]
- An attacker provides token-analysis text containing a shell substitution, such as a token-like value with
- Remediation
View remediation
Remediation Suggestions
Eliminate shell interpretation by using
execFileSync()orspawnSync()with an argument array:js const { execFileSync } = await import('child_process'); const scriptPath = new URL('./buzz-scan.mjs', import.meta.url); const token = message.content.text; const result = execFileSync( process.execPath, [scriptPath.pathname, '--token', token, '--json'], { encoding: 'utf-8', timeout: 30000, shell: false } ); return { text: result };Apply defense in depth:
- Validate that the input is a string and impose a strict length limit.
- Require a supported chain to be selected explicitly.
- Validate the token against the selected chain's address syntax:
- Ethereum and BSC: a properly formed hexadecimal contract address.
- Solana: a valid base58 public key of the expected decoded length.
- Reject token names in this execution path unless they are resolved through a non-shell API flow.
- Avoid manual shell escaping as the primary defense; pass every argument separately.
- Run the adapter with a restricted operating-system account, minimal filesystem access, a constrained environment, and limited outbound network permissions.
- Add regression tests containing command-substitution and shell-metacharacter payloads to confirm that they are treated only as literal arguments.
- Correct the scanner path by resolving
./buzz-scan.mjsrelative toimport.meta.url, rather than appending anotherscripts/directory.
