T07 · Tool Hijacking and Spoofing
- Location
scripts/tasmota-status.py:10- Finding
Execution of an Untrusted Controller Script Outside the Audited Package
- Content
View full analysis
Vulnerability Details
File Location:
scripts/tasmota-status.py, lines 10 and 16-20
Vulnerability Type: Local tool hijacking through a hard-coded external script path
Risk Level: HighVulnerable Code
python CSV_FILE = Path.home() / ".openclaw/workspace/memory/tasmota-inventory.csv" def get_device_status(ip): """Get power status from device""" try: result = subprocess.run( ["python3", Path.home() / ".openclaw/workspace/scripts/tasmota-control.py", ip, "status", "0"], capture_output=True, timeout=3 )Technical Analysis
The bulk-status script does not execute the audited controller bundled alongside it at
scripts/tasmota-control.py. Instead, it invokes a Python file from the separate, hard-coded path~/.openclaw/workspace/scripts/tasmota-control.py.The command uses an argument list rather than a shell, so this is not shell-command injection. The vulnerability is that the identity and integrity of the executed controller are not established. Any process or user capable of creating or modifying the external workspace file can replace the expected controller with arbitrary Python code. The substituted code will execute with the privileges of the user running
tasmota-status.py.This behavior is unnecessary for the declared bulk-status functionality because the audited package already contains the required controller script.
Attack Path
- An attacker gains write access to
~/.openclaw/workspace/scripts/, or compromises another process that can write there. - The attacker creates or replaces
tasmota-control.pyat that location with malicious Python code. - A user runs the documented command:
bash python3 scripts/tasmota-status.py - For each inventory entry,
subprocess.run()launches the attacker-controlled external script throughpython3. - The malicious script executes with the ...[truncated 850 chars]
- An attacker gains write access to
- Remediation
View remediation
Remediation Suggestions
Resolve the bundled controller relative to the current script rather than through the user's workspace:
python SCRIPT_DIR = Path(__file__).resolve().parent CONTROLLER = SCRIPT_DIR / "tasmota-control.py" result = subprocess.run( ["python3", str(CONTROLLER), ip, "status", "0"], capture_output=True, timeout=3, check=False, )Additional hardening measures:
- Verify that
CONTROLLERis a regular file inside the expected package directory. - Reject symlinks if the deployment model does not require them.
- Install package code in a location that is not writable by less-trusted users or processes.
- Prefer importing a shared controller module and calling its function directly, avoiding subprocess-based tool resolution entirely.
- Make the inventory path explicitly configurable and document its trust boundary.
- Validate inventory addresses with Python's
ipaddressmodule rather than accepting every string beginning with192. - Apply restrictive permissions to inventory and package files.
- Verify that
