Back to skill

Security audit

Iblai Openclaw Router

Security checks for vulnerabilities and agentic risk

Overview

The router appears to do its stated job, but its installer creates a persistent root-level service, handles API keys insecurely, and can expose prompt data through logs or configurable forwarding.

Review this carefully before installing. Prefer a foreground or user-level service, do not let an agent run the installer unattended, avoid storing provider keys in systemd unit files, disable request-content logging, and only use trusted HTTPS provider endpoints with explicit credentials for that provider.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/install.sh:9
Finding
Root System Service Executes User-Writable JavaScript<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.sh:9-10, 37-62` **Vulnerability Type**: Privilege escalation through a root service executing user-controlled code; persistent system service **Risk Level**: Critical ### Vulnerable Code ```bash ROUTER_DIR="$HOME/.openclaw/workspace/router" SERVICE_NAME="iblai-router" mkdir -p "$ROUTER_DIR" cp "$SKILL_DIR/server.js" "$ROUTER_DIR/server.js" NODE_BIN=$(which node) sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null << EOF [Unit] Description=iblai-router - Cost-optimizing Claude model routing After=network.target [Service] Type=simple ExecStart=$NODE_BIN $ROUTER_DIR/server.js Environment=ANTHROPIC_API_KEY=$API_KEY Environment=ROUTER_CONFIG=$ROUTER_DIR/config.json Environment=ROUTER_PORT=$PORT Environment=ROUTER_LOG=1 Restart=always RestartSec=3 [Install] WantedBy=multi-user.target EOF sudo systemctl daemon-reload sudo systemctl enable --now "$SERVICE_NAME" ``` ### Technical Analysis The installer creates a system-wide systemd unit but does not define a `User=` or `Group=` directive. System services run as root by default. The service therefore launches Node.js with root privileges. The executed `server.js` file is stored under the installing user's home directory at `~/.openclaw/workspace/router/server.js`. That location remains writable by the unprivileged user. This violates a fundamental privilege-boundary requirement: a privileged service must not execute code that a less-privileged user can modify. The router only needs to listen on `127.0.0.1:8402`, an unprivileged port, read its configuration, and make outbound HTTPS requests. Root access is not necessary for its declared functionality. Although persistent execution is relevant to operating a proxy, system-wide root persistence exceeds the minimum privileges required. ### Attack Path 1. A user runs `scripts/install.sh`, which copies `server.js` into the user's writable home directory. 2. The installer registers and ...[truncated 983 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Run the router as a dedicated unprivileged account using explicit `User=` and `Group=` directives. - Prefer a user-level systemd service under `~/.config/systemd/user/`, because binding to port 8402 does not require root. - If a system service is required, install executable code in a root-owned location such as `/opt/iblai-router/` or `/usr/local/lib/iblai-router/`. - Ensure the service account and ordinary users cannot modify the executable. - Add systemd hardening controls, including: - `NoNewPrivileges=true` - `ProtectSystem=strict` - `ProtectHome=true` - `PrivateTmp=true` - `PrivateDevices=true` - `RestrictSUIDSGID=true` - `RestrictAddressFamilies=AF_INET AF_INET6` - `CapabilityBoundingSet=` - Permit writes only to narrowly scoped paths if runtime writes are required. - Resolve and validate the Node.js executable using `command -v node`, and ensure it is root-owned before placing it in a privileged service definition. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/install.sh:23
Finding
API Credential Is Automatically Extracted and Stored in a Plaintext Systemd Unit<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.sh:23-28, 37-50`; equivalent manual setup is documented in `README.md:135-152` **Vulnerability Type**: Insecure secret extraction and plaintext credential storage **Risk Level**: High ### Vulnerable Code ```bash API_KEY="" AUTH_FILE="$HOME/.openclaw/agents/main/agent/auth-profiles.json" if [ -f "$AUTH_FILE" ]; then API_KEY=$(grep -o '"key": "[^"]*"' "$AUTH_FILE" 2>/dev/null | head -1 | cut -d'"' -f4 || true) fi if [ -z "$API_KEY" ]; then echo "" echo " ⚠ Could not auto-detect Anthropic API key." echo " Edit /etc/systemd/system/$SERVICE_NAME.service and set ANTHROPIC_API_KEY manually." API_KEY="sk-ant-YOUR-KEY-HERE" fi NODE_BIN=$(which node) sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null << EOF [Unit] Description=iblai-router - Cost-optimizing Claude model routing After=network.target [Service] Type=simple ExecStart=$NODE_BIN $ROUTER_DIR/server.js Environment=ANTHROPIC_API_KEY=$API_KEY Environment=ROUTER_CONFIG=$ROUTER_DIR/config.json Environment=ROUTER_PORT=$PORT Environment=ROUTER_LOG=1 Restart=always RestartSec=3 ``` ### Technical Analysis The installer parses `auth-profiles.json` with a regular expression, takes the first object property named `key`, and assumes it is the appropriate Anthropic credential. It does not parse the JSON structurally, select an explicitly named provider profile, or verify the key's intended destination. The extracted secret is interpolated directly into `/etc/systemd/system/iblai-router.service` as an `Environment=` value. This creates a persistent plaintext copy of the API key outside the original credential store. Systemd unit files are configuration files rather than secret stores and may be exposed through configuration backups, support bundles, diagnostic commands, or overly broad file permissions. Special characters in the extracted value could also produce an invalid or unexpectedly parsed environment assignment becaus ...[truncated 1063 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not automatically scrape credentials from `auth-profiles.json`. - Require the user to select and authorize a specific provider credential explicitly. - Use a proper JSON parser and validate the selected credential's provider and expected format. - Store the credential using systemd credentials, an operating-system secret store, or a dedicated root-owned environment file with mode `0600`. - Avoid placing secrets directly in systemd unit files or command-line arguments. - Bind each credential to an exact approved provider destination rather than reusing one variable for arbitrary upstream hosts. - Apply correct systemd escaping if an environment file must be generated. - Document credential rotation and immediately rotate any key previously written to broadly accessible files. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
server.js:301
Finding
User Prompt Contents Are Written to the System Journal by Default<![CDATA[ ## Vulnerability Details **File Location**: `server.js:52, 301-307`; logging is explicitly enabled by `scripts/install.sh:52` **Vulnerability Type**: Sensitive information exposure through persistent logs **Risk Level**: Medium ### Vulnerable Code ```javascript const LOG_ROUTING = process.env.ROUTER_LOG !== "0"; ``` ```javascript if (LOG_ROUTING) { const savings = opusCost > 0 ? ((opusCost - (estimatedTokens / 1_000_000) * cost.input) / opusCost * 100).toFixed(0) : 0; console.log( `[router] ${decision.tier.padEnd(6)} → ${decision.model} ` + `| score=${decision.score.toFixed(3)} conf=${decision.confidence.toFixed(2)} ` + `| ${decision.reasoning} | -${savings}% | ${text.slice(0, 80).replace(/\n/g, " ")}...` ); } ``` The installer enables this behavior: ```bash Environment=ROUTER_LOG=1 ``` ### Technical Analysis The router extracts recent user-message text and writes the first 80 characters to standard output for every routed request. Under systemd, standard output is normally captured by journald. User prompts can contain passwords, API keys, personal information, customer data, confidential code, internal hostnames, incident details, or regulated records. Truncating the content to 80 characters does not make it safe; many secrets are shorter than that and often appear at the start of a message. Prompt content is not necessary to calculate routing statistics. The service can log tier, selected model, score, confidence, and estimated token count without retaining raw user text. ### Attack Path 1. A user or automated OpenClaw workload sends a request containing sensitive data. 2. `extractText()` includes that data in the text used for classification. 3. With `ROUTER_LOG=1`, the router writes the first 80 characters to standard output. 4. Systemd stores the output in the journal. 5. A user with journal access, a log-forwarding system, a support bundle, or an attacker who later gains log access retrieves the prompt fragme ...[truncated 464 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Disable prompt-content logging by default. - Log only non-sensitive metadata such as the selected tier, model, score, confidence, request identifier, and estimated token count. - Require explicit user opt-in before recording any request content. - If diagnostic content logging is indispensable, redact common credential formats, authorization headers, personal data, and configurable sensitive patterns. - Apply strict journal access controls and short retention periods. - Clearly document that enabling content logging can expose prompts to journald and downstream log collectors. - Consider structured logs with a fixed schema that does not include raw message text. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
server.js:216
Finding
Configurable Upstream Allows API Keys and Prompt Data to Be Sent to Arbitrary or Plaintext Destinations<![CDATA[ ## Vulnerability Details **File Location**: `server.js:216-243` **Vulnerability Type**: Unrestricted sensitive-data forwarding and use of unencrypted HTTP **Risk Level**: Medium ### Vulnerable Code ```javascript // Support custom API base URL from config (e.g. OpenRouter) const baseUrl = config.apiBaseUrl || "https://api.anthropic.com"; const parsed = new URL(baseUrl); const options = { hostname: parsed.hostname, port: parsed.port || (parsed.protocol === "https:" ? 443 : 80), path: (parsed.pathname.replace(/\/$/, "") || "") + "/v1/messages", method: "POST", headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload), }, }; // Use appropriate auth header based on target const isOpenRouter = parsed.hostname.includes("openrouter"); if (isOpenRouter) { options.headers["Authorization"] = `Bearer ${ANTHROPIC_API_KEY}`; options.headers["HTTP-Referer"] = config.openRouterReferer || "https://github.com/iblai/iblai-openclaw-router"; } else { options.headers["x-api-key"] = ANTHROPIC_API_KEY; options.headers["anthropic-version"] = req.headers["anthropic-version"] || "2023-06-01"; if (req.headers["anthropic-beta"]) { options.headers["anthropic-beta"] = req.headers["anthropic-beta"]; } } const transport = parsed.protocol === "https:" ? https : http; const upstream = transport.request(options, (upstreamRes) => { res.writeHead(upstreamRes.statusCode, upstreamRes.headers); upstreamRes.pipe(res); }); ``` ### Technical Analysis The hot-reloaded `apiBaseUrl` configuration accepts an arbitrary URL. The code does not require HTTPS, restrict the destination to approved provider hosts, or bind credentials to a specific endpoint. For a hostname containing the substring `openrouter`, the key is sent in an `Authorization` header. For every other hostname, it is sent as `x-api-key`. In both cases, the complete serialized LLM request is also forwarded. The protocol selection explicitly falls back to N ...[truncated 1644 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Permit only `https:` upstream URLs and reject HTTP and all unsupported schemes. - Use an exact hostname allowlist for supported providers, such as `api.anthropic.com` and the documented OpenRouter API hostname. - Compare normalized hostnames exactly; do not use substring matching such as `includes("openrouter")`. - Require explicit, security-prominent confirmation before enabling a custom upstream. - Store separate credentials for each provider and bind each credential to its approved hostname. - Do not send an Anthropic credential to a non-Anthropic endpoint. - Validate the URL after parsing, including protocol, hostname, port, credentials, and path. - Protect the hot-reloaded configuration from unauthorized modification and verify its ownership and permissions before loading it. - Consider certificate or public-key pinning where the deployment threat model warrants it. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (42)

External Script Fetching

High
Category
Supply Chain
Content
Or from the command line:

```bash
curl -s http://127.0.0.1:8402/stats | python3 -m json.tool
```

## How It Works
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill claims to provide routing functionality, but its documented lifecycle includes service management, config mutation, cache/model metadata changes, and destructive uninstall behavior. Even if intended as normal setup/cleanup, bundling operational and destructive actions under a simple routing skill increases the chance of users executing commands that alter or remove system state without understanding the consequences.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill claims to provide routing functionality, but its documented lifecycle includes service management, config mutation, cache/model metadata changes, and destructive uninstall behavior. Even if intended as normal setup/cleanup, bundling operational and destructive actions under a simple routing skill increases the chance of users executing commands that alter or remove system state without understanding the consequences.

Ae1

High
Category
analysis-evasion
Content
1. Copy `server.js` and `config.json` to `~/.openclaw/workspace/router/`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# 2. Remove service file
if [ -f "/etc/systemd/system/$SERVICE_NAME.service" ]; then
  sudo rm "/etc/systemd/system/$SERVICE_NAME.service"
  sudo systemctl daemon-reload
  echo "  ✓ Systemd unit removed"
fi
Confidence
95% 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).

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README encourages users to ask an agent to clone a repository and run install/uninstall scripts automatically, which delegates code execution and system modification to unreviewed remote content. In an agent-driven environment, this materially increases the risk of arbitrary code execution, persistence, or unsafe configuration changes without explicit user review.

Session Persistence

Medium
Category
Rogue Agent
Content
cd ~/.openclaw/workspace
git clone https://github.com/iblai/iblai-openclaw-router.git router

# 2. Create the systemd service
sudo tee /etc/systemd/system/iblai-router.service > /dev/null << EOF
[Unit]
Description=iblai-router - Claude model routing
Confidence
92% confidence
Finding
The manual setup instructs users to create a systemd service, which is a persistence mechanism. Persistence is expected for a router, but when combined with unreviewed repository code and automated installation guidance, it increases the severity because the software will run continuously and at boot.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The manual setup embeds a shell command that extracts an API key from a local auth file and injects it into a systemd Environment line. This normalizes credential scraping from local agent state and risks exposing secrets via process metadata, service definitions, shell history, or accidental disclosure to anyone who can read the unit file.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
git clone https://github.com/iblai/iblai-openclaw-router.git router

# 2. Create the systemd service
sudo tee /etc/systemd/system/iblai-router.service > /dev/null << EOF
[Unit]
Description=iblai-router - Claude model routing
After=network.target
Confidence
90% confidence
Finding
The instructions create a root-owned systemd service via sudo, giving the installation persistence and elevated control over service management. In combination with repository cloning and script execution, this increases the blast radius of any compromised or unsafe code path because it becomes easier to run or maintain attacker-controlled code on boot.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
EOF

# 3. Start the router
sudo systemctl daemon-reload
sudo systemctl enable --now iblai-router

# 4. Verify it's running
Confidence
88% confidence
Finding
Reloading and enabling a service with sudo is legitimate administration, but in this installation context it operationalizes a newly cloned codebase as a managed service. If the repository or prior steps are untrusted, this gives that code privileged lifecycle control and persistence.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# 3. Start the router
sudo systemctl daemon-reload
sudo systemctl enable --now iblai-router

# 4. Verify it's running
curl -s http://127.0.0.1:8402/health | jq .
Confidence
88% confidence
Finding
Enabling the service to start immediately under systemd is not inherently malicious, but it does turn the router into an active background component with administrative control. In an agent-install workflow, automatic privileged service activation raises the risk of unnoticed long-lived execution.

Session Persistence

Medium
Category
Rogue Agent
Content
# 3. Start the router
sudo systemctl daemon-reload
sudo systemctl enable --now iblai-router

# 4. Verify it's running
curl -s http://127.0.0.1:8402/health | jq .
Confidence
93% confidence
Finding
`systemctl enable --now` establishes automatic startup persistence for the router. In the context of a newly cloned and agent-installable codebase, persistent background execution materially raises risk because any malicious or compromised code would survive reboots and continue receiving request traffic.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# Then /restart from your session

# Option 3: Restart the systemd service directly
sudo systemctl restart openclaw
```

### Verify the model is available
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
# Then /restart from your session

# Option 3: Restart the systemd service directly
sudo systemctl restart openclaw
```

### Verify the model is available
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
# Then /restart from your session

# Option 3: Restart the systemd service directly
sudo systemctl restart openclaw
```

### Verify the model is available
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
# Then /restart from your session

# Option 3: Restart the systemd service directly
sudo systemctl restart openclaw
```

### Verify the model is available
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
# Then /restart from your session

# Option 3: Restart the systemd service directly
sudo systemctl restart openclaw
```

### Verify the model is available
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
# Then /restart from your session

# Option 3: Restart the systemd service directly
sudo systemctl restart openclaw
```

### Verify the model is available
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
# Then /restart from your session

# Option 3: Restart the systemd service directly
sudo systemctl restart openclaw
```

### Verify the model is available
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

External Transmission

Medium
Category
Data Exfiltration
Content
curl -s http://127.0.0.1:8402/health | jq .

# 2. Test a request through the router
curl -s http://127.0.0.1:8402/v1/messages \
  -H "Content-Type: application/json" \
  -H "x-api-key: test" \
  -H "anthropic-version: 2023-06-01" \
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Session Persistence

Medium
Category
Rogue Agent
Content
bash scripts/install.sh
```

The install script is idempotent — it will update the systemd service, restart the router, and re-register the model provider if needed.

### What gets updated
Confidence
90% confidence
Finding
The README states the install script will update the systemd service, restart the router, and re-register the provider if needed, indicating automation that can maintain or re-establish persistent execution. For an agent-operated install path, this reduces visibility into ongoing system changes and can perpetuate unsafe configurations after updates.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# Check the service is actually listening
curl -s http://127.0.0.1:8402/health

# Check logs for errors (wrong API key, network issues)
journalctl -u iblai-router -n 20
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
journalctl -u iblai-router -n 20

# Verify your Anthropic API key works
curl -s https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises shell-based installation and file/system modifications but does not declare any tool scope or permissions boundaries. In a skill ecosystem, undeclared capabilities are dangerous because users and orchestration layers cannot accurately assess that the skill can execute shell commands, write files, and access environment/auth material before invocation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The install section tells the user to run a shell script immediately, while the material warning about side effects is incomplete relative to the actual consequences identified by analysis. Omission of prominent warnings is risky because users may execute the installer without realizing it creates persistence, writes files into their workspace, modifies configuration, and may require elevated privileges.

Static analysis

No suspicious patterns detected.