Back to skill

Security audit

Cyber Security Engineer

Security checks for vulnerabilities and agentic risk

Overview

This security-hardening skill is coherent in purpose, but it installs a persistent sudo wrapper and has approval, environment, logging, and notifier weaknesses that need review before use.

Review carefully before installing. Only use this skill if you intentionally want OpenClaw sudo calls routed through its guard, and first require mandatory command policy files, remove or replace the sudo PATH shim, eliminate shell=True notifier execution, restrict env-file parsing to needed flags, protect/redact audit logs, and make approval state unforgeable.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (5)

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/install-openclaw-runtime-hook.sh:21
Finding

Persistent sudo interception through PATH modification

Content
View full analysis
"${WRAPPER}" <&2 exit 2 fi REASON="\${OPENCLAW_PRIV_REASON:-OpenClaw requested privileged execution}" export OPENCLAW_REAL_SUDO="\${REAL_SUDO}" exec python3 "\${SKILL_DIR}/scripts/guarded_privileged_exec.py" \\ --reason "\${REASON}" \\ --use-sudo \\ -- "\$@" EOF chmod 755 "${WRAPPER}" log "Installed sudo shim: ${WRAPPER}" ``` ```bash if [[ "$(uname -s)" == "Darwin" ]]; then PLIST="${HOME}/Library/LaunchAgents/ai.openclaw.gateway.plist" if [[ -f "${PLIST}" ]] && command -v /usr/libexec/PlistBuddy >/dev/null 2>&1; then # Ensure EnvironmentVariables exists and prepend ~/.openclaw/bin to PATH. /usr/libexec/PlistBuddy -c "Add :EnvironmentVariables dict" "${PLIST}" 2>/dev/null || true EXISTING_PATH="$(/usr/libexec/PlistBuddy -c "Print :EnvironmentVariables:PATH" "${PLIST}" 2>/dev/null || true)" if [[ -z "${EXISTING_PATH}" ]]; then NEW_PATH="${BIN_DIR}:/usr/bin:/bin:/usr/sbin:/sbin" ...[truncated 2780 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/root_session_guard.py:197
Finding

Approval state can be forged through an unauthenticated state-management command

Content
View full analysis
None: ts = to_iso(now_utc()) state.privilege_mode = "elevated" state.last_elevated_activity_utc = ts state.last_transition_utc = ts state.last_action = "approved-command" state.approved_reason = reason state.approved_session_id = session_id if argv and not is_allowed(state, argv): state.allowed_commands.append(AllowedCommand(argv=argv, added_at_utc=ts)) ``` ```python approve = sub.add_parser( "approve", help="Approve a specific argv for elevated execution (adds to allowlist)", ) approve.add_argument("--reason", required=True, help="Approval reason") approve.add_argument( "--argv-json", required=True, help='Command argv as JSON array, e.g. ["launchctl","print","..."]', ) approve.add_argument("--session-id", help="Task session id to scope approvals") ``` ```python if args.command == "approve": argv = json.loads(args.argv_json) if not isinstance(argv, list) or not all(isinstance(x, str) for x in argv): print('{"status":"ERROR","error":"argv-json must be a JSON array of strings"}') return 2 approve_command(state, args.reason, argv, args.session_id) save_state(state_file, state) print('{"status":"OK","action":"approved-command"}') return 0 ``` The forged state is accepted by the guarded executor: ```python if session_id: authz = run_guard(args, "authorize", "--argv-json", argv_json, "--session-id", session_id) else: authz = run_guard(args, "authorize", "--argv-json", argv_json) if authz.returncode not in (0, 2): sys.stderr.write(authz.stderr or authz.stdout) return authz.returncode needs_appr ...[truncated 2023 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/notify_on_violation.py:101
Finding

Environment-controlled notification command is executed through a shell

Content
View full analysis
int: cmd = os.environ.get("OPENCLAW_VIOLATION_NOTIFY_CMD", "").strip() if not cmd: sys.stdout.write(message) return 0 # Best-effort: never crash the cycle due to a notifier. try: p = subprocess.run(cmd, shell=True, input=message, text=True) return int(p.returncode) except Exception: return 1 ``` ### Technical Analysis The complete value of `OPENCLAW_VIOLATION_NOTIFY_CMD` is interpreted by the platform shell. Shell metacharacters, pipelines, substitutions, redirections, and compound commands are therefore active. The command is invoked by the automated security cycle when a new compliance violation or severity escalation is detected. No executable allowlist, argument validation, trusted configuration source, or privilege separation is applied. This is not injection through the notification message itself because the message is passed through standard input. The vulnerability is the execution of an environment-controlled string with `shell=True`. ### Attack Path 1. An attacker gains the ability to influence the environment used by the assessment cycle or gateway. 2. The attacker sets `OPENCLAW_VIOLATION_NOTIFY_CMD` to a shell payload, such as a compound command. 3. A new compliance finding is introduced or the notification state is removed so existing findings appear new. 4. `auto_invoke_cycle.sh` runs `notify_on_violation.py`. 5. `_send_notification` passes the attacker-controlled string to the shell. 6. The shell executes the payload with the privileges and filesystem/network access of the account running the cycle. ### Impact Assessment Successful exploitation provides arbitrary command execution as the OpenClaw gateway or assessment-cycle user. The attacker can ...[truncated 261 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/live_assessment.py:68
Finding

Live assessment unnecessarily reads all values from the OpenClaw environment file

Content
View full analysis
Dict[str, str]: env_path = Path.home() / ".openclaw" / "env" if not env_path.exists(): return {} flags: Dict[str, str] = {} for line in env_path.read_text(encoding="utf-8").splitlines(): line = line.strip() if not line or line.startswith("#"): continue if line.startswith("export "): line = line[len("export ") :] if "=" in line: key, value = line.split("=", 1) flags[key.strip()] = value.strip().strip('"') return flags ``` ```python return { "openclaw_config_text": openclaw_config_text, "openclaw_config_json": load_openclaw_config(), "doctor_text": doctor_text, "gateway_status_text": gateway_status_text, "version_text": version_text, "port_report": port_report, "egress_report": egress_report, "env_flags": load_env_flags(), "command_policy": load_json_file(Path.home() / ".openclaw" / "security" / "command-policy.json"), "prompt_policy": load_json_file(Path.home() / ".openclaw" / "security" / "prompt-policy.json"), "egress_allowlist": load_json_file(Path.home() / ".openclaw" / "security" / "egress_allowlist.json"), } ``` ### Technical Analysis The assessment reads and stores every key-value pair from `~/.openclaw/env` in memory. Environment files commonly contain authentication tokens, API keys, webhook secrets, or service credentials. The subsequent checks only need to determine whether `OPENCLAW_REQUIRE_SESSION_ID` equals `1` and whether an approval token is configured; they do not need the actual values of unrelated entries or the approval token itself. No external transmission or report persistence of the complete `env_flags` object was identified in the reviewed code. The issue is ...[truncated 930 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/audit_logger.py:19
Finding

Privileged command arguments are logged without redaction or explicit permission enforcement

Content
View full analysis
None: """ Append an audit event to an on-disk JSONL timeline. Best-effort only: audit logging must never block privileged operations. """ try: AUDIT_LOG.parent.mkdir(parents=True, exist_ok=True) payload = {"ts_utc": _utc_now_iso(), **event} with AUDIT_LOG.open("a", encoding="utf-8") as f: f.write(json.dumps(payload, separators=(",", ":")) + "\n") except Exception: return ``` Complete command arguments are supplied to the logger: ```python def ask_for_approval(reason: str, command_argv: List[str]) -> bool: append_audit({"action": "approval_requested", "reason": reason, "argv": command_argv}) print("Approval required for elevated execution.") print(f"Reason: {reason}") print("Command argv:") print(json.dumps(command_argv, indent=2)) answer = input("Approve elevated access for this command? [y/N]: ").strip().lower() approved = answer in {"y", "yes"} append_audit({"action": "approval_decision", "reason": reason, "argv": command_argv, "approved": approved}) return approved ``` ```python def run_command(argv: List[str], use_sudo: bool, sudo_kill_cache: bool) -> int: sudo_bin = os.environ.get("OPENCLAW_REAL_SUDO", "sudo") exec_argv = [sudo_bin, "--"] + argv if use_sudo else argv print("Executing argv:") print(json.dumps(exec_argv, indent=2)) if use_sudo and sudo_kill_cache: subprocess.run([sudo_bin, "-k"], check=False, capture_output=True, text=True) append_audit({"action": "exec_start", "argv": argv, "use_sudo": use_sudo}) result = subprocess.run(exec_argv) append_audit({"action": "exec_finish", "argv": argv, "use_sudo": use_sudo, "r ...[truncated 1603 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (75)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The finding suggests the skill's actual primary behavior is prompt-policy configuration loading, not the broad hardening workflow it advertises. In this context, the mismatch materially increases danger because defenders may defer other safeguards expecting this skill to provide them.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The finding suggests the skill's actual primary behavior is prompt-policy configuration loading, not the broad hardening workflow it advertises. In this context, the mismatch materially increases danger because defenders may defer other safeguards expecting this skill to provide them.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The finding suggests the skill's actual primary behavior is prompt-policy configuration loading, not the broad hardening workflow it advertises. In this context, the mismatch materially increases danger because defenders may defer other safeguards expecting this skill to provide them.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The finding suggests the skill's actual primary behavior is prompt-policy configuration loading, not the broad hardening workflow it advertises. In this context, the mismatch materially increases danger because defenders may defer other safeguards expecting this skill to provide them.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The finding suggests the skill's actual primary behavior is prompt-policy configuration loading, not the broad hardening workflow it advertises. In this context, the mismatch materially increases danger because defenders may defer other safeguards expecting this skill to provide them.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The finding suggests the skill's actual primary behavior is prompt-policy configuration loading, not the broad hardening workflow it advertises. In this context, the mismatch materially increases danger because defenders may defer other safeguards expecting this skill to provide them.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The finding suggests the skill's actual primary behavior is prompt-policy configuration loading, not the broad hardening workflow it advertises. In this context, the mismatch materially increases danger because defenders may defer other safeguards expecting this skill to provide them.

Content

No source excerpt is available for this finding.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/generate_approved_ports.py (reported line 90)May include surrounding context.

python
rules.append(rule)

    rules.sort(key=lambda r: (int(r.get("port", 0)), str(r.get("protocol", "")), str(r.get("command", ""))))
    return rules, len(seen)


def main() -> int:

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
98% confidence
Finding

This is tool parameter abuse because the script exposes shell command execution through a configurable notification hook. In the context of a cyber-security-engineer skill focused on privilege governance and monitoring, such a hook is more dangerous because the script may run in privileged or sensitive environments, turning misconfiguration or env injection into full command execution.

Content

Scanner excerpt · scripts/notify_on_violation.py (reported line 108)May include surrounding context.

python
# Best-effort: never crash the cycle due to a notifier.
    try:
        p = subprocess.run(cmd, shell=True, input=message, text=True)
        return int(p.returncode)
    except Exception:
        return 1

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill advertises and references capabilities that imply shell execution, file access, and environment-variable use, but it does not declare any explicit tool scope or permission boundaries. In a security-sensitive skill, this creates ambiguity about what the agent may access or execute and weakens review-time enforcement of least privilege.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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.

Content

Scanner excerpt · assessments/compliance-dashboard.html (reported line 109)May include surrounding context.

html
<tr><td>privilege_approval_required</td><td>Route all privileged tasks through guarded_privileged_exec.py and enforce approval prompts for elevated execution.</td><td>Security Engineering</td><td>2026-03-15</td></tr>
<tr><td>least_privilege_enforced</td><td>Fix state dir ownership/permissions and enforce command allowlist/approval defaults.</td><td>Platform Security</td><td>2026-03-07</td></tr>
<tr><td>elevation_timeout_30m</td><td>Invoke guarded_privileged_exec.py for every elevated operation path.</td><td>Security Engineering</td><td>2026-03-15</td></tr>
<tr><td>audit_logging_privileged_actions</td><td>Create append-only correlated audit records linking approval, execution, and drop events.</td><td>SecOps</td><td>2026-03-22</td></tr>
<tr><td>open_ports_approved</td><td>Populate ~/.openclaw/security/approved_ports.json and remove unnecessary listeners.</td><td>Infrastructure</td><td>2026-02-28</td></tr>
<tr><td>insecure_ports_remediated</td><td>Enforce baseline checks to block insecure service ports.</td><td>Network Security</td><td>2026-04-01</td></tr>
        </tbody>

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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.

Content

Scanner excerpt · assessments/compliance-summary.json (reported line 74)May include surrounding context.

json
"observed_state": "Gateway logs and session transition logs are available.",
        "evidence": "gateway status reports log paths; root_session_guard records transition metadata.",
        "gap": "No single correlated privileged action audit timeline is guaranteed.",
        "mitigation": "Create append-only correlated audit records linking approval, execution, and drop events.",
        "owner": "SecOps",
        "due_date": "2026-03-22"
      },

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% 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.

Content

Scanner excerpt · assessments/openclaw-assessment.json (reported line 51)May include surrounding context.

json
"observed_state": "Gateway logs and session transition logs are available.",
      "evidence": "gateway status reports log paths; root_session_guard records transition metadata.",
      "gap": "No single correlated privileged action audit timeline is guaranteed.",
      "mitigation": "Create append-only correlated audit records linking approval, execution, and drop events.",
      "owner": "SecOps",
      "due_date": "2026-03-22"
    },

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · references/compliance-controls-map.json (reported line 96)May include surrounding context.

json
"iso27001": ["A.5.15"],
    "nist": ["PR.AA-05"],
    "default_risk": "medium",
    "expected_state": "su/doas or other escalation paths are restricted or guarded."
  },
  {
    "check_id": "backup_configured",

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/live_assessment.py (reported line 398)May include surrounding context.

python
"iso27001": ["A.5.15"],
    "nist": ["PR.AA-05"],
    "default_risk": "medium",
    "expected_state": "su/doas or other escalation paths are restricted or guarded."
  },
  {
    "check_id": "backup_configured",

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/live_assessment.py (reported line 400)May include surrounding context.

python
"iso27001": ["A.5.15"],
    "nist": ["PR.AA-05"],
    "default_risk": "medium",
    "expected_state": "su/doas or other escalation paths are restricted or guarded."
  },
  {
    "check_id": "backup_configured",

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

This shell script performs safety-relevant actions including subprocess execution and persistent file writes to assessment and log locations. While it logs timestamps, it does not disclose to the user that it will create or overwrite output artifacts or run monitoring commands, and no inline comment or docstring provides a user warning about these effects.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The helper writes JSON objects to disk and is used for both assessment scaffolds and compliance summaries, which may contain risk ratings, evidence, gaps, owners, and mitigations. Although the command prints output paths after writing, there is no prior comment, docstring, or warning that sensitive compliance information will be persisted locally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The HTML dashboard embeds assessment-controlled fields such as system name, title, gap, evidence, mitigation, owner, and due_date directly into HTML without escaping. If any of those values contain HTML or JavaScript, opening the generated dashboard can trigger stored cross-site scripting in the viewer’s browser, which is especially relevant because compliance evidence may include externally sourced or manually pasted content.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/guarded_privileged_exec.py (reported line 30)May include surrounding context.

python
str(args.timeout_minutes),
        *guard_args,
    ]
    return subprocess.run(cmd, capture_output=True, text=True)


def ask_for_approval(reason: str, command_argv: List[str]) -> bool:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
96% confidence
Finding

The script invokes a sudo-related binary whose path is taken from OPENCLAW_REAL_SUDO. If an attacker can influence the environment, they can substitute a malicious executable, causing arbitrary code execution when the script attempts to clear sudo state.

Content

Scanner excerpt · scripts/guarded_privileged_exec.py (reported line 52)May include surrounding context.

python
print(json.dumps(exec_argv, indent=2))
    if use_sudo and sudo_kill_cache:
        # Best-effort: ensure sudo timestamp for this user is not reused implicitly.
        subprocess.run([sudo_bin, "-k"], check=False, capture_output=True, text=True)
    append_audit({"action": "exec_start", "argv": argv, "use_sudo": use_sudo})
    result = subprocess.run(exec_argv)
    append_audit({"action": "exec_finish", "argv": argv, "use_sudo": use_sudo, "returncode": result.returncode})

Tainted flow: 'sudo_bin' from os.environ.get (line 46, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
98% confidence
Finding

This is a direct environment-to-exec path: sudo_bin comes from OPENCLAW_REAL_SUDO and is passed to subprocess.run. Because this script is designed to manage privileged operations, any environment injection substantially increases risk and can lead to arbitrary code execution.

Content

Scanner excerpt · scripts/guarded_privileged_exec.py (reported line 52)May include surrounding context.

python
print(json.dumps(exec_argv, indent=2))
    if use_sudo and sudo_kill_cache:
        # Best-effort: ensure sudo timestamp for this user is not reused implicitly.
        subprocess.run([sudo_bin, "-k"], check=False, capture_output=True, text=True)
    append_audit({"action": "exec_start", "argv": argv, "use_sudo": use_sudo})
    result = subprocess.run(exec_argv)
    append_audit({"action": "exec_finish", "argv": argv, "use_sudo": use_sudo, "returncode": result.returncode})

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
89% confidence
Finding

This call executes attacker-influenced argv and can optionally prefix it with a sudo binary taken from an environment variable. Although evaluate_command() provides a policy gate, this file still directly executes privileged commands and trusts external policy/guard modules; if policy is bypassed, misconfigured, or incomplete, this becomes arbitrary command execution with elevated privileges.

Content

Scanner excerpt · scripts/guarded_privileged_exec.py (reported line 54)May include surrounding context.

python
# Best-effort: ensure sudo timestamp for this user is not reused implicitly.
        subprocess.run([sudo_bin, "-k"], check=False, capture_output=True, text=True)
    append_audit({"action": "exec_start", "argv": argv, "use_sudo": use_sudo})
    result = subprocess.run(exec_argv)
    append_audit({"action": "exec_finish", "argv": argv, "use_sudo": use_sudo, "returncode": result.returncode})
    return result.returncode

Tainted flow: 'exec_argv' from os.environ.get (line 47, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
97% confidence
Finding

The tainted flow is real: exec_argv incorporates sudo_bin from os.environ and is then executed. In a privileged-execution workflow, environment-controlled executable resolution is especially dangerous because it can turn an approval wrapper into an attacker-controlled code launcher.

Content

Scanner excerpt · scripts/guarded_privileged_exec.py (reported line 54)May include surrounding context.

python
# Best-effort: ensure sudo timestamp for this user is not reused implicitly.
        subprocess.run([sudo_bin, "-k"], check=False, capture_output=True, text=True)
    append_audit({"action": "exec_start", "argv": argv, "use_sudo": use_sudo})
    result = subprocess.run(exec_argv)
    append_audit({"action": "exec_finish", "argv": argv, "use_sudo": use_sudo, "returncode": result.returncode})
    return result.returncode

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
96% confidence
Finding

This repeats the same environment-controlled sudo binary execution during cleanup. Cleanup paths are still exploitable execution paths, so a malicious OPENCLAW_REAL_SUDO value could run arbitrary code even after the main command completes.

Content

Scanner excerpt · scripts/guarded_privileged_exec.py (reported line 212)May include surrounding context.

python
append_audit({"action": "drop_elevation", "argv": argv, "reason": "post-command"})
        if args.use_sudo and args.sudo_kill_cache:
            sudo_bin = os.environ.get("OPENCLAW_REAL_SUDO", "sudo")
            subprocess.run([sudo_bin, "-k"], check=False, capture_output=True, text=True)


if __name__ == "__main__":

Static analysis

No suspicious patterns detected.