T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/script.sh:310
- Finding
- Arbitrary Python Code Injection Through the Pair Filter Argument<![CDATA[ ## Vulnerability Details **File Location**: `scripts/script.sh`, lines 310–320 **Vulnerability Type**: User-controlled Python source-code injection **Risk Level**: High ### Vulnerable Code ```bash cmd_pairs() { local filter="${1:-USDT}" local res=$(_request "$BINANCE_API/exchangeInfo") [[ -z "$res" ]] && _error "Failed to fetch exchange info" echo "$res" | python3 -c " import json, sys d = json.load(sys.stdin) pairs = [s['symbol'] for s in d['symbols'] if s['status'] == 'TRADING' and '$filter' in s['symbol']] print(f'Found {len(pairs)} trading pairs for \"$filter\":') for i, p in enumerate(sorted(pairs), 1): print(f'{p:12s}', end='\n' if i % 5 == 0 else '') print() " } ``` ### Technical Analysis The `pairs` command accepts the `filter` argument from the command line and directly interpolates it into a Python program passed to `python3 -c`. The value is inserted into Python string literals in two locations without escaping or validation. Although the shell does not reevaluate shell metacharacters introduced through ordinary parameter expansion, Python subsequently parses the resulting text as source code. An attacker can supply a filter containing quotes, newlines, Python statements, and comment or multiline-string delimiters. This can terminate the intended string expression, add arbitrary Python statements, and neutralize the remaining generated source. The injected code runs under the same operating-system account, environment, working directory, and permissions as the Skill process. Successful exploitation requires the CoinGecko-style request made by this function to be replaced? No: this command uses Binance `exchangeInfo`; successful exploitation requires that request to return nonempty data so execution reaches `python3 -c`. ### Attack Path 1. An attacker causes the user or Agent to invoke the `pairs` command with an attacker-controlled filter argument. 2. `cmd_pairs` assigns the untrusted argument directly to `filte ...[truncated 1568 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Never interpolate untrusted data into source code supplied to `python3 -c`. Pass the filter as a positional argument or through a controlled environment variable and treat it exclusively as data. A safer implementation is: ```bash cmd_pairs() { local filter="${1:-USDT}" local res res=$(_request "$BINANCE_API/exchangeInfo") [[ -z "$res" ]] && _error "Failed to fetch exchange info" printf '%s' "$res" | python3 -c ' import json import sys filter_value = sys.argv[1] data = json.load(sys.stdin) pairs = [ item["symbol"] for item in data["symbols"] if item["status"] == "TRADING" and filter_value in item["symbol"] ] print(f"Found {len(pairs)} trading pairs for {filter_value!r}:") for index, pair in enumerate(sorted(pairs), 1): print(f"{pair:12s}", end="\n" if index % 5 == 0 else "") print() ' "$filter" } ``` Additional hardening measures should include: 1. Validate the filter against the intended symbol syntax, such as `^[A-Za-z0-9]{1,20}$`, if arbitrary text is unnecessary. 2. Use positional arguments, standard input, JSON, or environment variables whenever shell code invokes another language interpreter. 3. Search for and prohibit direct user-input interpolation into `python3 -c`, `sh -c`, `eval`, template-generated code, and similar execution contexts. 4. Add regression tests using quotes, newlines, semicolons, comment markers, and multiline-string delimiters to verify that inputs are treated only as data. 5. Run the Skill with least privilege and avoid exposing unrelated secrets through its environment. ]]>
