Back to skill

Security audit

iOS Setup

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate iOS setup purpose, but it exposes a reusable gateway token in agent output and sets up network access and persistence with insufficient safeguards.

Review carefully before installing. Only use this skill if you are comfortable exposing OpenClaw to your phone and handling the gateway token as a secret. Avoid posting the token in chat logs or summaries, prefer a private network such as Tailscale, and do not add the reboot cron entry unless you understand that a workspace script will run automatically after reboot.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/detect_env.py:31
Finding

Gateway Authentication Token Exposed in Plaintext Output

Content
View full analysis
/dev/null") if result and result != "null": return result.strip('"') except Exception: pass # Fallback: read from config file config_path = os.path.expanduser("~/.openclaw/openclaw.json") if os.path.exists(config_path): try: import re with open(config_path) as f: content = f.read() # Simple regex for token field (JSON5 may have comments) m = re.search(r'"token"\s*:\s*"([^"]+)"', content) if m: return m.group(1) except Exception: pass return None ``` ```python result = { "install_type": install_type, "workspace": get_workspace(), "gateway_token": get_gateway_token(), "gateway_port": get_gateway_port(), "stats_running": stats_running, "stats_port": 8765, "public_ip": get_public_ip(), "lan_ip": get_lan_ip(), "tailscale_ip": tailscale_ip, "tailscale_hostname": tailscale_hostname, "nginx_available": nginx_available(), "tailscale_available": tailscale_available(), "os": detect_os(), } print(json.dumps(result, indent=2)) ``` The associated Skill instructions also require the token to be placed in the connection summary: ```text ✅ OpenClaw iOS Setup Complete Gateway URL: https://your-domain-or-ip Token: your-token-here ``` ### Technical Analysis The environment detector retrieves a reusable gateway authentication token from either the OpenClaw CLI or the local configuration file. It then includes the complete token in JSON written to standard output. Tool output may be re ...[truncated 1736 chars]
Remediation
View remediation

T06 · System Persistence

Warning
Location
SKILL.md:67
Finding

User-Directed Reboot Persistence Executes a Mutable Workspace Script

Content
View full analysis
/scripts/dashboard/ensure_stats_server.sh" # Tell user to run: crontab -e # Add this line (substituting the actual path): @reboot bash /scripts/dashboard/ensure_stats_server.sh ``` The Skill also recommends container-level persistence: ```text Docker install — tell the user to add `restart: unless-stopped` to their `docker-compose.yml` for the OpenClaw service. ``` ### Technical Analysis The direct-install workflow directs the user to create an `@reboot` cron entry that executes `ensure_stats_server.sh` from the OpenClaw workspace. This creates cross-session persistence: the referenced script is executed after subsequent machine reboots with the privileges of the crontab owner. Availability after reboot is useful, but it is not required to complete the initial iOS setup. The persistence step therefore exceeds the minimum behavior necessary for immediate connectivity. More importantly, the execution target resides in a mutable workspace and no ownership check, permission validation, content pinning, or integrity verification is specified. The Skill does not silently edit the crontab, and explicit user involvement reduces the risk. Nevertheless, once configured, later replacement or modification of the workspace script changes the code that will execute at reboot without requiring another persistence-registration action. Docker's `restart: unless-stopped` is a conventional availability mechanism and is less concerning when applied to a trusted, pinned container. Its security depends on the integrity of the image, compose configuration, mounted files, and container entrypo ...[truncated 1088 chars]
Remediation
View remediation

other

Warning
Location
scripts/detect_env.py:60
Finding

Unconditional External Public-IP Discovery and Excessive Environment Enumeration

Content
View full analysis
/dev/null | awk '{print $1}'") or None ``` ```python result = { "install_type": install_type, "workspace": get_workspace(), "gateway_token": get_gateway_token(), "gateway_port": get_gateway_port(), "stats_running": stats_running, "stats_port": 8765, "public_ip": get_public_ip(), "lan_ip": get_lan_ip(), "tailscale_ip": tailscale_ip, "tailscale_hostname": tailscale_hostname, "nginx_available": nginx_available(), "tailscale_available": tailscale_available(), "os": detect_os(), } print(json.dumps(result, indent=2)) ``` ### Technical Analysis The detector unconditionally contacts `https://api.ipify.org`, causing a third-party service to observe the host's public source IP and request timing. It also initiates a UDP route probe toward `8.8.8.8` to determine the selected local address. At the same time, the script enumerates and prints installation type, workspace path, process state, gateway and stats ports, public and LAN addresses, Tailscale addressing and hostname, installed networking tools, and operating-system family. Some network discovery is relevant to iOS connectivity setup. However, the Skill offers domain, Tailscale, LAN, and self-managed modes only after detection. Public-IP discovery is unnecessary for several of those choices, an ...[truncated 1662 chars]
Remediation
View remediation
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 (23)

Chaining Abuse

High
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Content

Scanner excerpt · SKILL.md (reported line 140)May include surrounding context.

}

text

Then run: `sudo nginx -t && sudo systemctl reload nginx`

**Connection details for iOS app:**
- Gateway URL: `https://YOUR_DOMAIN`

Ssd 3

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill explicitly instructs the agent to reveal the gateway authentication token in responses. Echoing secrets into chat output increases the risk of credential exposure through logs, transcripts, screenshots, integrations, or other observers, and directly enables unauthorized access to the OpenClaw gateway if the token is reused.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The manual setup path tells the agent to output raw sensitive connection details including the full token. This creates the same secret-spillage risk as above, with added danger because the section is framed as a generic handoff for self-managed exposure and may be copied into insecure notes or support channels.

Content

No source excerpt is available for this finding.

Ssd 3

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

Requiring a final completion block that echoes the token guarantees secret disclosure whenever the skill succeeds. Because summaries are especially likely to be logged, shared, or reviewed later, this materially raises the probability of credential compromise.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
88% confidence
Finding

Copying the full process environment into child processes can leak unrelated secrets such as tokens, credentials, and proxy settings to external commands. In this deployment skill, subprocesses include shell commands and helper scripts, so inherited secrets may be exposed to less-trusted tooling or logs if those subprocesses are modified or compromised.

Content

Scanner excerpt · scripts/deploy_stats.py (reported line 17)May include surrounding context.

python
def run(cmd, timeout=30, env=None):
    e = os.environ.copy()
    if env:
        e.update(env)
    r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout, env=e)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

Using shell=True in a generic command helper is a tool-parameter abuse pattern because it turns string input into shell-interpreted commands, enabling injection if any caller passes variable data. In a deployment script that reads config, discovers files under a workspace, and starts services, this pattern is more dangerous because operational scripts often evolve to include user- or config-derived command fragments.

Content

Scanner excerpt · scripts/deploy_stats.py (reported line 20)May include surrounding context.

python
e = os.environ.copy()
    if env:
        e.update(env)
    r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout, env=e)
    return r.returncode, r.stdout.strip(), r.stderr.strip()

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
88% confidence
Finding

This helper forwards the entire parent environment to any subprocess started via run_args, which increases secret exposure to child processes. Because this script launches operational tooling and shell scripts in a deployment context, broad environment inheritance expands the blast radius if those tools are untrusted, replaced, or verbose about their runtime state.

Content

Scanner excerpt · scripts/deploy_stats.py (reported line 26)May include surrounding context.

python
def run_args(args, timeout=30, env=None):
    """Run a command as a list of args (avoids shell interpolation of secrets)."""
    e = os.environ.copy()
    if env:
        e.update(env)
    r = subprocess.run(args, capture_output=True, text=True, timeout=timeout, env=e)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
92% confidence
Finding

The server is launched with the entire parent environment plus the gateway token, which can expose many secrets to the spawned process beyond the one intended credential. In this skill, the child process is discovered dynamically from the workspace, so if a malicious or tampered stats_server.py is present, it gains access to all inherited environment variables and the gateway token.

Content

Scanner excerpt · scripts/deploy_stats.py (reported line 123)May include surrounding context.

python
["python3", stats_script],
                stdout=log,
                stderr=log,
                env={**os.environ, **env_override},
                start_new_session=True
            )
        time.sleep(1)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% 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).

Content

Scanner excerpt · scripts/detect_env.py (reported line 15)May include surrounding context.

python
def run(cmd, timeout=5):
    try:
        r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
        return r.stdout.strip()
    except Exception:
        return ""

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script explicitly retrieves a gateway authentication token from CLI config or local configuration and includes it in printed JSON output, even though its stated purpose is only environment detection. Emitting a secret to stdout can expose credentials to logs, parent processes, calling agents, or downstream telemetry, enabling unauthorized access to the gateway.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script accesses and discloses a gateway authentication token without any user-facing warning, consent flow, or indication in its stated purpose. Silent secret extraction is dangerous because it bypasses informed approval and can leak credentials into outputs that are routinely captured or shared.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The trigger phrases are broad enough that the skill may activate on casual requests such as wanting help with the iOS app, causing the agent to perform setup actions and reveal configuration details without sufficiently specific user intent. In this skill, unintended invocation is more dangerous because later steps include deploying services, modifying persistence, and disclosing tokens.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
85% 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 · SKILL.md (reported line 80)May include surrounding context.

ENSURE_SCRIPT="/scripts/dashboard/ensure_stats_server.sh"

Tell user to run:

crontab -e

Add this line (substituting the actual path):

@reboot bash /scripts/dashboard/ensure_stats_server.sh

text

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 · SKILL.md (reported line 140)May include surrounding context.

}

text

Then run: `sudo nginx -t && sudo systemctl reload nginx`

**Connection details for iOS app:**
- Gateway URL: `https://YOUR_DOMAIN`

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill instructs exposing a gateway and sharing an authentication token without a strong warning about the security implications of publishing the service to the internet. Users may unknowingly create externally reachable access paths and mishandle credentials, increasing the chance of unauthorized account or service access.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The LAN instructions state that the setup only works on the same Wi‑Fi network, but they do not clearly warn that anyone else on that network who obtains the token may be able to reach the service. In shared home, office, hotel, or guest networks, this can expose administrative access more broadly than the user expects.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
96% confidence
Finding

This call executes a shell command with shell=True, which creates command-injection risk if the command string is ever influenced by external configuration, environment, or future code changes. In this skill, the helper is used for commands like openclaw config get and pgrep, so immediate exploitability is limited, but the reusable wrapper makes the dangerous pattern broadly available in a deployment script that handles secrets and process control.

Content

Scanner excerpt · scripts/deploy_stats.py (reported line 20)May include surrounding context.

python
e = os.environ.copy()
    if env:
        e.update(env)
    r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout, env=e)
    return r.returncode, r.stdout.strip(), r.stderr.strip()

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/deploy_stats.py (reported line 29)May include surrounding context.

python
e = os.environ.copy()
    if env:
        e.update(env)
    r = subprocess.run(args, capture_output=True, text=True, timeout=timeout, env=e)
    return r.returncode, r.stdout.strip(), r.stderr.strip()

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/deploy_stats.py (reported line 119)May include surrounding context.

python
# Start directly — token passed via env, NOT inlined in shell command
        log_path = "/tmp/stats_server.log"
        with open(log_path, "a") as log:
            proc = subprocess.Popen(
                ["python3", stats_script],
                stdout=log,
                stderr=log,

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The docstring describes benign environment metadata collection, but the implementation also extracts and outputs a gateway authentication token. This mismatch reduces transparency and can mislead reviewers or users into approving execution of a script that handles sensitive credentials unexpectedly.

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/detect_env.py (reported line 15)May include surrounding context.

python
def run(cmd, timeout=5):
    try:
        r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout)
        return r.stdout.strip()
    except Exception:
        return ""

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script contacts an external service (api.ipify.org) to determine public IP without warning the user, creating undisclosed outbound network traffic. In an agent skill context this is more sensitive because execution may occur in restricted, enterprise, or privacy-sensitive environments where external callbacks reveal host activity and metadata.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

In Step 1, the documented JSON output fields include public_ip and do not list lan_ip (L037-L047). Later, Step 4c instructs the operator to use lan_ip from detect_env.py output directly (L174), which contradicts the earlier documentation about what the script outputs and could mislead setup behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.