T09 · Insecure Skill Coding Practices
Error
- Location
- add_task.sh:22
- Finding
- Arbitrary Python Code Execution Through Unsafely Interpolated Task Input## Vulnerability Details **File Location**: `add_task.sh`, lines 22–33 **Vulnerability Type**: Python code injection through an unquoted heredoc **Risk Level**: High ```bash # JSON 기록 python3 - <<PY >> "$LOG" import json event = { "id": "$ID", "ts": "$TS", "title": "$TITLE", "owner": "$OWNER" if "$OWNER" else None, "due": "$DUE" if "$DUE" else None, "status": "draft" if (not "$OWNER" or not "$DUE") else "open", "raw": "$TEXT" } print(json.dumps(event, ensure_ascii=False)) PY ``` ### Technical Analysis The script places task attributes derived from user-controlled Telegram input directly into Python source code. The heredoc delimiter is unquoted, so Bash expands variables such as `TITLE`, `OWNER`, `DUE`, and `TEXT` before passing the resulting program to Python. These values are inserted between Python quotation marks without escaping them as Python string literals. An attacker can consequently terminate or modify the intended string expression and introduce an arbitrary Python expression. For example, input shaped like the following can cause a command to execute while Python evaluates the generated dictionary: ```text "+str(__import__('os').system('id'))+" ``` The resulting `title` expression would contain executable Python rather than an inert string: ```python "title": ""+str(__import__('os').system('id'))+"", ``` JSON serialization occurs only after the dynamically constructed Python source has already been parsed and executed. Therefore, the use of `json.dumps` does not mitigate this vulnerability. ### Attack Path 1. An attacker sends a crafted `/todo` message containing Python syntax in the task text. 2. The skill passes the full text as an argument to `add_task.sh`, as directed by `SKILL.md`. 3. The script derives `TITLE`, `OWNER`, `DUE`, and `TEXT` from that attacker-controlled argument. 4. Bash substitutes these values into the unquoted Python heredoc. 5. The substituted content changes the syntax or semantics of the ...[truncated 1102 chars]
- Remediation
- ## Remediation Suggestions Do not construct executable source code by interpolating user-controlled data. 1. Pass values to a fixed Python program through positional arguments or environment variables: ```bash python3 - "$ID" "$TS" "$TITLE" "$OWNER" "$DUE" "$TEXT" <<'PY' >> "$LOG" import json import sys task_id, timestamp, title, owner, due, raw = sys.argv[1:] event = { "id": task_id, "ts": timestamp, "title": title, "owner": owner or None, "due": due or None, "status": "draft" if not owner or not due else "open", "raw": raw, } print(json.dumps(event, ensure_ascii=False)) PY ``` 2. Quote the heredoc delimiter (`<<'PY'`) so that Bash does not perform parameter, command, or arithmetic expansion within the Python program. 3. Keep all user data outside the Python source and allow `json.dumps` to serialize it as data. 4. Add regression tests covering quotation marks, backslashes, newlines, Unicode, shell metacharacters, and Python expressions. 5. Run the skill with least privilege and restrict its filesystem, credential, and network access to reduce the impact of any future injection flaw.
