T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/habitica.sh:7
- Finding
- Arbitrary Code Execution Through Sourced Credential Configuration## Vulnerability Details **File Location**: `scripts/habitica.sh`, lines 7-13 **Vulnerability Type**: Unsafe execution of configuration-file contents **Risk Level**: High ### Vulnerable Code ```bash CONFIG_FILE="${HABITICA_CONFIG:-$HOME/.habitica}" # Load credentials load_creds() { if [[ -f "$CONFIG_FILE" ]]; then source "$CONFIG_FILE" fi ``` ### Technical Analysis The script loads credentials by passing the configuration file to the Bash `source` built-in. `source` does not parse the file as passive configuration data; it executes every statement in that file in the current shell process. The file path defaults to `~/.habitica`, but it can also be selected through the `HABITICA_CONFIG` environment variable. Consequently, anyone able to modify the default credential file or influence both the environment and referenced file can cause arbitrary shell commands to run when any Habitica command invokes `load_creds`. The malicious content does not need to resemble a credential assignment. It can contain command substitutions, shell functions, redirections, pipelines, or direct command invocations. Execution occurs before the requested Habitica API operation and inherits the script's user privileges and environment, including credentials subsequently loaded by the file. ### Attack Path 1. An attacker gains write access to `~/.habitica`, or causes `HABITICA_CONFIG` to reference a file under attacker control. 2. The attacker inserts shell commands into that file, for example: ```bash HABITICA_USER_ID="expected-user" HABITICA_API_TOKEN="expected-token" arbitrary_command ``` 3. The user or AI Agent invokes any command through `scripts/habitica.sh`. 4. `load_creds` calls `source "$CONFIG_FILE"`. 5. Bash executes `arbitrary_command` with the privileges of the user running the skill. 6. The injected command can access local files and environment variables, steal credentials, alter ...[truncated 1003 chars]
- Remediation
- ## Remediation Suggestions - Do not use `source`, `.`, or `eval` to read credential files. - Parse only an explicit allowlist of supported keys, such as `HABITICA_USER_ID` and `HABITICA_API_TOKEN`. - Reject unknown keys, shell metacharacters, command substitutions, malformed lines, and duplicate assignments. - Verify that the configuration is a regular file owned by the current user and is not a symbolic link. - Require restrictive permissions, preferably mode `0600`, and fail securely when ownership or permissions are unsafe. - Validate values according to Habitica's expected credential formats before using them. - If `HABITICA_CONFIG` remains supported, treat it as untrusted input and apply the same ownership, type, and permission checks to the selected path. - Prefer a dedicated secret store or environment variables supplied through a trusted execution environment. A safe implementation should read values as data rather than shell syntax, for example by using a strict parser: ```bash load_creds() { local config_file="${HABITICA_CONFIG:-$HOME/.habitica}" if [[ -f "$config_file" ]]; then while IFS='=' read -r key value; do [[ -z "$key" || "$key" == \#* ]] && continue value="${value%$'\r'}" if [[ "$value" == \"*\" && "$value" == *\" ]]; then value="${value:1:${#value}-2}" fi case "$key" in HABITICA_USER_ID) HABITICA_USER_ID="$value" ;; HABITICA_API_TOKEN) HABITICA_API_TOKEN="$value" ;; *) echo "Error: Unsupported configuration key: $key" >&2 exit 1 ;; esac done < "$config_file" fi if [[ -z "${HABITICA_USER_ID:-}" || -z "${HABITICA_API_TOKEN:-}" ]]; then echo "Error: Habitica credentials not found." >&2 exit 1 fi } ...[truncated 102 chars]
