T09 · Insecure Skill Coding Practices
- Location
- scripts/gateway-qr.sh:81
- Finding
- Arbitrary Python Code Execution Through Unescaped Configuration Values<![CDATA[ ## Vulnerability Details **File Location**: `scripts/gateway-qr.sh`, lines 22-31 and 81-89 **Vulnerability Type**: Python source injection through unsafe shell interpolation **Risk Level**: High ### Vulnerable Code ```bash TOKEN=$(python3 -c " import json, sys try: c = json.load(open('$CONFIG')) print(c['gateway']['auth']['token']) except Exception as e: print(f'Error: {e}', file=sys.stderr) sys.exit(1) ") PORT=$(python3 -c " import json c = json.load(open('$CONFIG')) print(c.get('gateway', {}).get('port', 18789)) ") ``` ```bash PAYLOAD=$(python3 -c " import json print(json.dumps({ 'host': '$LAN_IP', 'port': int('$PORT'), 'token': '$TOKEN', 'tls': False }, separators=(',', ':'))) ") ``` ### Technical Analysis The script constructs executable Python source by directly interpolating shell variables into a `python3 -c` program. In particular, `TOKEN` and `PORT` originate from `~/.openclaw/openclaw.json` and are embedded inside single-quoted Python string literals without escaping. A token containing quote characters and a valid Python expression can terminate or alter the intended literal. For example, a value shaped like: ```text ' + str(__import__("os").system("COMMAND")) + ' ``` would cause the generated Python program to evaluate `os.system("COMMAND")` while constructing the dictionary. The exact payload must be represented using valid JSON escaping in the configuration file. The configuration path is also interpolated into Python source at `open('$CONFIG')`. Consequently, a specially crafted `HOME` value containing Python syntax presents an additional injection surface if the resulting configuration path can be created. This is source-code injection rather than ordinary malformed-input handling: attacker-controlled data changes the Python program executed by `python3 -c`. ### Attack Path 1. An attacker, compromised integration, or less-trusted process gains the ability to modify `~/.openclaw/openclaw.json` ...[truncated 1192 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Never insert shell-expanded values into executable Python source. - Load the configuration and construct the payload in a single statically quoted Python program. - Pass the configuration path through `sys.argv` or an environment variable instead of embedding it in source. - Validate the port as an integer from `1` through `65535`. - Validate the detected host with Python's `ipaddress.ip_address()` before using it. - Treat the token exclusively as data and let `json.dumps()` perform all required JSON escaping. A safer pattern is: ```bash PAYLOAD=$( python3 - "$CONFIG" "$LAN_IP" <<'PY' import ipaddress import json import sys config_path, host = sys.argv[1], sys.argv[2] with open(config_path, encoding="utf-8") as config_file: config = json.load(config_file) token = config["gateway"]["auth"]["token"] port = int(config.get("gateway", {}).get("port", 18789)) if not isinstance(token, str) or not token: raise ValueError("Gateway token must be a non-empty string") if not 1 <= port <= 65535: raise ValueError("Gateway port is outside the valid range") ipaddress.ip_address(host) print(json.dumps({ "host": host, "port": port, "token": token, "tls": False, }, separators=(",", ":"))) PY ) ``` This keeps configuration values outside the Python grammar and prevents them from becoming executable code. ]]>
