T09 · Insecure Skill Coding Practices
- Location
scripts/script.sh:20- Finding
Arbitrary Command Execution Through Bash Arithmetic Injection
- Content
View full analysis
/dev/null || echo "$((ml / 15))") echo "⚖️ For $cups cups ($ml ml): Use ${grams}g of coffee (1:15 ratio)." } ``` ### Technical Analysis The `ratio` command accepts its first positional argument as the untrusted `cups` value. This value is used in Bash arithmetic expansion at line 22 without first verifying that it contains only a valid, bounded decimal integer: ```bash local ml=$((cups * 240)) ``` Bash recursively evaluates variable values used in arithmetic contexts as arithmetic expressions. Crafted arithmetic expressions can contain array references whose subscripts undergo shell expansion, including command substitution. Consequently, an argument that appears where a number is expected can cause an embedded command to execute. The later fallback arithmetic expansion on line 23 also operates on a derived arithmetic value, but execution can already occur when `ml` is assigned. The presence or absence of `bc` therefore does not prevent exploitation. ### Attack Path 1. An attacker causes the Skill or a user to invoke the script's `ratio` command with an attacker-controlled argument. 2. The argument is stored unchanged in the local `cups` variable. 3. Line 22 evaluates `cups` as Bash arithmetic syntax. 4. A crafted array-subscript expression triggers command substitution during arithmetic evaluation. 5. The embedded command executes with the operating-system identity and environment of the process running the Skill. For example, an invocation shaped as follows can create a file as proof of command execution: ```bash ./scripts/script.sh ratio 'x[$(touch /tmp/coffee-audit-poc)0]' ``` The attack does not require `bc`, network access, or an ...[truncated 785 chars]- Remediation
View remediation
&2 return 1 fi if (( ${#cups} > 4 )) || (( 10#$cups > 1000 )); then printf '%s\n' "Error: cups must not exceed 1000." >&2 return 1 fi local ml=$((10#$cups * 240)) local grams if command -v bc >/dev/null 2>&1; then grams=$(printf 'scale=1; %d / 15\n' "$ml" | bc) else grams=$((ml / 15)) fi printf 'For %d cups (%d ml): Use %sg of coffee (1:15 ratio).\n' \ "$cups" "$ml" "$grams" } ``` Additional hardening measures: 1. Apply strict allowlist validation before every use of user input in an arithmetic expression. 2. Impose practical upper bounds to prevent integer overflow and resource misuse. 3. Use `10#` only after validation to force decimal interpretation and avoid leading-zero octal behavior. 4. Prefer `printf` over interpolated `echo` for predictable output handling. 5. Add regression tests using malformed inputs, arithmetic expressions, array-syntax payloads, command substitutions, negative values, leading zeros, and excessively large integers. 6. Consider enabling `set -euo pipefail` after ensuring expected command failures are handled explicitly. ]]>
