T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/hospital_search.sh:54
- Finding
- Arbitrary Python Code Execution Through the Hospital Name Argument<![CDATA[ ## Vulnerability Details **File Location**: `scripts/hospital_search.sh`, lines 31 and 54–55 **Vulnerability Type**: User-controlled input interpolated into dynamically generated Python source **Risk Level**: High ### Vulnerable Code ```bash while [[ $# -gt 0 ]]; do case "$1" in --sido) SIDO_CD="$2"; shift 2 ;; --sggu) SGGU_CD="$2"; shift 2 ;; --name) HOSP_NAME="$2"; shift 2 ;; --type) CL_CD="$2"; shift 2 ;; --dgsbjtCd) DGSBJ_CD="$2"; shift 2 ;; --page) PAGE_NO="$2"; shift 2 ;; --rows) NUM_OF_ROWS="$2"; shift 2 ;; *) shift ;; esac done ``` ```bash # yadmNm은 URL 인코딩 필요 if [ -n "$HOSP_NAME" ]; then ENCODED_NAME=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${HOSP_NAME}'))") PARAMS="${PARAMS}&yadmNm=${ENCODED_NAME}" fi ``` ### Technical Analysis The value of the `--name` command-line argument is assigned to `HOSP_NAME` and then inserted directly into source code supplied to `python3 -c`. Although the shell variable appears inside a single-quoted Python string, those single quotes do not protect the Python program from source-code injection. They are literal characters within the shell's outer double-quoted argument. A hospital name containing a single quote can terminate the Python string. Additional Python syntax can then be introduced before the attacker comments out or otherwise neutralizes the remaining generated source. This is a code-injection vulnerability rather than ordinary malformed-input handling. The injected content is parsed and executed by the Python interpreter with the same identity and environment as the Skill process. ### Attack Path 1. An attacker supplies or influences a hospital search name processed by the Skill. 2. The agent invokes `hospital_search.sh --name` with that attacker-controlled value. 3. The script assigns the value to `HOSP_NAME` without validation. 4. Lines 54–55 concatenate the value into the pro ...[truncated 1280 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Do not interpolate `HOSP_NAME` into Python source. Pass it as a separate argument: ```bash ENCODED_NAME=$( python3 -c \ 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1]))' \ "$HOSP_NAME" ) ``` A preferable design is to let `curl` perform query encoding and avoid manually constructing the query string: ```bash curl -sS --fail-with-body -G "$URL" \ --data-urlencode "serviceKey=$API_KEY" \ --data-urlencode "pageNo=$PAGE_NO" \ --data-urlencode "numOfRows=$NUM_OF_ROWS" \ --data-urlencode "_type=json" \ ${HOSP_NAME:+--data-urlencode "yadmNm=$HOSP_NAME"} ``` Additional hardening should include: 1. Validate that every option requiring a value actually has one before reading `$2`. 2. Reject unknown options rather than silently discarding them. 3. Apply reasonable length limits to hospital names. 4. Use `curl -sS --fail-with-body --max-time <seconds>` so HTTP and transport errors fail predictably. 5. Add regression tests containing quotes, backslashes, newlines, shell metacharacters, and non-ASCII names to confirm that all values remain data rather than executable code. ]]>
