T09 · Insecure Skill Coding Practices
- Location
scripts/sync-abi.sh:119- Finding
Python Code Injection Through Unsafely Interpolated Artifact Paths
- Content
View full analysis
/dev/null; then python3 -c "import json,sys; d=json.load(open('$artifact_path')); print(json.dumps(d['abi'], indent=2))" return elif command -v python &>/dev/null; then python -c "import json,sys; d=json.load(open('$artifact_path')); print(json.dumps(d['abi'], indent=2))" return fi fi ``` ### Technical Analysis The script embeds `artifact_path` directly into Python source passed to the `-c` option. Shell quoting protects the value from direct shell expansion, but it does not protect the generated Python program. A pathname containing a single quote can terminate the argument passed to `open(...)` and append attacker-controlled Python statements. The vulnerable branch is used when `jq` is unavailable and either `python3` or `python` is installed. Artifact paths are derived from the configurable `ABI_SOURCE`, contract names, and files found beneath the artifact directory. Consequently, an attacker who can control the artifact tree or relevant synchronization configuration may be able to introduce a specially crafted pathname. ### Attack Path 1. An attacker gains control over the artifact tree, `ABI_SOURCE`, or contract synchronization input. 2. The attacker creates an artifact whose pathname contains characters that terminate the Python string and inject additional Python statements. 3. The artifact contains the text `"abi"` so that the Python fallback is selected. 4. The victim runs `sync-abi.sh` on a system where `jq` is unavailable but Python is installed. 5. The script incorporates the malicious pathname into the program supplied to `python -c`. 6. The injected Python statements execute with the permissions of the developer or CI runner. ### Impact Assessment Successful exploit ...[truncated 544 chars]- Remediation
View remediation
/dev/null; then python3 -c ' import json import sys with open(sys.argv[1], encoding="utf-8") as stream: data = json.load(stream) print(json.dumps(data["abi"], indent=2)) ' "$artifact_path" return elif command -v python &>/dev/null; then python -c ' import json import sys with open(sys.argv[1], encoding="utf-8") as stream: data = json.load(stream) print(json.dumps(data["abi"], indent=2)) ' "$artifact_path" return fi ``` Additional hardening should include: - Validate that the resolved artifact remains inside the canonical `ABI_SOURCE` directory. - Reject symbolic links that resolve outside the expected artifact tree if untrusted artifacts are supported. - Validate that parsed JSON is either an ABI array or an object containing an ABI array. - Add regression tests using filenames containing quotes, whitespace, newlines, and shell/Python metacharacters. ]]>
