T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- clawstatus.py:2742
- Finding
- Unauthenticated Administrative APIs Permit OpenClaw Configuration Changes and Cron Control<![CDATA[ ## Vulnerability Details **File Location**: `clawstatus.py:2742-2745`, with affected routes at `clawstatus.py:2814-2889` and privileged operations at `clawstatus.py:818-976` **Vulnerability Type**: Missing authentication and authorization on administrative endpoints **Risk Level**: High ### Vulnerable Code The application explicitly disables authentication: ```python def create_app() -> 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/<agent_id>/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/<job_id>/model") def api_cron_model_update(job_id: str): auth_resp = _require_a ...[truncated 4863 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Load and enforce the configured authentication token: ```python def create_app() -> 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. ]]>
