Back to skill

Security audit

ClawStatus

Security checks for vulnerabilities and agentic risk

Overview

ClawStatus is a legitimate-looking OpenClaw dashboard, but it exposes sensitive status data and administrative controls without authentication when run as documented.

Only install or run this in a tightly controlled local environment. Do not bind it to 0.0.0.0 or expose it to a network unless authentication is enforced, mutation endpoints are disabled or protected, and sensitive dashboard responses are reviewed/redacted. Treat it as an administrative control surface, not a read-only status page.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
clawstatus.py:2742
Finding

Unauthenticated Administrative APIs Permit OpenClaw Configuration Changes and Cron Control

Content
View full analysis
Flask: app = Flask(__name__) # Requirement: page works out of box, no token input needed required_token = None ``` The authorization helper permits every request when no token is configured: ```python def _is_authorized(required_token: Optional[str]) -> bool: if not required_token: return True got = _token_from_request() return bool(got and got == required_token) def _require_auth(required_token: Optional[str]): if _is_authorized(required_token): return None return jsonify({"error": "unauthorized", "valid": False}), 401 ``` State-changing routes rely on this ineffective check: ```python @app.post("/api/agents//model") def api_agent_model_update(agent_id: str): auth_resp = _require_auth(required_token) if auth_resp is not None: return auth_resp payload = request.get_json(silent=True) or {} model_id = str(payload.get("model") or "").strip() if not model_id: return jsonify({"error": "missing model"}), 400 try: result = _update_agent_model(agent_id, model_id) except KeyError: return jsonify({"error": "agent not found", "agentId": agent_id}), 404 except ValueError: return jsonify({"error": "invalid model", "model": model_id}), 400 except PermissionError as e: return jsonify({"error": f"write failed: {e}"}), 500 except OSError as e: return jsonify({"error": f"write failed: {e}"}), 500 return jsonify(result) @app.post("/api/crons//model") def api_cron_model_update(job_id: str): auth_resp = _require_a ...[truncated 4863 chars]
Remediation
View remediation
Flask: app = Flask(__name__) required_token = _load_auth_token() ``` 2. Fail closed when binding to any non-loopback address. Refuse startup on `0.0.0.0` or an external address unless strong authentication is configured. 3. Separate read-only monitoring permissions from administrative permissions. Administrative routes should require a distinct, higher-privilege credential. 4. Disable mutation endpoints by default. Require an explicit option such as `--enable-admin-api` before registering routes that edit models, execute jobs, delete jobs, or restart services. 5. Add CSRF protection to browser-accessible state-changing routes. Use unpredictable CSRF tokens and restrictive `SameSite` cookies if session-based authentication is introduced. 6. Do not accept authentication tokens through URL query parameters because URLs may be stored in browser history, proxy logs, and access logs. 7. Compare secret tokens using `hmac.compare_digest()` and enforce transport encryption through a secured reverse proxy when remote access is required. 8. Apply rate limits and record authenticated audit events for every configuration change, job execution, job deletion, and service restart. 9. Bind to `127.0.0.1` by default and update the documentation to recommend SSH tunneling or an authenticated TLS reverse proxy for remote access. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
clawstatus.py:2561
Finding

Unauthenticated APIs Expose Sessions, Cron Payloads, Task Instructions, and Local Operational Metadata

Content
View full analysis
Dict[str, Any]: status_data, status_err = _run_openclaw_status() status_ts_ms = _cache_ts_ms(_status_cache, _status_lock) subagents = _collect_subagent_runs() subagents_ts_ms = int(time.time() * 1000) agents = _collect_agents_data(status_data or {}, subagents) agents_ts_ms = int(time.time() * 1000) crons = _collect_cron_data() crons_ts_ms = int(time.time() * 1000) models = _collect_models_usage() models_ts_ms = _cache_ts_ms(_models_cache, _models_lock) or int(time.time() * 1000) memory_data = _collect_memory_data(status_data=status_data or {}, crons_data=crons) memory_ts_ms = _cache_ts_ms(_memory_cache, _memory_lock) or int(time.time() * 1000) openclaw_summary = _collect_openclaw_summary(status_data, status_err) openclaw_ts_ms = status_ts_ms or int(time.time() * 1000) sessions = (status_data or {}).get("sessions", {}) recent_sessions = sessions.get("recent", []) if isinstance(sessions, dict) else [] main_model = "unknown" main_tokens = 0 for rs in recent_sessions: if not isinstance(rs, dict): continue if rs.get("agentId") == "main": main_model = str(rs.get("model") or main_model) main_tokens = int(rs.get("totalTokens") or 0) break generated_at_ms = int(time.time() * 1000) source_timestamps = { "status": status_ts_ms, "openclaw": openclaw_ts_ms, "agents": agents_ts_ms, "subagents": subagents_ts_ms, "crons": crons_ts_ms, "models": ...[truncated 5861 chars]
Remediation
View remediation
` by default. 5. Redact or pseudonymize: - Session and agent identifiers. - Workspace and memory filenames. - Local filesystem paths. - Gateway URLs and service details. - Provider/account metadata. - Task prompts and operational notes. 6. Add role-based authorization so read-only users cannot access sensitive task content or administrative metadata. 7. Use `127.0.0.1` as the recommended deployment binding. For remote access, require TLS and authentication through a properly configured reverse proxy. 8. Add security-focused response tests that verify anonymous requests receive `401 Unauthorized` and that authorized responses do not contain raw secrets, credentials, prompts, or unnecessary filesystem details. 9. Review all fields returned by the external `openclaw status --json` command before forwarding them. Apply an allowlist rather than returning complete upstream structures. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (19)

Context-Inappropriate Capability

Critical
Category
Not specified by scanner
Confidence
100% confidence
Finding

The application explicitly sets required_token = None, disabling authentication even though token-loading support exists elsewhere in the code. Because the server exposes admin endpoints that modify config, restart services, trigger jobs, and delete jobs, any network-reachable user can perform those actions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code rewrites openclaw.json and then restarts OpenClaw, meaning a web request can persistently modify system configuration and apply it immediately. In context, this is highly dangerous because the app is a local admin dashboard with direct access to the user's OpenClaw home directory and service lifecycle.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The comment states the page should work without token input, normalizing unauthenticated access despite the presence of privileged mutation endpoints. That design choice makes the overall system more dangerous because it removes friction and encourages deployment of an admin interface with no access control.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

This dashboard is not purely observational; it exposes administrative mutation endpoints for model switching, cron execution, and cron deletion. A status dashboard that can change runtime state becomes a control surface, and in this file that control surface is available without auth, dramatically increasing the attack surface.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · clawstatus.py (reported line 4475)May include surrounding context.

python
stdin=subprocess.DEVNULL,
            start_new_session=True,
            cwd=os.getcwd(),
            env=os.environ.copy(),
        )
        _write_pid(proc.pid)

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 · clawstatus.py (reported line 228)May include surrounding context.

python
def _run_status_cmd(cmd: List[str], timeout_sec: int) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
    try:
        proc = subprocess.run(
            cmd,
            capture_output=True,
            text=True,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

This allows the application to restart a user-level systemd service from a web-exposed code path. The command itself is fixed, but in this application's context it is part of an unauthenticated administrative workflow, enabling remote service disruption or forced restarts.

Content

Scanner excerpt · clawstatus.py (reported line 823)May include surrounding context.

python
# 1. Try systemctl user service
    for svc in ("openclaw.service", "openclaw"):
        try:
            proc = subprocess.run(
                ["systemctl", "--user", "restart", svc],
                capture_output=True, text=True, timeout=10,
            )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

This endpoint path can restart OpenClaw by executing a binary whose path may come from the OPENCLAW_BIN environment variable. In this file, admin APIs are exposed without authentication, so a remote caller can trigger privileged operational changes, and if the runtime environment is attacker-influenced, the invoked executable may be replaced with an arbitrary program.

Content

Scanner excerpt · clawstatus.py (reported line 841)May include surrounding context.

python
or str(HOME / ".npm-global" / "bin" / "openclaw")
    )
    try:
        proc = subprocess.run(
            [openclaw_bin, "restart"],
            capture_output=True, text=True, timeout=10,
        )

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

Medium
Category
Data Flow
Confidence
96% confidence
Finding

The binary path used for restart can be sourced from an environment variable, creating a tainted path into code execution. In a privileged service context, if an attacker can influence environment configuration or service startup settings, they can redirect this call to an arbitrary executable and gain code execution under the dashboard's privileges.

Content

Scanner excerpt · clawstatus.py (reported line 841)May include surrounding context.

python
or str(HOME / ".npm-global" / "bin" / "openclaw")
    )
    try:
        proc = subprocess.run(
            [openclaw_bin, "restart"],
            capture_output=True, text=True, timeout=10,
        )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This path updates agent model configuration and triggers a restart without any backend-enforced warning, approval, or safety check. In an exposed dashboard, silent state changes can cause downtime, behavioral drift, or unexpected billing impacts with little traceability.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
97% confidence
Finding

This launches openclaw cron run from an HTTP-triggerable endpoint, causing server-side state changes and task execution. Because the dashboard explicitly disables auth, any reachable user can trigger cron jobs, which may execute arbitrary downstream actions configured in OpenClaw.

Content

Scanner excerpt · clawstatus.py (reported line 903)May include surrounding context.

python
if not found:
        raise KeyError(job_id)
    try:
        subprocess.Popen(
            ["openclaw", "cron", "run", job_id, "--timeout", "30000"],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The backend performs destructive cron deletion immediately and does not enforce any server-side confirmation, safety interlock, or soft-delete behavior. Client-side confirmation dialogs are insufficient because an attacker can call the API directly and bypass the UI entirely.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

This subprocess deletes cron jobs through an unauthenticated HTTP API. Even though shell injection is not present, the capability is destructive and allows remote tampering with automation state, potentially disabling monitoring, backups, or security-related jobs.

Content

Scanner excerpt · clawstatus.py (reported line 930)May include surrounding context.

python
or str(HOME / ".npm-global" / "bin" / "openclaw")
    )
    try:
        proc = subprocess.run(
            [openclaw_bin, "cron", "delete", job_id],
            capture_output=True, text=True, timeout=15,
        )

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

Medium
Category
Data Flow
Confidence
95% confidence
Finding

The cron deletion command also trusts OPENCLAW_BIN from the environment, creating a path-to-execution issue for a privileged action. This is especially risky in service deployments where environment variables may be configurable through unit files, wrappers, or orchestration metadata.

Content

Scanner excerpt · clawstatus.py (reported line 930)May include surrounding context.

python
or str(HOME / ".npm-global" / "bin" / "openclaw")
    )
    try:
        proc = subprocess.run(
            [openclaw_bin, "cron", "delete", job_id],
            capture_output=True, text=True, timeout=15,
        )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This endpoint edits cron model configuration immediately through the backend with no server-side warning or confirmation semantics. While not a memory-safety flaw, it is a risky design because direct API callers can induce costly or behavior-changing automation updates invisibly.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
98% confidence
Finding

This subprocess edits cron job model configuration via an HTTP endpoint, changing system behavior at runtime. In the context of this dashboard, the endpoint is reachable without auth, so unauthorized users can alter model selection and indirectly affect cost, output quality, or behavior of scheduled jobs.

Content

Scanner excerpt · clawstatus.py (reported line 961)May include surrounding context.

python
or str(HOME / ".npm-global" / "bin" / "openclaw")
    )
    try:
        proc = subprocess.run(
            [openclaw_bin, "cron", "edit", job_id, "--model", model_id],
            capture_output=True, text=True, timeout=15,
        )

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

Medium
Category
Data Flow
Confidence
95% confidence
Finding

The cron edit command inherits the same tainted executable-path problem through OPENCLAW_BIN. An attacker who can control that environment value can turn a routine config change into arbitrary program execution by the service.

Content

Scanner excerpt · clawstatus.py (reported line 961)May include surrounding context.

python
or str(HOME / ".npm-global" / "bin" / "openclaw")
    )
    try:
        proc = subprocess.run(
            [openclaw_bin, "cron", "edit", job_id, "--model", model_id],
            capture_output=True, text=True, timeout=15,
        )

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 · clawstatus.py (reported line 4468)May include surrounding context.

python
if not debug:
            args.append("--no-debug")

        proc = subprocess.Popen(
            args,
            stdout=logf,
            stderr=logf,

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 · clawstatus.py (reported line 4530)May include surrounding context.

python
cmd = [sys.executable, self_path, "--host", host, "--port", str(port), "--debug"]
        
        try:
            proc = subprocess.Popen(
                cmd,
                cwd=os.path.dirname(self_path),
                stdout=sys.stdout,