Back to skill

Security audit

WebClaw

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed web-dashboard installer, but it pulls unreviewed remote code and installs persistent system services with broad host privileges.

Review this carefully before installing on a real server. It is not clearly malicious, but installation gives it sudo-backed control over nginx, certbot, systemd services, package installation, and dashboard accounts. Prefer installing only on a dedicated host or VM, verify the upstream GitHub tag yourself, back up nginx configuration first, and avoid passing or sharing passwords through chat or command-line arguments.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (3)

T03 · Remote Payload Retrieval and Execution

Error
Location
scripts/install.sh:35
Finding
Unverified Remote Application Retrieval and Dependency Execution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.sh:35-64`, `scripts/install.sh:76-87`, `scripts/install.sh:155-174` **Vulnerability Type**: Remote payload retrieval and unsafe software supply chain **Risk Level**: High ### Vulnerable Code ```bash if [ ! -d "$INSTALL_DIR/api" ] || [ ! -d "$INSTALL_DIR/web" ]; then log "Source directories missing. Cloning $REPO_URL @ $RELEASE_TAG ..." TEMP_CLONE=$(mktemp -d) git clone --depth 1 --branch "$RELEASE_TAG" "$REPO_URL" "$TEMP_CLONE" || err "Failed to clone webclaw repo from $REPO_URL (tag: $RELEASE_TAG)" # Copy source into install dir, preserving any existing files (SKILL.md, scripts/) rsync -a --ignore-existing "$TEMP_CLONE/" "$INSTALL_DIR/" --exclude='.git/' rm -rf "$TEMP_CLONE" log "Source cloned into $INSTALL_DIR (tag: $RELEASE_TAG)" fi log "Setting up Python backend..." if [ ! -d "$INSTALL_DIR/.venv" ]; then python3 -m venv "$INSTALL_DIR/.venv" fi "$INSTALL_DIR/.venv/bin/pip" install --quiet --upgrade pip "$INSTALL_DIR/.venv/bin/pip" install --quiet -r "$INSTALL_DIR/api/requirements.txt" log "Backend ready." log "Building frontend..." cd "$INSTALL_DIR/web" npm install --silent 2>/dev/null || npm install npm run build ``` The downloaded Python implementation is subsequently imported and executed: ```bash "$INSTALL_DIR/.venv/bin/python3" -c " import sys, os, sqlite3 sys.path.insert(0, '$INSTALL_DIR/api') os.environ['WEBCLAW_DB_PATH'] = '$DB_PATH' from db import get_connection conn = get_connection('$DB_PATH') tables = conn.execute(\"SELECT COUNT(*) FROM sqlite_master WHERE type='table'\").fetchone()[0] if tables < 5: print(f'ERROR: Only {tables} tables created, expected 9+', file=sys.stderr) sys.exit(1) conn.close() print(f'Database initialized at $DB_PATH ({tables} tables)') " ``` Downloaded service templates are also installed persistently: ```bash TEMP_SVC=$(mktemp) sed -e "s|{{INSTALL_DIR}}|$INSTALL_DIR|g" \ -e "s|{{USER}}|$CURRENT_USE ...[truncated 3403 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Include the complete `api/`, `web/`, `templates/`, dependency manifests, and lockfiles in the reviewed Skill package. 2. If remote retrieval is unavoidable, pin the source to an immutable Git commit hash rather than relying only on a tag. 3. Publish a signed release manifest containing SHA-256 or stronger hashes for every downloaded artifact, and verify it before copying or executing any content. 4. Require cryptographic signature verification using a pinned and documented maintainer key. 5. Use fully locked Python dependencies with hashes, such as `pip install --require-hashes -r requirements.lock`. 6. Commit the npm lockfile and use `npm ci` rather than `npm install`. 7. Disable npm lifecycle scripts where they are unnecessary. If lifecycle scripts are required, audit and explicitly allow them. 8. Validate generated systemd units against a locally packaged policy before privileged installation. 9. Apply systemd hardening such as `NoNewPrivileges=true`, `PrivateTmp=true`, `ProtectSystem=strict`, `ProtectHome=true`, restricted writable paths, and explicit network restrictions where compatible. 10. Display the exact source commit and verified artifact digest to the administrator before installation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/db_query.py:369
Finding
Plaintext Passwords Exposed Through Process Arguments and Action Output<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:98-104`, `scripts/db_query.py:306-366`, `scripts/db_query.py:369-401`, `scripts/db_query.py:553-562` **Vulnerability Type**: Plaintext sensitive-data exposure **Risk Level**: Medium ### Vulnerable Code The documented interface encourages supplying a password as a command-line argument: ```markdown ### Reset a Password ``` ```text Reset the web password for alice@company.com → runs: reset-password --email alice@company.com Set a specific password for alice → runs: reset-password --email alice@company.com --password MyNewPass123! ``` Generated credentials are returned as ordinary action output: ```python def action_create_user(args): """Create a web dashboard user account.""" email = args.email if not email: _fail("--email is required") full_name = args.full_name or email.split("@")[0] role_name = args.role or "Accounts User" # Generate a secure temporary password temp_password = secrets.token_urlsafe(12) pw_hash = _hash_password(temp_password) conn = _get_conn() # Check if email already exists import uuid wu = Table("webclaw_user") q = Q.from_(wu).select(wu.id).where(wu.email == P()) existing = conn.execute(q.get_sql(), (email,)).fetchone() if existing: _fail(f"User with email {email} already exists") user_id = str(uuid.uuid4()) username = email.split("@")[0] try: q = ( Q.into(wu) .columns("id", "username", "email", "full_name", "password_hash", "status") .insert(P(), P(), P(), P(), P(), P()) ) conn.execute(q.get_sql(), (user_id, username, email, full_name, pw_hash, "active")) # Find or create role wr = Table("webclaw_role") q = Q.from_(wr).select(wr.id).where(wr.name == P()) role = conn.execute(q.get_sql(), (role_name,)).fetchone() if not role: role_id = str(uuid.uuid4()) q = ( ...[truncated 4939 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--password` command-line option for plaintext secrets. 2. Read explicit passwords from protected standard input, an interactive no-echo prompt such as `getpass`, or a dedicated file descriptor. 3. Do not place passwords in environment variables because they may also be exposed by process inspection or diagnostic tooling. 4. Replace returned temporary passwords with single-use, short-lived account activation or password-reset tokens. 5. Store reset tokens only as hashes, bind them to a specific user and purpose, enforce a short expiration, and invalidate them after one use. 6. Mark newly created users as requiring password setup before ordinary authentication. 7. If a plaintext temporary password must be supported, display it only in an authenticated, non-logged administrative interface and ensure it cannot be retrieved again. 8. Redact fields named `password`, `temporary_password`, and similar values from OpenClaw logs, telemetry, transcripts, and messaging integrations. 9. Document that credentials must not be entered into shell commands or shared through persistent messaging channels. 10. Preserve the existing session invalidation behavior after password changes. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/install.sh:131
Finding
Installer Deletes the Host’s Existing Default Nginx Site<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.sh:131-147` **Vulnerability Type**: Excessive privileged modification of global web-server configuration **Risk Level**: Medium ### Vulnerable Code ```bash TEMP_CONF=$(mktemp) sed -e "s|{{DOMAIN}}|$DOMAIN|g" \ -e "s|{{SSL_CERT}}|$CERT_DIR/cert.pem|g" \ -e "s|{{SSL_KEY}}|$CERT_DIR/key.pem|g" \ "$NGINX_CONF" > "$TEMP_CONF" sudo cp "$TEMP_CONF" /etc/nginx/sites-enabled/webclaw rm -f "$TEMP_CONF" sudo rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true if sudo nginx -t 2>/dev/null; then sudo systemctl reload nginx log "Nginx configured (HTTPS on port 443, self-signed cert)." else err "Nginx config test failed. Check /etc/nginx/sites-enabled/webclaw" fi ``` ### Technical Analysis Configuring an nginx virtual host is consistent with the declared dashboard functionality. However, deleting `/etc/nginx/sites-enabled/default` is a global, privileged operation that is not inherently required to add a new Webclaw virtual host. The installer removes the existing site without: - Verifying whether it belongs to Webclaw. - Checking whether another application depends on it. - Requesting explicit administrator approval. - Creating a backup. - Recording enough state for rollback. - Restoring the deleted site if the new configuration causes operational problems. Although `nginx -t` checks configuration syntax, it does not verify that unrelated applications remain reachable or that virtual-host routing still behaves as intended. ### Attack Path 1. A user installs Webclaw on a server that already uses nginx. 2. Another application or the host’s default routing depends on `/etc/nginx/sites-enabled/default`. 3. The installer copies the Webclaw configuration and deletes the existing default site using `sudo`. 4. The installer validates syntax and reloads nginx. 5. Requests formerly handled by the deleted site fail or are routed to a different server block. 6. Existing applica ...[truncated 901 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not delete `/etc/nginx/sites-enabled/default` automatically. 2. Install Webclaw as a separate named virtual host and preserve all unrelated server blocks. 3. Detect port, hostname, and default-server conflicts before making changes. 4. If removal or modification of an existing site is necessary, show the exact path and require explicit administrator confirmation. 5. Back up every modified or removed configuration file with ownership, permissions, and symlink state preserved. 6. Test the complete resulting nginx configuration before applying changes. 7. If reload or post-install health checks fail, automatically restore the previous configuration and reload nginx again. 8. Provide a documented uninstall command that removes only Webclaw-owned files and restores any configuration changed during installation. 9. Consider installing configuration into `sites-available` and creating a dedicated symlink in `sites-enabled`, following the host distribution’s nginx conventions. 10. Record an installation manifest identifying only the files created or modified by Webclaw. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (40)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
The database is auto-created on first API request at `~/.openclaw/webclaw/webclaw.sqlite`. To reset it:

```bash
rm -f ~/.openclaw/webclaw/webclaw.sqlite
# Restart the API server — tables will be recreated
```
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Credential Access

High
Category
Privilege Escalation
Content
## Security Model

- **HTTPS enforced** via Let's Encrypt (setup-ssl action)
- **JWT authentication** — access tokens (15 min) + refresh tokens (7 days, httpOnly cookies)
- **RBAC** — role-based permission checks before every skill action
- **Rate limiting** — 5/min auth, 30/min writes, 100/min general (nginx)
- **Audit logging** — all mutating actions logged to audit_log table
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Context-Inappropriate Capability

High
Confidence
95% confidence
Finding
The script can obtain certificates, overwrite nginx configuration, reload nginx, renew certs, and restart system services via sudo. Even without classic injection flaws, this creates a powerful remote host-management capability that can disrupt services, alter network exposure, or be abused for privilege escalation depending on sudo policy and how the script is invoked.

Chaining Abuse

High
Category
Tool Misuse
Content
EXISTING_CONF="/etc/nginx/sites-enabled/webclaw"
fi

if [ -n "$EXISTING_CONF" ] && sudo nginx -t 2>/dev/null; then
    sudo systemctl reload nginx
    log "Nginx: reusing existing config at $EXISTING_CONF"
else
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"$NGINX_CONF" > "$TEMP_CONF"
    sudo cp "$TEMP_CONF" /etc/nginx/sites-enabled/webclaw
    rm -f "$TEMP_CONF"
    sudo rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true

    if sudo nginx -t 2>/dev/null; then
        sudo systemctl reload nginx
Confidence
98% confidence
Finding
The installer deletes the default nginx site file unconditionally using sudo. This can break unrelated web hosting on the machine and alters existing system configuration in a destructive way that is disproportionate for a skill installer.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises executable installation hooks, shell scripts, package installation, network access, and sudo-required system changes, but it does not declare an explicit tool scope such as permissions or allowed-tools. That creates a confused-deputy risk where an agent may invoke powerful capabilities without clear least-privilege boundaries or user-visible approval expectations.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The activation triggers include broad phrases like login page, users, roles, browser access, and setup web, which are likely to overlap with ordinary admin conversations. In an agentic environment this can cause unintended routing into a high-privilege infrastructure skill, increasing the chance of accidental user management, SSL, or service operations.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The password reset workflow allows setting a specific password and mentions sharing temporary passwords, but it does not require a warning about sensitive credential handling, identity verification, or account disruption. This can normalize insecure transmission of secrets through chat logs and enable accidental or unauthorized account takeover if invoked on the wrong user.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
This script exposes administrative actions through a Telegram-invoked management interface, including user lifecycle and service operations, which materially exceeds the stated browser-dashboard scope. Expanding a skill's control plane into a chat-triggered script increases attack surface and raises the risk of unauthorized remote administration if the surrounding invocation path is weakly authenticated or audited.

Session Persistence

Medium
Category
Rogue Agent
Content
setup-ssl        — Configure HTTPS with Let's Encrypt (--domain required)
  renew-ssl        — Check and renew SSL certificate
  list-users       — List web dashboard user accounts
  create-user      — Create a user (--email, --full-name, --role)
  reset-password   — Generate new password for a user (--email)
  disable-user     — Disable a user account (--email)
  list-sessions    — Show active sessions
Confidence
76% confidence
Finding
The script manages user accounts and sessions, including listing active sessions and clearing them, which indicates persistent authentication state and account administration accessible from this management channel. In this context, the danger comes from exposing identity and session-management operations through an auxiliary interface that may bypass the dashboard's normal controls.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""Run a command and return (stdout, returncode). cmd is a list of args."""
    if isinstance(cmd, str):
        cmd = cmd.split()
    result = subprocess.run(cmd, capture_output=True, text=True)
    if check and result.returncode != 0:
        _fail(f"Command failed: {' '.join(cmd)}\n{result.stderr[:500]}")
    return result.stdout.strip(), result.returncode
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# Check certbot is available
    if not shutil.which("certbot"):
        _fail("certbot not found. Install with: sudo apt install certbot python3-certbot-nginx")

    # Obtain certificate
    email_addr = args.email or f"admin@{domain}"
Confidence
93% confidence
Finding
This code path relies on sudo-enabled execution for certificate issuance and related system management tasks. In the context of a remotely invokable skill, root-adjacent capabilities are highly dangerous because compromise of the caller path or business-logic abuse can translate into host-level control or service disruption.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# Webclaw post-install script.
# Sets up backend (venv), frontend (npm build), database, nginx, and systemd.
#
# Privileges required: sudo (for nginx config, systemd services, certbot)
# What this script does:
#   1. Clones full source from GitHub if api/web dirs are missing
#   2. Creates Python venv + installs pip dependencies
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
# Webclaw post-install script.
# Sets up backend (venv), frontend (npm build), database, nginx, and systemd.
#
# Privileges required: sudo (for nginx config, systemd services, certbot)
# What this script does:
#   1. Clones full source from GitHub if api/web dirs are missing
#   2. Creates Python venv + installs pip dependencies
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
# Webclaw post-install script.
# Sets up backend (venv), frontend (npm build), database, nginx, and systemd.
#
# Privileges required: sudo (for nginx config, systemd services, certbot)
# What this script does:
#   1. Clones full source from GitHub if api/web dirs are missing
#   2. Creates Python venv + installs pip dependencies
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
# Webclaw post-install script.
# Sets up backend (venv), frontend (npm build), database, nginx, and systemd.
#
# Privileges required: sudo (for nginx config, systemd services, certbot)
# What this script does:
#   1. Clones full source from GitHub if api/web dirs are missing
#   2. Creates Python venv + installs pip dependencies
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
# Webclaw post-install script.
# Sets up backend (venv), frontend (npm build), database, nginx, and systemd.
#
# Privileges required: sudo (for nginx config, systemd services, certbot)
# What this script does:
#   1. Clones full source from GitHub if api/web dirs are missing
#   2. Creates Python venv + installs pip dependencies
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
# Webclaw post-install script.
# Sets up backend (venv), frontend (npm build), database, nginx, and systemd.
#
# Privileges required: sudo (for nginx config, systemd services, certbot)
# What this script does:
#   1. Clones full source from GitHub if api/web dirs are missing
#   2. Creates Python venv + installs pip dependencies
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
# Webclaw post-install script.
# Sets up backend (venv), frontend (npm build), database, nginx, and systemd.
#
# Privileges required: sudo (for nginx config, systemd services, certbot)
# What this script does:
#   1. Clones full source from GitHub if api/web dirs are missing
#   2. Creates Python venv + installs pip dependencies
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
# Webclaw post-install script.
# Sets up backend (venv), frontend (npm build), database, nginx, and systemd.
#
# Privileges required: sudo (for nginx config, systemd services, certbot)
# What this script does:
#   1. Clones full source from GitHub if api/web dirs are missing
#   2. Creates Python venv + installs pip dependencies
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Static analysis

No suspicious patterns detected.