T09 · Insecure Skill Coding Practices
- Location
scripts/cred_helper.sh:40- Finding
Python Code Injection Through Shell Helper Arguments
- Content
View full analysis
Vulnerability Details
File Location:
scripts/cred_helper.sh, lines 40–41
Vulnerability Type: Python code injection caused by unsafe interpolation into apython3 -cprogram
Risk Level: HighVulnerable Code
bash echo "$CRED_MASTER_PASS" | gpg --batch --yes --passphrase-fd 0 --decrypt "$CRED_FILE" 2>/dev/null | \ python3 -c "import sys,json; d=json.load(sys.stdin); print(d['$service']['$key'])" 2>/dev/nullTechnical Analysis
The
cred_getfunction obtainsserviceandkeyfrom its first and second arguments and interpolates them directly into Python source code passed topython3 -c.Shell quoting does not make these values safe within the generated Python program. An argument containing Python quote delimiters and additional expressions can terminate the intended dictionary index and alter the program evaluated by Python. Consequently, an attacker who controls either argument can execute arbitrary Python code instead of merely selecting a credential field.
Although the shell invokes Python rather than evaluating the arguments as shell syntax directly, arbitrary Python execution can invoke operating-system commands through modules such as
osorsubprocess. The vulnerability therefore results in arbitrary code execution with the privileges of the user who sourced and called the helper.Attack Path
- A user sources
scripts/cred_helper.sh, makingcred_getavailable in the current shell. - The user or another script supplies an attacker-controlled service name or field name to
cred_get. - The function decrypts
credentials.json.gpgand pipes the complete plaintext credential document to Python through standard input. - The attacker-controlled value is inserted into the source string supplied to
python3 -c. - A crafted value breaks out of the intended dictionary lookup and introduces additional Python expressions or statements.
- Python evaluates the injected code with the invoking user's permiss ...[truncated 991 chars]
- A user sources
- Remediation
View remediation
Remediation Suggestions
Do not construct Python source code using caller-controlled values. Pass the service and key as separate positional arguments and retrieve them through
sys.argv:bash echo "$CRED_MASTER_PASS" | gpg --batch --yes --passphrase-fd 0 --decrypt "$CRED_FILE" 2>/dev/null | python3 -c ' import json import sys data = json.load(sys.stdin) print(data[sys.argv[1]][sys.argv[2]]) ' "$service" "$key"Additional hardening measures should include:
- Validate that exactly two non-empty arguments are supplied.
- Preserve strict quoting around
"$service"and"$key". - Add controlled handling for missing services, missing fields, malformed JSON, and GPG failures without revealing unrelated secrets.
- Enable pipeline failure propagation, preferably in a dedicated executable script using
set -o pipefail, so a decryption failure cannot be mistaken for a successful lookup. - Add regression tests using arguments containing single quotes, double quotes, brackets, semicolons, newlines, and Python expressions to confirm they are treated exclusively as data.
- Consider replacing the inline Python implementation with a call to
cred_manager.pyso lookup and error-handling logic are maintained in one implementation.
