T01 · Skill Instruction Hijacking
Error
- Location
- scripts/cron_builder.py:69
- Finding
- Persistent Agent Prompt Injection Through Unvalidated Route Values<![CDATA[ ## Vulnerability Details **File Location**: `scripts/cron_builder.py:69-86`; data reaches persistent cron registration through `scripts/rule_wizard.py:151-225` **Vulnerability Type**: Persistent prompt injection and potential command injection **Risk Level**: High ### Vulnerable Code ```python def parse_routes(s: str) -> List[str]: parts = [p.strip() for p in (s or "").split(",") if p.strip()] if not parts: raise ValueError("routes required") return parts def build_prompt(city: str, node: str, routes: List[str]) -> str: routes_csv = ",".join(routes) # The agent will run the deterministic script and then output a clean summary. return ( "You are running a scheduled TAGO bus arrival alert.\n" "- Do not reveal any secrets.\n" "- Query TAGO arrivals using the helper script and format a short message.\n\n" "Steps:\n" f"1) Run: python3 korea-metropolitan-bus-alerts/scripts/tago_bus_alert.py arrivals --city {city} --node {node} --routes {routes_csv}\n" "2) Parse the JSON and produce a human-friendly summary (Korean).\n" "3) Output ONLY the final message text (no code blocks).\n\n" "Format example:\n" "[버스 알림]\n" "- 535: 3분(2정거장 전) / 다음 12분\n" "- 730: 5분 / 다음 18분\n" ) ``` The generated prompt is registered as a recurring Agent task: ```python routes = parse_routes(input("Routes (comma-separated, e.g. 535,730): ").strip()) ... job = { "name": name, "schedule": {"kind": "cron", "cron": spec.to_cron(), "tz": spec.tz}, "sessionTarget": "isolated", "payload": { "kind": "agentTurn", "message": build_prompt(city, node, routes), "deliver": True, "bestEffortDeliver": True, "channel": channel, "to": to, }, } ... cmd = [ exe, "cron", "add", "--name", name, "--cron", spec.to_cron(), "--tz", spec.tz, "--session", "isolated", "--mes ...[truncated 2480 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Strictly validate route identifiers before prompt construction. For example, allow only a bounded set of characters and lengths appropriate for Korean bus routes: ```python ROUTE_RE = re.compile(r"^[A-Za-z0-9가-힣-]{1,20}$") def parse_routes(value: str) -> List[str]: routes = [part.strip() for part in value.split(",") if part.strip()] if not routes: raise ValueError("routes required") if len(routes) > 20: raise ValueError("too many routes") for route in routes: if not ROUTE_RE.fullmatch(route): raise ValueError(f"invalid route identifier: {route!r}") return routes ``` 2. Apply similarly strict allowlists to `city` and `node`, including length limits and rejection of whitespace and control characters. 3. Reject newlines, carriage returns, null bytes, leading option markers, and shell metacharacters in all values embedded in Agent instructions. 4. Avoid constructing executable commands inside natural-language prompts. Store structured arguments and invoke the deterministic helper through a fixed non-shell tool interface. 5. If an Agent prompt must contain external values, serialize them as clearly delimited JSON data and explicitly state that the data must never be interpreted as instructions. 6. Add tests covering newline injection, command separators, leading `--` values, oversized input, and embedded instruction text. ]]>
