T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/feishu-reaction.sh:11
- Finding
- Arbitrary Python Code Execution Through OPENCLAW_CONFIG Path Injection## Vulnerability Details **File Location**: `scripts/feishu-reaction.sh`, lines 11–19 **Vulnerability Type**: Python source injection through unsafe shell-variable interpolation **Risk Level**: High ### Vulnerable Code ```bash # Locate openclaw config CONFIG="${OPENCLAW_CONFIG:-$HOME/.openclaw/openclaw.json}" if [ ! -f "$CONFIG" ]; then echo "Error: openclaw.json not found at $CONFIG" >&2 exit 1 fi # Extract Feishu credentials APP_ID=$(python3 -c "import json;print(json.load(open('$CONFIG'))['channels']['feishu']['appId'])") APP_SECRET=$(python3 -c "import json;print(json.load(open('$CONFIG'))['channels']['feishu']['appSecret'])") ``` ### Technical Analysis The value of `OPENCLAW_CONFIG`, stored in `CONFIG`, is interpolated directly into Python source passed to `python3 -c`. Shell quoting protects the shell command structurally, but it does not encode the value as a safe Python string literal. An attacker-controlled configuration path containing a single quote and valid Python syntax can terminate the intended `open('...')` expression and inject additional Python statements or expressions. The file-existence check does not eliminate the issue because Unix filenames can contain quotes and other characters usable in a Python injection sequence. If an attacker can create a correspondingly named file and control `OPENCLAW_CONFIG`, the injected Python executes with the privileges of the user running the Skill. The unsafe value is evaluated twice, once while extracting `appId` and again while extracting `appSecret`. ### Attack Path 1. An attacker gains influence over the environment used to invoke the Skill, particularly `OPENCLAW_CONFIG`. 2. The attacker creates a file whose path both passes the `-f` check and contains characters that terminate the Python string literal. 3. The attacker sets `OPENCLAW_CONFIG` to that crafted path. 4. The script interpolates the path into the `python3 -c` program without ...[truncated 1002 chars]
- Remediation
- ## Remediation Suggestions Never embed a dynamic path directly in Python source. Pass it as a positional argument: ```bash APP_ID=$(python3 - "$CONFIG" <<'PY' import json import sys with open(sys.argv[1], encoding="utf-8") as config_file: config = json.load(config_file) print(config["channels"]["feishu"]["appId"]) PY ) APP_SECRET=$(python3 - "$CONFIG" <<'PY' import json import sys with open(sys.argv[1], encoding="utf-8") as config_file: config = json.load(config_file) print(config["channels"]["feishu"]["appSecret"]) PY ) ``` Additional hardening should include: - Resolve the path to a canonical absolute path before use. - Restrict custom configuration paths to explicitly trusted directories where feasible. - Verify that the configuration is a regular file and is not unexpectedly writable by other users. - Parse the configuration once rather than invoking Python separately for each credential. - Ensure errors never print `appSecret` or tenant tokens. - Apply restrictive filesystem permissions to the OpenClaw configuration.
