T09 · Insecure Skill Coding Practices
- Location
scripts/discord-roster.sh:53- Finding
Arbitrary Python Code Execution Through the Members Filter Argument
- Content
View full analysis
Vulnerability Details
File Location:
scripts/discord-roster.sh, lines 53–66
Vulnerability Type: Unsanitized argument interpolation into dynamically constructed Python source
Risk Level: HighVulnerable Code:
bash cmd_members() { local guild_id="${1:?Usage: members <guild_id> [--bots|--humans]}" local filter="${2:-all}" local raw raw=$(api "/guilds/${guild_id}/members?limit=1000") python3 -c " import json, sys data = json.loads('''$( echo "$raw" | python3 -c "import sys; print(sys.stdin.read().replace(\"'\",\"\\\\'\"))" )''') if isinstance(data, dict) and 'message' in data: print(f\"API Error: {data['message']}\", file=sys.stderr) sys.exit(1) filt = '$filter'Technical Analysis
The second positional argument to the
memberscommand is stored infilterand inserted directly into the source string supplied topython3 -c. It is not validated or passed as inert data throughsys.argv, standard input, or an environment variable.An attacker who can control this argument can include a single quote to terminate the Python string assigned to
filt, append Python statements, and comment out the remaining source on that line. The injected Python can invoke operating-system commands through modules such asosorsubprocess.Shell metacharacters introduced through ordinary parameter expansion are not re-evaluated as shell syntax, but this does not prevent exploitation: the resulting text is subsequently parsed as Python source by
python3 -c.Exploitation requires the preceding Discord API request to complete successfully because the vulnerable Python invocation occurs after
raw=$(api ...).Attack Path
- The attacker gains control over, or influences, the arguments used to invoke the Skill.
- The attacker invokes
memberswith a valid or accessible guild ID so the Discord API request succeeds. - The second argument is crafted to cl ...[truncated 1212 chars]
- Remediation
View remediation
Remediation Suggestions
Do not embed command-line arguments in Python source. Pass the filter as a positional argument and validate it against an explicit allowlist before use.
A hardened approach is:
bash local filter="${2:-all}" case "$filter" in all|--bots|--humans) ;; *) die "Invalid member filter: $filter" ;; esac printf '%s' "$raw" | python3 -c ' import json import sys filt = sys.argv[1] data = json.load(sys.stdin) # Process data without constructing source from input. ' "$filter"Additionally:
- Pass API responses through standard input rather than embedding them into Python source.
- Reject unexpected extra arguments.
- Add regression tests containing quotes, newlines, backslashes, semicolons, and Python syntax.
- Treat every command-line argument as untrusted, even when expected to come from an agent-generated command.
