T09 · Insecure Skill Coding Practices
- Location
scripts/setup_bots.py:13- Finding
Arbitrary Command Execution Through Shell Injection
- Content
View full analysis
Vulnerability Details
File Location:
scripts/setup_bots.py, lines 13-18, 27-30, and 163-168
Vulnerability Type: OS command injection through attacker-controlled configuration values
Risk Level: HighVulnerable Code
python def run_command(cmd, check=True): """Run a shell command and return output.""" result = subprocess.run( cmd, shell=True, capture_output=True, text=True )python def create_agent(agent_id, workspace_path): """Create a new OpenClaw agent.""" print(f"Creating agent: {agent_id}") cmd = f'openclaw agents add {agent_id} --workspace {workspace_path} --non-interactive' run_command(cmd) print(f"✓ Agent {agent_id} created")python def bind_agents(bots): """Bind agents to Feishu accounts.""" print("Binding agents to Feishu accounts...") for bot in bots: cmd = f"openclaw agents bind --agent {bot['agentId']} --bind feishu:{bot['name']}" run_command(cmd, check=False) print(f"✓ Bound {bot['agentId']} to {bot['name']}")Technical Analysis
The script constructs shell command strings using the configuration-controlled
agentIdandnamefields. It then executes those strings withsubprocess.run(..., shell=True).The shell interprets metacharacters such as semicolons, command substitutions, pipes, redirections, and logical operators contained in these values. Neither field is validated nor safely quoted before it reaches the shell. Consequently, a configuration file can inject additional commands rather than merely supplying OpenClaw identifiers.
Setting
check=Falseinbind_agentsdoes not mitigate injection. It only prevents the Python process from terminating when the shell command returns a nonzero status.Attack Path
- An attacker creates or modifies the JSON configuration supplied to
setup_bots.py. - The att ...[truncated 1036 chars]
- An attacker creates or modifies the JSON configuration supplied to
- Remediation
View remediation
Remediation Suggestions
-
Remove
shell=Trueand pass each command as an argument list:python subprocess.run( [ "openclaw", "agents", "add", agent_id, "--workspace", workspace_path, "--non-interactive", ], shell=False, check=True, capture_output=True, text=True, ) -
Apply the same argument-list approach to the binding command.
-
Validate
agentIdandnameagainst a strict allowlist, such as^[A-Za-z0-9_-]+$. -
Reject empty, excessively long, or duplicate identifiers.
-
Do not rely only on shell quoting. Avoiding shell interpretation is the primary control.
-
Report binding failures instead of suppressing them with
check=False. -
Run the setup process with the minimum filesystem and OpenClaw privileges required.
-
