Back to skill

Security audit

건강보험심사평가원 병원 검색

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real Korean hospital lookup skill, but crafted hospital search inputs can run local Python code on the user’s machine.

Review before installing. Use only if you trust the publisher and can patch the unsafe python3 -c argument handling first; otherwise a maliciously crafted hospital name or type could execute code with your user permissions. If used, store the API key with restrictive permissions and expect queries plus the service key to be sent to data.go.kr.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

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. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/hospital.sh:64
Finding
Arbitrary Python Code Execution Through the Hospital Type Argument<![CDATA[ ## Vulnerability Details **File Location**: `scripts/hospital.sh`, lines 14 and 64 **Vulnerability Type**: User-controlled input interpolated into dynamically generated Python source **Risk Level**: High ### Vulnerable Code ```bash # 파라미터 SIDO_CD="${1:-110000}" # 시도코드 (기본: 서울) HOSP_TYPE="${2:-병원}" # 병원 종류 PAGE_NO="${3:-1}" NUM_OF_ROWS="${4:-100}" ``` The value is later embedded directly into the Python program: ```python # 종별 필터링 if '$HOSP_TYPE' == '병원' or item_dict.get('clCdNm', '').find('$HOSP_TYPE') >= 0: items.append(item_dict) ``` This Python code is part of a shell double-quoted argument passed to `python3 -c`, so `$HOSP_TYPE` is expanded by the shell before Python parses the program. ### Technical Analysis The second positional argument is stored in `HOSP_TYPE` and interpolated twice into single-quoted Python string literals. The single quotes only delimit strings in the generated Python program; they do not safely encode the argument. An attacker can include a single quote and subsequent Python syntax in the hospital-type value. When shell expansion constructs the `python3 -c` argument, the attacker-controlled syntax becomes part of the Python source and is executed by the interpreter. The use of `set -euo pipefail` does not prevent this issue. It controls shell error handling but does not provide safe encoding for values embedded in another programming language. ### Attack Path 1. An attacker supplies or influences the hospital-type criterion. 2. The Skill invokes `hospital.sh`, placing the crafted criterion in the second positional argument. 3. Line 14 stores the input in `HOSP_TYPE` without constraining it to an allowlist. 4. Shell expansion inserts the value into both Python expressions on line 64. 5. Crafted input closes the expected Python string and introduces executable Python syntax. 6. The Python interpreter runs the injected code with the privileges and environment of the Skill pro ...[truncated 931 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Pass the hospital type as data rather than interpolating it into Python source. For example, supply it as a Python argument while retaining the XML response on standard input: ```bash printf '%s' "$RESPONSE" | python3 -c ' import sys import json import xml.etree.ElementTree as ET hospital_type = sys.argv[1] xml_str = sys.stdin.read() root = ET.fromstring(xml_str) # Parsing omitted for brevity. # Use hospital_type only as a normal runtime value: # if hospital_type == "병원" or hospital_type in item_dict.get("clCdNm", ""): # items.append(item_dict) ' "$HOSP_TYPE" ``` The final implementation should preserve the existing parsing logic but must never place `HOSP_TYPE` inside generated Python source. Further hardening should include: 1. Prefer an allowlist of supported hospital-type values or official type codes. 2. Enforce length and character constraints appropriate to the API field. 3. Validate `SIDO_CD`, `PAGE_NO`, and `NUM_OF_ROWS` as numeric values with reasonable ranges. 4. Use `curl -sS --fail-with-body --max-time <seconds>` for bounded and observable network failures. 5. Add tests using quotes, newlines, backslashes, Python operators, and non-ASCII values. 6. Keep the API-key file permission-restricted, for example with mode `0600`, to reduce impact if another local account is compromised. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (14)

External Script Fetching

High
Category
Supply Chain
Content
echo "시도: $SIDO_CD | 종별: $HOSP_TYPE" >&2

# 요청 실행
RESPONSE=$(curl -s -G "$URL" \
    --data-urlencode "serviceKey=$API_KEY" \
    --data "sidoCd=$SIDO_CD" \
    --data "pageNo=$PAGE_NO" \
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
PARAMS="${PARAMS}&yadmNm=${ENCODED_NAME}"
fi

RESPONSE=$(curl -s "${URL}?${PARAMS}")

echo "$RESPONSE" | python3 -c "
import sys, json
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill declares connectors, shell scripts, and external API usage but does not define an explicit tool/permission boundary such as allowed tools or scoped permissions. In an agent environment, this can let the runtime invoke broader shell or network capabilities than users or reviewers expect, increasing the chance of unintended external calls or command execution.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The detail-lookup examples are underspecified because phrases like 'tell me the hours' or 'is parking available' are common follow-up questions that may refer to many contexts, not necessarily a hospital already identified by this skill. If routed without verifying the target institution, the agent may perform unintended lookups, mis-associate a hospital, or leak user context to external services.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The routing rule maps very broad Korean keywords like search/find/recommend to this skill, which can overlap with ordinary health-related conversation rather than a clear hospital lookup request. Over-broad triggering can cause the agent to invoke networked scripts unexpectedly, disclose user queries to external services, or return medical-provider suggestions when the user did not explicitly ask for that action.

Session Persistence

Medium
Category
Rogue Agent
Content
2. 로그인 → 마이페이지 → **일반 인증키(Decoding)** 복사
3. API 키 저장:
   ```bash
   mkdir -p ~/.config/data-go-kr
   echo "YOUR_API_KEY" > ~/.config/data-go-kr/api_key
   ```
4. 아래 서비스 **활용신청** 후 사용 (자동승인)
Confidence
88% confidence
Finding
The setup instructions direct users to persist a long-lived API key in a predictable plaintext path under the home directory. Secrets stored this way are vulnerable to accidental disclosure through permissive file permissions, backups, logs, shell history, or compromise by other tools running with user access.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script accesses an API key from ~/.config/data-go-kr/api_key and exits with an error if it is missing, but there is no comment or user-facing warning explaining that the skill reads a locally stored credential. For code files, access to sensitive credentials should have some form of disclosure unless clearly communicated elsewhere in the skill description, which is not available here.

External Transmission

Medium
Category
Data Exfiltration
Content
echo "시도: $SIDO_CD | 종별: $HOSP_TYPE" >&2

# 요청 실행
RESPONSE=$(curl -s -G "$URL" \
    --data-urlencode "serviceKey=$API_KEY" \
    --data "sidoCd=$SIDO_CD" \
    --data "pageNo=$PAGE_NO" \
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The curl invocation sends the serviceKey API credential to an external HTTPS endpoint, but the script does not provide a warning or disclosure that authentication data is being transmitted over the network. Although the script's purpose is to query a public API, the current file lacks any explicit notice about this credential-bearing outbound request.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The stated purpose is hospital information lookup, and network access to the public API is expected. However, directly reading a credential from ~/.config/data-go-kr/api_key introduces local secret access that is not disclosed by the manifest description and is a distinct capability beyond straightforward search behavior.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script loads an API key from ~/.config/data-go-kr/api_key and uses it in outbound curl requests to an external API. While this is part of the script's functionality, the file provides no explicit warning that local credentials will be accessed and sent over the network.

External Transmission

Medium
Category
Data Exfiltration
Content
SELECTED=$(echo "$ITEMS" | tr ',' ' ')
fi

# 병렬로 curl 호출
for key in $SELECTED; do
    ep="${EP_MAP[$key]:-}"
    [ -z "$ep" ] && continue
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The script reads an API key from a local file in the user's home directory, which is a privileged local-data access capability beyond pure argument-only hospital lookup logic. Even if needed for API authentication, this creates secret-handling risk because any invocation of the skill implicitly accesses local sensitive material, and the manifest/behavior should explicitly justify and constrain that access.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The trigger examples, routing logic, setup notes, and user-facing phrasing are presented only in Korean, which effectively forces a specific language experience. The file does not mention any user language choice, alternative locale, or explicit justification for restricting interaction language.

Static analysis

No suspicious patterns detected.