T09 · Insecure Skill Coding Practices
Error
- Location
- watchdog.sh:238
- Finding
- Shell Command Injection Through the TCP Check Fallback<![CDATA[ ## Vulnerability Details **File Location**: `watchdog.sh`, lines 238-240 **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```bash else # Bash /dev/tcp fallback if timeout "$timeout_s" bash -c "echo >/dev/tcp/${host}/${port}" 2>/dev/null; then success=true fi fi ``` The values reaching the vulnerable operation are read from the configuration without validation: ```bash host=$(echo "$svc" | jq -r '.host') port=$(echo "$svc" | jq -r '.port') timeout_ms=$(echo "$svc" | jq -r ".timeout_ms // $DEFAULT_TIMEOUT_MS") result=$(check_tcp "$host" "$port" "$timeout_ms") ``` ### Technical Analysis The `host` and `port` values originate in `watchdog.json`. When neither `nc` nor `ncat` is installed, they are interpolated directly into a command string passed to `bash -c`. Quoting the overall argument to `bash -c` does not make the interpolated content safe. Shell metacharacters contained in either value become part of the command text parsed by the newly launched shell. An attacker capable of modifying or supplying the watchdog configuration can therefore introduce command substitutions, command separators, redirections, or pipelines. For example, a malicious host value containing shell syntax could cause an additional command to run when the fallback is reached. Exploitation depends on the absence of both `nc` and `ncat`, but the fallback is explicitly supported and documented by the Skill. ### Attack Path 1. An attacker gains the ability to create or modify the JSON configuration used by the Skill, including through a user-provided `--config` path or `WATCHDOG_CONFIG`. 2. The attacker defines a TCP service and inserts shell metacharacters into its `host` or `port` field. 3. The user or Agent invokes `watchdog.sh` as documented. 4. The target environment does not have `nc` or `ncat`, causing execution to enter the `/dev/tcp` fallback. 5. The untrusted values are interpolated into the string passed to ...[truncated 718 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Do not interpolate configuration data into a `bash -c` command. - Validate `port` using a strict integer check and require a value between 1 and 65535. - Validate `host` against an explicit hostname, IPv4, or IPv6 grammar before using it. - Prefer requiring a dedicated TCP client such as `nc` or `ncat`. - If `/dev/tcp` must be retained, pass values as positional parameters to an isolated shell and validate them before invocation rather than embedding them in command text. - Reject malformed configuration before processing any service. - Add regression tests containing shell metacharacters in `host` and `port` fields and verify that no command is executed. A safer design is to validate first and then pass values positionally: ```bash [[ "$port" =~ ^[0-9]+$ ]] && (( port >= 1 && port <= 65535 )) || { echo "fail|0|Invalid TCP port" return } [[ "$host" =~ ^[A-Za-z0-9._:-]+$ ]] || { echo "fail|0|Invalid TCP host" return } if timeout "$timeout_s" bash -c \ 'exec 3<>"/dev/tcp/$1/$2"' bash "$host" "$port" 2>/dev/null; then success=true fi ``` The hostname validation should be made more precise if internationalized names or IPv6 zone identifiers must be supported. ]]>
