Back to skill

Security audit

weixin-send

Security checks for vulnerabilities and agentic risk

Overview

This skill is openly a WeChat message-sending fallback, but it grants credential-backed outbound messaging from scripts or agents that may not have normal messaging permissions or audit logs.

Install only for trusted workflows that are allowed to send WeChat messages as the configured bot. Treat command execution of this script as equivalent to messaging permission, restrict access to the OpenClaw Weixin account directory, prefer the normal OpenClaw message tool when available, and avoid using configurable API base URLs unless they are strictly validated and trusted.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
send.py:54
Finding

Unvalidated API Base URL Can Exfiltrate WeChat Credentials and Message Data

Content
View full analysis

Vulnerability Details

File Location: send.py, lines 54-78
Vulnerability Type: Untrusted credential destination / sensitive-data disclosure
Risk Level: High

Vulnerable Code

python
def send_text(to_user: str, text: str, account_id: str = None) -> dict:
    acct, acct_id = find_account(account_id)
    token = acct["token"]
    base_url = acct.get("baseUrl", "https://ilinkai.weixin.qq.com")
    ctx_token = find_context_token(acct_id, to_user)

    body = json.dumps({
        "msg": {
            "from_user_id": "",
            "to_user_id": to_user,
            "client_id": generate_client_id(),
            "message_type": 2,
            "message_state": 2,
            "item_list": [{"type": 1, "text_item": {"text": text}}],
            "context_token": ctx_token,
        }
    }).encode()

    url = f"{base_url.rstrip('/')}/ilink/bot/sendmessage"
    req = urllib.request.Request(
        url, data=body,
        headers={
            "Content-Type": "application/json",
            "Authorization": f"Bearer {token}",
            "AuthorizationType": "ilink_bot_token",
            "X-WECHAT-UIN": random_wechat_uin(),
        },
        method="POST",
    )

Technical Analysis

The destination URL is derived from the baseUrl field of a local account JSON file. The implementation does not validate the URL scheme, hostname, port, embedded credentials, or resolved network address before attaching the WeChat bearer token and transmitting the request.

Consequently, a modified or unexpectedly configured account file can direct the request to an attacker-controlled endpoint. The resulting request discloses:

  • The WeChat bot bearer token in the Authorization header.
  • The target user's identifier.
  • The target user's context token.
  • The complete outbound message.
  • Request metadata associated with the bot operation.

The code also does not require HTTPS. ...[truncated 1702 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove configurable API destinations if they are not operationally required and always use https://ilinkai.weixin.qq.com.
  2. If configurability is required, implement a strict allowlist of approved HTTPS hostnames.
  3. Reject non-HTTPS schemes, unexpected ports, embedded URL credentials, fragments, and malformed URLs.
  4. Disable cross-origin redirects or verify every redirect target against the same allowlist before forwarding authorization headers.
  5. Consider validating DNS resolution to prevent approved-looking hostnames from resolving to loopback, link-local, or private infrastructure where inappropriate.
  6. Store account files with restrictive filesystem permissions and verify their ownership before reading credentials.
  7. Avoid returning raw remote response bodies in error messages because they may contain attacker-controlled content or sensitive server details.
  8. Add security tests proving that bearer tokens cannot be sent to HTTP endpoints or unapproved domains.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
send.py:32
Finding

Direct Credential Use Bypasses Messaging Authorization and Audit Controls

Content
View full analysis

Vulnerability Details

File Location: send.py, lines 32-78
Vulnerability Type: Messaging authorization and audit-control bypass
Risk Level: High

Vulnerable Code

python
def find_context_token(account_id: str, to_user: str) -> str:
    ctx_file = ACCOUNTS_DIR / f"{account_id}.context-tokens.json"
    if ctx_file.exists():
        tokens = json.loads(ctx_file.read_text())
        return tokens.get(to_user, "")
    return ""

def send_text(to_user: str, text: str, account_id: str = None) -> dict:
    acct, acct_id = find_account(account_id)
    token = acct["token"]
    base_url = acct.get("baseUrl", "https://ilinkai.weixin.qq.com")
    ctx_token = find_context_token(acct_id, to_user)

    body = json.dumps({
        "msg": {
            "from_user_id": "",
            "to_user_id": to_user,
            "client_id": generate_client_id(),
            "message_type": 2,
            "message_state": 2,
            "item_list": [{"type": 1, "text_item": {"text": text}}],
            "context_token": ctx_token,
        }
    }).encode()

    url = f"{base_url.rstrip('/')}/ilink/bot/sendmessage"
    req = urllib.request.Request(
        url, data=body,
        headers={
            "Content-Type": "application/json",
            "Authorization": f"Bearer {token}",
            "AuthorizationType": "ilink_bot_token",
            "X-WECHAT-UIN": random_wechat_uin(),
        },
        method="POST",
    )
    try:
        resp = urllib.request.urlopen(req, timeout=15)
        return {"ok": True, "status": resp.status}

Technical Analysis

The Skill directly reads bot credentials and context tokens from the OpenClaw account directory and calls the WeChat API without using the OpenClaw messaging framework. There is no authorization check establishing that the invoking agent has messaging privileges, no recipient allowlist, no user-confirmation boundary, and ...[truncated 2380 chars]

Remediation
View remediation

Remediation Suggestions

  1. Route messages through the approved OpenClaw messaging tool whenever operating inside an agent environment.
  2. Do not use the standalone script to defeat an intentionally restricted tool profile.
  3. Require explicit authorization before credential access, such as a narrowly scoped service identity, signed invocation token, or operating-system access-control boundary.
  4. Enforce recipient and account allowlists for cron jobs and non-agent automation.
  5. Add durable audit records containing the invoking identity, selected bot account, recipient, timestamp, result, and a safe message identifier.
  6. Apply restrictive ownership and permissions to bot token and context-token files.
  7. Separate background automation credentials from interactive agent credentials and grant each credential only the minimum necessary scope.
  8. Add rate limits and user-confirmation requirements for new recipients or unusually high message volumes.
  9. Clearly disclose in the documentation that direct invocation grants authenticated messaging capability and must not be exposed to agents lacking messaging authorization.
  10. Prefer a brokered helper service that enforces policy without exposing reusable bearer tokens to the calling process.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (9)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README explicitly documents that the skill reads local bot tokens and context tokens, then directly sends messages via the WeChat ilink API, but it does not clearly warn users about the privacy and integrity implications of using locally stored credentials for proactive outbound messaging. In this context, the skill enables message sending outside the normal channel framework, which increases the chance of unintended or unauthorized messaging if the skill is invoked carelessly or by another component.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares no explicit tool scope even though the documentation describes capabilities that read local token files and perform outbound network requests. Without a declared permission boundary, an agent or reviewer may underestimate what the skill can do, increasing the risk of unauthorized message sending or token misuse.

Content

No source excerpt is available for this finding.

Ssd 4

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill instructs agents to bypass the standard messaging channel and directly call a local token-backed API from any session or cron context. This expands who can send messages, weakens channel-level safeguards, and creates a path for unauthorized or policy-evading communications using locally stored credentials.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The skill promotes proactive outbound messaging but does not prominently warn that it bypasses the normal messaging path and may expose user identifiers, message content, and local token-backed delivery outside standard agent controls. Users and operators may not realize the privacy, consent, and audit implications before using it.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documentation explicitly advertises that use does not trigger session logs, which reduces auditability and can enable untracked outbound communications. That makes misuse harder to detect and investigate, especially in cron jobs or cross-session contexts where messages can be sent without normal conversation records.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The script loads account JSON from the user's home directory and extracts a bearer token for authenticated API calls. There is no warning in code comments, help text, or runtime output that local bot credentials will be accessed and used.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code performs an outbound HTTP POST containing the recipient ID, message text, and context token to the WeChat API. While sending is the script's purpose, there is no explicit disclosure in comments, docstrings, or runtime messaging that user-provided content and account context will be transmitted to an external service.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The natural-language instructions and usage guidance are entirely in Chinese, effectively forcing a specific language for users of the skill documentation. The file does not indicate that the language is optional, selectable, or justified as region-specific documentation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The natural-language module description is written entirely in Chinese and provides no indication that the language is configurable or intentionally restricted to a specific locale. Under the policy, fixed language behavior without opt-in can be a locale-policy issue.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.