T09 · Insecure Skill Coding Practices
- Location
SKILL.md:26- Finding
Shell Command Injection Through Image-Generation Prompt Interpolation
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 26-46
Vulnerability Type: Shell command injection through unsafe interpolation
Risk Level: HighVulnerable Code
bash curl -s https://api.x.ai/v1/images/generations \ -H "Authorization: Bearer $XAI_API_KEY" \ -H "Content-Type: application/json" \ --data '{ "model": "grok-imagine-image-pro", "prompt": "<PROMPT>", "n": 1, "response_format": "b64_json" }' | python3 -c " import json, sys, base64, os, time os.makedirs(os.path.expanduser('~/.openclaw/media'), exist_ok=True) r = json.load(sys.stdin) ts = int(time.time()) for i, img in enumerate(r['data']): img_data = base64.b64decode(img['b64_json']) fpath = os.path.expanduser(f'~/.openclaw/media/generated_{ts}_{i}.png') with open(fpath, 'wb') as f: f.write(img_data) print(fpath) "Technical Analysis
The documentation instructs the Agent to substitute a user-supplied image prompt directly into a single-quoted shell argument. No JSON serialization or shell-safe argument handling is applied.
If the prompt contains a single quote followed by shell syntax, it can terminate the
--dataargument and introduce additional shell commands. JSON double quotes do not protect the value from shell parsing because the entire JSON document is enclosed in shell single quotes.This is especially dangerous in an Agent Skill because image prompts are expected to originate from users and may therefore be attacker-controlled.
Attack Path
- An attacker submits an image-generation request containing a prompt with a single quote, shell separators, and an injected command.
- The Agent replaces
<PROMPT>with that content as instructed by the Skill. - The injected quote terminates the shell's single-quoted JSON argument.
- The shell interprets the remaining attacker-controlled content as commands.
- Those commands ...[truncated 592 chars]
- Remediation
View remediation
Remediation Suggestions
- Do not construct executable shell text by substituting the prompt into a quoted command.
- Build the complete request body using a Python script and
json.dumporjson.dumps. - Supply the prompt through a non-executable channel, such as a Python argument, standard input, or a narrowly scoped environment variable.
- Invoke the HTTP client through
subprocess.runwith an argument array andshell=False, or perform the request directly with a vetted HTTP library. - Validate maximum prompt length and reject unexpected control characters.
- Run the image-generation process in a restricted environment with minimal filesystem and network access.
