Back to skill

Security audit

OpenClaw Network Diagnostics

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Telegram network diagnostics skill, but it should be reviewed carefully because it can expose bot credentials, send live Telegram messages, and run as a persistent background network worker.

Install only if you control the Telegram bot, chat, host, and config files. Keep telegram_api_host set to api.telegram.org unless you intentionally trust another endpoint, keep redaction enabled, protect or minimize logs, avoid --proxy until the plaintext runtime-config behavior is fixed, prefer foreground mode for testing, and rotate the bot token if validate-config output or .netdiag.runtime.config.json may have exposed it.

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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/netdiag.py:1774
Finding
Configuration Validation Exposes Telegram Credentials<![CDATA[ ## Vulnerability Details **File Location**: `scripts/netdiag.py:1774-1775` **Vulnerability Type**: Sensitive credential disclosure through standard output **Risk Level**: High ### Vulnerable Code ```python print("valid") print(json.dumps(cfg, ensure_ascii=True, indent=2)) ``` ### Technical Analysis The `validate-config` command prints the complete merged configuration after successful validation. The configuration contains sensitive values including: - `telegram.bot_token` - `telegram.personal_chat_id` The output is not passed through the Skill's existing redaction functions. Consequently, validating a production configuration discloses the Telegram bot token in plaintext to standard output. This contradicts the otherwise enabled `logging.redact_sensitive_fields` protection. Standard output is commonly captured by CI/CD systems, orchestration tools, agent transcripts, terminal logging, and support diagnostics, making it an unsafe location for secrets. ### Attack Path 1. An operator creates a real configuration containing a valid Telegram bot token. 2. The operator or an automated job runs: ```bash python3 scripts/netdiag.py validate-config --config config.json ``` 3. The command prints the complete configuration, including the plaintext bot token. 4. The output is retained in a CI log, terminal recording, agent transcript, or support artifact. 5. A party with access to that retained output obtains the bot token. 6. The party uses the token to invoke Telegram Bot API methods available to that bot. ### Impact Assessment Disclosure grants possession of the bot credential rather than host-level privileges. An attacker may impersonate the bot, send messages, retrieve pending updates where API behavior and bot configuration permit it, inspect bot metadata, or otherwise exercise the Bot API permissions associated with the exposed token. The personal chat ID is also disclosed and may facilitate targeted misuse of the compromised bot. The ...[truncated 134 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never print the complete merged configuration by default. 2. Return only a validation status and a list of non-sensitive configuration checks. 3. If configuration output is required, recursively redact at least: - `telegram.bot_token` - authentication headers - proxy credentials - cookies and secret tokens 4. Apply redaction regardless of `logging.redact_sensitive_fields`; validation output should not support plaintext secret display. 5. Add automated tests asserting that known token values never appear in stdout or stderr. 6. Document that any previously captured validation output must be removed and affected bot tokens rotated. A safer implementation would print only: ```python print("valid") print("Configuration passed validation; sensitive values were not displayed.") ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/netdiag.py:1834
Finding
Proxy Override Persists a Plaintext Copy of the Bot Token<![CDATA[ ## Vulnerability Details **File Location**: `scripts/netdiag.py:1834-1836` and `scripts/netdiag.py:1847-1849` **Vulnerability Type**: Insecure persistent storage of credentials **Risk Level**: High ### Vulnerable Code The `run` command writes the merged configuration when `--proxy` is supplied: ```python if getattr(args, "proxy", None) is not None: config_path = config_path.parent / ".netdiag.runtime.config.json" config_path.write_text(json.dumps(cfg, ensure_ascii=False, indent=2), encoding="utf-8") ``` The `start` command repeats the same behavior: ```python if getattr(args, "proxy", None) is not None: config_path = config_path.parent / ".netdiag.runtime.config.json" config_path.write_text(json.dumps(cfg, ensure_ascii=False, indent=2), encoding="utf-8") ``` ### Technical Analysis Supplying a proxy override causes the entire merged configuration to be serialized into the predictable file `.netdiag.runtime.config.json`. That configuration includes the plaintext Telegram bot token and personal chat ID. The code does not: - Enforce owner-only file permissions such as mode `0600`. - Use exclusive creation to prevent interaction with a pre-existing path. - Use an unpredictable temporary filename. - Remove the runtime file when the worker exits. - Avoid copying unrelated secrets when only the proxy value changed. The resulting access permissions depend on the parent directory and process umask. The hidden filename does not provide access control, and the credential copy remains after shutdown. ### Attack Path 1. An operator stores a valid Telegram bot token in the normal configuration. 2. The operator invokes `run` or `start` with `--proxy`. 3. The Skill writes the complete merged configuration to the predictable `.netdiag.runtime.config.json` path. 4. The file remains after the worker terminates. 5. Another local account, backup process, artifact collector, or compromised process with access to the directory reads the file. 6. T ...[truncated 670 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not create a runtime configuration file for a single proxy override. 2. Pass the already merged `cfg` object directly to `NetworkDiagnosticWorker` instead of reloading it from disk. 3. If cross-process transfer is required: - Create a private temporary file using secure exclusive creation. - Set permissions to `0600` before writing secrets. - Use an unpredictable filename in an owner-controlled directory. - Delete the file in a `finally` block and during abnormal-start cleanup. 4. Serialize only the minimum required runtime values instead of copying the complete credential-bearing configuration. 5. Reject symlinks and pre-existing runtime paths if a fixed path must be retained. 6. Add tests checking file permissions, cleanup behavior, symlink resistance, and absence of unnecessary credential copies. 7. Rotate tokens if existing runtime files may have been exposed. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/netdiag.py:1247
Finding
Unrestricted API Host Can Redirect Bot Credentials to a Non-Telegram Server<![CDATA[ ## Vulnerability Details **File Location**: `scripts/netdiag.py:355`, `scripts/netdiag.py:1247`, and `scripts/netdiag.py:1363-1367` **Vulnerability Type**: Credential forwarding to a configurable remote endpoint **Risk Level**: High ### Vulnerable Code The destination hostname is loaded directly from configuration: ```python self.telegram_host: str = str(config["network"]["telegram_api_host"]) ``` The bot token is embedded in the HTTP request path: ```python path = f"/bot{self.bot_token}/{method_name}" ``` TLS authenticates the configured hostname, not specifically Telegram: ```python tls_sock = self.ssl_context.wrap_socket( sock, server_hostname=self.telegram_host, do_handshake_on_connect=False, ) ``` The request containing the token-bearing path is then sent over that connection: ```python request_line = f"{method.upper()} {path} HTTP/1.1\r\n".encode("ascii") header_blob = b"".join( f"{key}: {value}\r\n".encode("utf-8") for key, value in headers.items() ) raw_request = request_line + header_blob + b"\r\n" + payload_bytes tcp_state = "http_request_sent" tls_sock.sendall(raw_request) ``` ### Technical Analysis The bot token is part of every Telegram Bot API URL path. The `telegram_api_host` setting is configurable, but validation does not restrict it to `api.telegram.org` or another explicitly trusted Telegram endpoint. The default TLS context correctly verifies certificates for the selected hostname. However, this only proves that the peer controls a valid certificate for the configured hostname. It does not prove that the peer is Telegram. If an attacker can provide or modify the configuration, they can select an attacker-controlled HTTPS domain with a valid certificate. The Skill will then send the token-bearing request path to that domain. This is a trust-boundary flaw: a credential scoped conceptually to Telegram is forwarded to an arbitrary configurable host without an explicit trust decision or warning. ### Att ...[truncated 1105 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict `network.telegram_api_host` to `api.telegram.org` for standard operation. 2. Reject arbitrary hosts during configuration validation. 3. If custom Bot API servers are a legitimate requirement: - Introduce an explicit `allow_custom_api_endpoint` option that defaults to `false`. - Require an exact allowlist rather than accepting arbitrary hostnames. - Display a prominent warning that the bot token will be sent to the custom endpoint. - Consider certificate or public-key pinning for approved private endpoints. 4. Separate diagnostic targets from credential-bearing API targets. Arbitrary hosts may be acceptable for generic connectivity tests but must not automatically receive Telegram credentials. 5. Add tests proving that an unapproved hostname prevents startup before any authenticated request is made. 6. Rotate the token if it has already been sent to an untrusted endpoint. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/netdiag.py:1710
Finding
Unverified PID File Can Terminate an Unrelated Process<![CDATA[ ## Vulnerability Details **File Location**: `scripts/netdiag.py:1710-1723` **Vulnerability Type**: Unsafe process control through trusted PID-file contents **Risk Level**: Medium ### Vulnerable Code ```python try: pid = int(pid_file.read_text(encoding="utf-8").strip()) except ValueError: print("Invalid pid file") return 2 if not is_process_alive(pid): pid_file.unlink(missing_ok=True) print("Not running (stale pid file removed)") return 1 os.kill(pid, signal.SIGTERM) ``` The liveness check only tests whether some process exists at that PID: ```python def is_process_alive(pid: int) -> bool: if pid <= 0: return False try: os.kill(pid, 0) except OSError: return False return True ``` ### Technical Analysis The `stop` command accepts a user-selected PID-file path and trusts the integer stored in it. Before sending `SIGTERM`, the code verifies only that a process currently exists at the specified PID. It does not verify: - That the process is an instance of `netdiag.py`. - That the process command line matches the expected worker. - That the PID has not been recycled since the file was written. - A process start timestamp or other stable identity. - A worker-specific random nonce. - Ownership and secure permissions of the PID file. As a result, a replaced, stale, or deliberately supplied PID file can cause the command to signal an unrelated process. Operating-system permission checks still apply, so the invoking user can generally terminate only processes that user is authorized to signal. ### Attack Path 1. An attacker identifies a process that the operator is permitted to terminate. 2. The attacker writes that process's PID into a PID file writable by the attacker, or persuades the operator to use an attacker-controlled path. 3. The operator runs: ```bash python3 scripts/netdiag.py stop --pid-file /path/to/controlled.pid ``` 4. `is_process_alive` confirms that some proces ...[truncated 787 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create PID files with exclusive creation and owner-only permissions. 2. Store more than a PID, including: - Process start time. - Resolved script path. - A cryptographically random worker nonce. 3. Before signaling, verify that: - The PID belongs to the expected user. - The process start time matches the recorded value. - The executable or command line identifies this `netdiag.py` worker. - The worker nonce matches an independently verifiable value. 4. Refuse to operate on symlinked, group-writable, or world-writable PID files and parent directories. 5. Prefer a supervised process manager or an authenticated local control socket where available. 6. Handle PID reuse explicitly and remove stale PID files only after identity verification. 7. Add tests covering altered PID files, recycled PIDs, symlinked PID paths, and unrelated live processes. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (9)

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill describes operational behavior that clearly relies on shell execution, file reads/writes, and likely environment access for credentials, but it does not declare any explicit tool scope or permissions boundary. This increases the chance that an agent runs the skill with broader-than-necessary capabilities, making unintended command execution, file access, or secret exposure harder to review and constrain.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill actively sends real Telegram messages and may poll for acknowledgements from a personal chat, but the user-facing description does not prominently warn about these externally visible side effects. This can cause unintended message delivery, surprise user interaction, rate-limit exposure, or accidental testing against production accounts without informed consent.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The logging model states that records may include IPs, ports, DNS snapshots, TLS metadata, HTTP headers, payload sizes, rate-limit metadata, and stack traces, yet the skill lacks a clear warning that these logs can contain sensitive operational and network information. If logs are retained, shared, or analyzed later, they may expose infrastructure details, identifiers, or secrets-in-context that increase reconnaissance and privacy risk.

Context-Inappropriate Capability

Medium
Confidence
84% confidence
Finding
The script executes external networking tools such as dig, traceroute, and ping, which gives the skill direct host-level probing capability. While arguments are mostly fixed, this still broadens the attack surface and can be abused for reconnaissance, traffic generation, or running privileged diagnostics from within the agent environment.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The skill includes start/stop/status process-management and persistent background execution via PID files and signals, which exceeds pure diagnostics and creates session persistence on the host. In an agent skill context, this is risky because it enables long-lived autonomous activity and can continue network probing or message sending after the initial invocation ends.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"--background",
    ]

    process = subprocess.Popen(
        cmd,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Session Persistence

Medium
Category
Rogue Agent
Content
run_parser.add_argument("--pid-file", default="./logs/netdiag.pid", help="PID file path")
    run_parser.add_argument("--background", action="store_true", help=argparse.SUPPRESS)

    start_parser = sub.add_parser("start", help="Start background worker")
    start_parser.add_argument("--config", help="Path to config JSON")
    start_parser.add_argument("--proxy", help="Override proxy URL")
    start_parser.add_argument("--pid-file", default="./logs/netdiag.pid", help="PID file path")
Confidence
90% confidence
Finding
The explicit 'start background worker' capability provides persistence beyond the immediate command session. In a security-sensitive agent ecosystem, persistence materially increases risk because the tool can continue polling Telegram, generating logs, and interacting with the network without continuous operator awareness.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
When --proxy is used, the code writes a .netdiag.runtime.config.json file containing the merged configuration, including bot token and personal chat ID, without warning or permission hardening. This creates a plaintext secret-spillage risk on disk, especially in shared workspaces, repositories, backups, or multi-user systems.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
for signame in ("SIGINT", "SIGTERM"):
            if hasattr(signal, signame):
                sig = getattr(signal, signame)
                try:
                    loop.add_signal_handler(sig, _handle_signal, signame)
                except NotImplementedError:
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Static analysis

No suspicious patterns detected.