Back to skill

Security audit

OpenClaw Doctor

Security checks for vulnerabilities and agentic risk

Overview

This OpenClaw diagnostic skill is mostly coherent, but its repair mode can change services and run a separate workspace script without enough guardrails.

Install only if you want an agent to run local OpenClaw health checks. Treat the default diagnostic as read-only, but do not use --fix unless you are comfortable with service restarts, LaunchAgent loading, config permission changes, and execution of a workspace log-rotation script that should be inspected first.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/diagnose.sh:151
Finding
Execution of an Untrusted Workspace Log-Rotation Script<![CDATA[ ## Vulnerability Details **File Location**: `scripts/diagnose.sh`, lines 151–153 **Vulnerability Type**: Execution of a mutable external script without trust validation **Risk Level**: Medium ```bash if $FIX && [ -f "$OC_WORKSPACE/scripts/log_rotate.sh" ]; then bash "$OC_WORKSPACE/scripts/log_rotate.sh" >/dev/null 2>&1 && fixed "Logs rotated" fi ``` ### Technical Analysis When invoked with `--fix`, the diagnostic executes `~/.openclaw/workspace/scripts/log_rotate.sh` if the OpenClaw log directory exceeds 500 MB. This script is outside the audited Skill package and may reside in a user-modifiable workspace. Before execution, the code only confirms that the path is a regular file. It does not validate: - The file's owner or group - Whether its permissions permit modification by untrusted users - Its canonical path or possible symbolic-link resolution - Its provenance or relationship to this Skill - Its integrity through a pinned cryptographic hash - Whether the file changed between validation and execution Consequently, the diagnostic crosses a local trust boundary by treating a mutable workspace file as executable trusted code. Quoting the path prevents argument splitting but does not mitigate malicious contents within the script. ### Attack Path 1. An attacker, compromised process, malicious extension, or other actor with write access to `~/.openclaw/workspace/scripts/` creates or replaces `log_rotate.sh`. 2. The attacker inserts arbitrary shell commands into that file. 3. The OpenClaw log directory reaches the script's failure threshold of at least 500 MB. 4. A user or agent invokes: ```bash bash scripts/diagnose.sh --fix ``` 5. The diagnostic launches the attacker-controlled file with `bash`. 6. The injected commands execute under the identity and environment of the account running the diagnostic. This path requires local write access to the workspace and explicit use of `--fix`; it is not triggered during the documented read-on ...[truncated 783 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Do not execute a workspace-provided repair script.** Bundle a reviewed log-rotation implementation inside the Skill package and invoke it using a path derived from the diagnostic script's trusted installation directory: ```bash SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" bash "$SCRIPT_DIR/log_rotate.sh" ``` 2. **Prefer implementing rotation directly** in `diagnose.sh` with explicit file-selection, retention, and size limits. Avoid evaluating generated shell text or invoking scripts from mutable workspace locations. 3. **If external delegation is unavoidable, validate the target before execution:** - Resolve and verify its canonical path. - Reject symbolic links. - Require ownership by the invoking user or a designated trusted administrator. - Reject files writable by group or other users. - Compare the file against a pinned cryptographic hash or signed manifest. - Minimize time-of-check/time-of-use exposure. 4. **Request explicit confirmation** before executing any external repair component and display the exact resolved path. 5. **Run repair operations with minimum privileges.** Use a restricted environment and avoid inheriting unnecessary credentials or sensitive environment variables. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (21)

Missing User Warnings

High
Confidence
98% confidence
Finding
The skill advertises automatic fixes including restarting services, changing file permissions, rotating logs, clearing session data, and restarting channels, but the description does not prominently require user approval before those write operations. Because the skill is framed as routine diagnostics and health checks, users may trigger it expecting read-only inspection while the agent performs disruptive or destructive changes.

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill clearly instructs the agent to execute a shell script (`scripts/diagnose.sh`) yet declares no explicit tool scope or permission boundaries. That creates an authorization ambiguity where a troubleshooting request can invoke command execution without the skill manifest clearly constraining or disclosing that capability.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The activation phrasing is broad enough to match ordinary troubleshooting requests such as 'system feels slow' or 'diagnose my setup,' which can cause this skill to be selected unexpectedly. In this context, accidental activation is more dangerous because the skill is tied to shell execution and repair behavior, increasing the chance of unintended diagnostics or system changes.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The --fix path performs immediate state-changing actions, including restarting the gateway service, without any interactive confirmation, dry-run, or additional guardrail. In a diagnostic skill, this can unexpectedly disrupt running workloads or change host state when invoked by an agent or user who only intended inspection.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# Service configuration
if [ "$OS" = "Darwin" ]; then
  PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
  if [ -f "$PLIST" ]; then
    pass "LaunchAgent plist exists"
    if launchctl list 2>/dev/null | grep -q "ai.openclaw.gateway"; then
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
else
      fail "LaunchAgent not loaded"
      if $FIX; then
        launchctl load "$PLIST" 2>/dev/null && fixed "LaunchAgent loaded"
      fi
    fi
    grep -q "<key>KeepAlive</key>" "$PLIST" 2>/dev/null && pass "KeepAlive enabled" || warn "KeepAlive not configured"
Confidence
88% confidence
Finding
In --fix mode, the script will load a user LaunchAgent automatically, which re-enables persistent execution at login without a separate confirmation prompt. In the context of an agent skill, automatic persistence-related actions are more sensitive because they can change long-lived execution behavior on the host beyond a one-time diagnostic run.

Session Persistence

Medium
Category
Rogue Agent
Content
else
      fail "LaunchAgent not loaded"
      if $FIX; then
        launchctl load "$PLIST" 2>/dev/null && fixed "LaunchAgent loaded"
      fi
    fi
    grep -q "<key>KeepAlive</key>" "$PLIST" 2>/dev/null && pass "KeepAlive enabled" || warn "KeepAlive not configured"
Confidence
88% confidence
Finding
In --fix mode, the script will load a user LaunchAgent automatically, which re-enables persistent execution at login without a separate confirmation prompt. In the context of an agent skill, automatic persistence-related actions are more sensitive because they can change long-lived execution behavior on the host beyond a one-time diagnostic run.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
section "Power & Resilience"
  
  AUTO_RESTART=$(pmset -g 2>/dev/null | awk '/autorestart/ {print $2}')
  [ "$AUTO_RESTART" = "1" ] && pass "Auto-restart after power loss" || warn "Auto-restart disabled (sudo pmset -a autorestart 1)"
  
  SLEEP_VAL=$(pmset -g 2>/dev/null | awk '/^ sleep / {print $2}')
  [ "$SLEEP_VAL" = "0" ] && pass "System never sleeps" || warn "System may sleep (sleep=$SLEEP_VAL, fix: sudo pmset -a sleep 0)"
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
section "Power & Resilience"
  
  AUTO_RESTART=$(pmset -g 2>/dev/null | awk '/autorestart/ {print $2}')
  [ "$AUTO_RESTART" = "1" ] && pass "Auto-restart after power loss" || warn "Auto-restart disabled (sudo pmset -a autorestart 1)"
  
  SLEEP_VAL=$(pmset -g 2>/dev/null | awk '/^ sleep / {print $2}')
  [ "$SLEEP_VAL" = "0" ] && pass "System never sleeps" || warn "System may sleep (sleep=$SLEEP_VAL, fix: sudo pmset -a sleep 0)"
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
else
    warn "Config permissions $PERM (should be 600)"
    if $FIX; then
      chmod 600 "$OC_HOME/openclaw.json" 2>/dev/null && fixed "Config permissions set to 600"
    fi
  fi
fi
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.