Back to skill

Security audit

企业微信存档服务

Security checks for vulnerabilities and agentic risk

Overview

This skill is meant to archive Enterprise WeChat conversations, but it ships hard-coded WeCom secrets and can expose archived messages through unauthenticated public-facing APIs.

Do not install this as-is on a real Enterprise WeChat tenant. Treat all embedded WeCom tokens/secrets as compromised, remove them, require the service to load protected user-supplied secrets, add authentication and authorization to every archive/query/debug endpoint, avoid exposing admin APIs through the public tunnel, disable debug endpoints, stop logging message contents, and add encryption, retention, and access-control enforcement before production use.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wework_combined_service.py:485
Finding
Archived Enterprise Conversations Exposed Through Unauthenticated API<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wework_combined_service.py:485-510`, with external exposure enabled at `scripts/wework_combined_service.py:647` **Vulnerability Type**: Missing authentication and authorization on a sensitive-data API **Risk Level**: High ### Vulnerable Code ```python @app.route('/api/messages', methods=['GET']) def api_get_messages(): """Query message API""" try: # Get query parameters start_time = request.args.get('start_time', type=int) end_time = request.args.get('end_time', type=int) from_user = request.args.get('from_user') room_id = request.args.get('room_id') msg_type = request.args.get('msg_type') limit = request.args.get('limit', 100, type=int) offset = request.args.get('offset', 0, type=int) # Query messages messages = storage_system.query_messages( start_time=start_time, end_time=end_time, from_user=from_user, room_id=room_id, msg_type=msg_type, limit=limit, offset=offset ) return jsonify({ 'success': True, 'count': len(messages), 'messages': messages }) except Exception as e: logger.error(f"Message query API failed: {e}") return jsonify({ 'success': False, 'error': str(e) }), 500 ``` The service is bound to every network interface: ```python app.run(host='0.0.0.0', port=8400, debug=False) ``` ### Technical Analysis The `/api/messages` endpoint returns archived enterprise conversation records without performing any authentication or authorization check. Returned database rows can contain message contents, sender identifiers, recipient lists, room identifiers, timestamps, and the `metadata` field containing the original parsed message. The service listens on `0.0.0.0`, and the project explicitly documents mapping port ...[truncated 1609 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require strong authentication for every archive query and statistics endpoint. 2. Implement role-based authorization so only designated archive administrators can retrieve conversation content. 3. Isolate administrative APIs from public callback routes: - Bind the administrative interface to localhost or a private network. - Use a separate authenticated management service or listener. - Configure Cloudflare ingress so only callback paths are publicly routed. 4. Add strict pagination controls: - Require `1 <= limit <= 100`. - Reject negative offsets. - Apply server-side maximum result and time-range limits. 5. Enforce source-network restrictions where practical, but do not use IP allowlisting as a substitute for authentication. 6. Record authenticated administrative access in tamper-resistant audit logs without logging returned message content. 7. Add automated tests proving anonymous requests receive `401 Unauthorized` or `403 Forbidden`. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wework_combined_service.py:43
Finding
Hard-Coded WeCom Credentials Override the Documented Configuration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wework_combined_service.py:43-51`; related configuration check at `scripts/start_service.sh:10-15` **Vulnerability Type**: Embedded secrets and configuration bypass **Risk Level**: High ### Vulnerable Code ```python # Standard callback configuration CALLBACK_TOKEN = "QFIFdVxh" CALLBACK_ENCODING_AES_KEY = "EsHGr7F9296zJaAhAKtO5Mfa5qnITs2D0S7fSBS0Fj3" CORP_ID = "ww1533baf21ccf36ff" AGENT_ID = 1000016 CORP_SECRET = "zddW0DZ3YIYykXyqk5SKmMm4GN1fduqVhFsR8UXSENA" # Conversation archive configuration ARCHIVE_TOKEN = "mXSkgsfxr3k0OKQrJprkPl" ARCHIVE_ENCODING_AES_KEY = "EuauICQFeCAUI6srNiVR0jDBc8X6oE2QclpHbnrjhyS" ``` The launcher requires a configuration file: ```bash CONFIG_FILE="$SCRIPT_DIR/../config/wework_config.json" # Check whether the configuration file exists if [ ! -f "$CONFIG_FILE" ]; then echo "Error: Configuration file does not exist: $CONFIG_FILE" echo "Create the configuration file first; see config/wework_config_template.json" exit 1 fi ``` However, the Python service does not load that file and instead uses the embedded values. ### Technical Analysis The source contains callback tokens, AES keys, a corporate identifier, an application identifier, and a corporate secret that appear structurally usable. These values are accessible to anyone who obtains the package or repository history. The runtime behavior contradicts the documented setup process. Although `start_service.sh` refuses to start unless `config/wework_config.json` exists, `wework_combined_service.py` never reads that file. Operators may populate and rotate their own credentials while the application continues using the embedded values. The embedded corporate secret is transmitted to the official WeCom token endpoint through the query string when `get_access_token()` runs. The official destination is consistent with the declared functionality, but embedding the credential in distributed source code is not nece ...[truncated 1518 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Immediately revoke and rotate every embedded token, AES key, and corporate secret; assume repository history retains old values. 2. Remove all real or realistic credentials from source code and release artifacts. 3. Load configuration from a protected mechanism, such as: - A dedicated secret manager. - Environment variables injected by the service manager. - A configuration file readable only by the dedicated service account. 4. Make the service fail closed when required configuration is absent, malformed, or still contains template placeholders. 5. Ensure `start_service.sh` and the Python process reference the same configuration path. 6. Never log token values, secrets, AES keys, or access-token-bearing URLs. 7. Use a dedicated WeCom application with only the minimum API permissions required for the fixed automatic reply. 8. Apply tenant-side source-IP restrictions where supported. 9. Add secret-scanning checks to version control and CI, and purge secrets from repository history where feasible. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wework_combined_service.py:217
Finding
Decrypted Conversation Content Is Logged and Duplicated in Plaintext Storage<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wework_combined_service.py:217-228`, `scripts/wework_combined_service.py:342-373`, and `scripts/simple_storage.py:67-88` **Vulnerability Type**: Plaintext storage and excessive logging of sensitive data **Risk Level**: High ### Vulnerable Code The callback handler logs decrypted message data and text: ```python raw_data = decrypt_data(encrypt_msg, CALLBACK_ENCODING_AES_KEY, CORP_ID) if not raw_data: logger.error("Standard callback message decryption failed") return 'success' logger.info(f"Standard callback message decrypted successfully, raw data: {raw_data}") # Parse a standard message and automatically reply try: msg_root = ET.fromstring(raw_data) from_user = msg_root.find('FromUserName').text msg_type = msg_root.find('MsgType').text if msg_type == 'text': content = msg_root.find('Content').text logger.info(f"Received text message from user {from_user}: {content}") ``` Archive records are written to plaintext JSON files: ```python # Add new message existing_data.append({ 'timestamp': datetime.now().isoformat(), 'data': message_data }) # Save with open(filename, 'w', encoding='utf-8') as f: json.dump(existing_data, f, ensure_ascii=False, indent=2) logger.info(f"Archived message saved to: {filename}") return True ``` The SQLite metadata duplicates the original message: ```python # Build metadata metadata = { 'original_data': message_data, 'parsed_time': datetime.now().isoformat() } cursor.execute(''' INSERT OR REPLACE INTO messages (msg_id, msg_type, from_user, to_users, room_id, content, timestamp, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ''', (msg_id, msg_type, from_user, to_users, room_id, content, timestamp, json.dumps(metadata))) conn.commit() logger.info(f"Message saved successfully: {msg_id} ({msg_type})") ``` ### Technical Analysis The service decrypts WeCom callback messages and writes the full plaintext ...[truncated 1951 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all logging of decrypted payloads, message text, recipients, room identifiers, tokens, and cryptographic material. 2. Log only non-sensitive operational fields, such as a generated request ID, processing result, message type, and redacted message identifier. 3. Eliminate duplicate JSON archive storage unless it is explicitly required. Prefer one controlled storage system. 4. Avoid duplicating the full original message in `metadata` when normalized fields already exist. 5. Encrypt sensitive database fields or the database volume at rest using keys stored separately from the archive. 6. Create files with restrictive permissions and run under a dedicated unprivileged account: - Directories should normally be accessible only by the service account. - Databases, archives, PID files, and logs should not be world-readable. 7. Implement documented retention, automatic deletion, legal-hold handling, and secure backup disposal. 8. Protect backups and log aggregation systems with equivalent access controls and encryption. 9. Add tests that detect message-content leakage in application logs. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
config/wework_config_template.json:22
Finding
Advertised IP Allowlisting and Rate Limiting Are Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `config/wework_config_template.json:22-27`; affected request handling at `scripts/wework_combined_service.py:485-523` **Vulnerability Type**: Security configuration is ignored by runtime code **Risk Level**: Medium ### Vulnerable Code The configuration template advertises network and rate controls: ```json "security": { "enable_ip_whitelist": false, "ip_whitelist": ["127.0.0.1"], "rate_limit_enabled": true, "rate_limit_per_minute": 60 } ``` The affected APIs directly process requests without checking those settings: ```python @app.route('/api/messages', methods=['GET']) def api_get_messages(): """Query message API""" try: start_time = request.args.get('start_time', type=int) end_time = request.args.get('end_time', type=int) from_user = request.args.get('from_user') room_id = request.args.get('room_id') msg_type = request.args.get('msg_type') limit = request.args.get('limit', 100, type=int) offset = request.args.get('offset', 0, type=int) messages = storage_system.query_messages( start_time=start_time, end_time=end_time, from_user=from_user, room_id=room_id, msg_type=msg_type, limit=limit, offset=offset ) return jsonify({ 'success': True, 'count': len(messages), 'messages': messages }) ``` ```python @app.route('/api/stats', methods=['GET']) def api_get_stats(): """Get statistics""" try: stats = storage_system.get_statistics() return jsonify({ 'success': True, 'stats': stats }) ``` ### Technical Analysis The runtime service does not load `wework_config.json`, so none of the advertised `security` settings affect request processing. No middleware or route-level logic implements source-IP filtering or rate limiting. Operators m ...[truncated 1682 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Load and validate the documented configuration during startup. 2. Fail closed if security configuration cannot be loaded or contains invalid values. 3. Implement rate limiting at both the application and reverse-proxy layers: - Use separate limits for callback, archive-query, debug, and health endpoints. - Apply per-source and global limits. - Bound request body sizes and query complexity. 4. Implement trusted source filtering where supported, while retaining cryptographic callback verification. 5. Correctly determine client addresses behind Cloudflare; trust forwarding headers only from known proxy addresses. 6. Require authentication and authorization for management APIs regardless of source IP. 7. Add integration tests demonstrating that: - Non-allowlisted sources are rejected. - Excessive requests receive `429 Too Many Requests`. - Invalid or missing security configuration prevents insecure startup. 8. Remove unsupported settings from the template until their behavior is actually implemented, so operators are not given a false security assurance. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (55)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The documented purpose says the skill provides callback and archive services, but the analyzed behavior reportedly includes additional active capabilities such as using built-in WeWork credentials to obtain access tokens, proactively sending messages, exposing debug signature endpoints, and serving message/statistics APIs. Undisclosed functionality is dangerous because operators may deploy the skill with broader trust than intended, enabling data exposure, unauthorized messaging, or abuse of embedded credentials.

Credential Access

High
Category
Privilege Escalation
Content
## 3. 访问令牌管理

### 3.1 获取Access Token
```
GET https://qyapi.weixin.qq.com/cgi-bin/gettoken
参数: corpid, corpsecret
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The README promotes deployment of a service that archives enterprise WeChat conversations and stores them in SQLite, but it does not prominently warn that this data is highly sensitive, may contain personal or confidential business content, and requires strict access control, retention, and legal review. In the context of an agent skill meant for quick deployment, this omission can lead operators to expose or retain sensitive chat data without adequate safeguards.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The documented query and management endpoints include direct access to messages, per-user and per-room archives, statistics, configuration, and backups, yet the README does not clearly warn that these endpoints can disclose archived messages and sensitive configuration if left unauthenticated or weakly protected. Because the skill is designed to expose the service via Cloudflare/public callback infrastructure, weak operator assumptions around these endpoints materially increase the risk of privacy breaches and secret leakage.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill documentation declares a service that installs software, exposes network endpoints, reads/writes configuration and databases, and runs shell scripts, but it does not define any explicit tool scope or permissions boundary. In an agent/skill ecosystem this increases the chance of overbroad execution and unintended access because consumers cannot tell in advance what capabilities the skill requires or should be restricted to.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill explicitly supports storing and querying archived enterprise chat content, but the documentation does not prominently warn about privacy, consent, retention, access control, or regulatory implications. Because archived workplace communications are highly sensitive, omission of these safeguards can lead to unauthorized surveillance, over-collection, insider misuse, and compliance violations.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The configuration steps instruct users to place multiple high-value secrets into a local JSON file without warning about secure storage, file permissions, rotation, or exclusion from version control. Exposure of callback tokens, AES keys, corp secrets, or archive secrets could allow message forgery, callback spoofing, unauthorized API use, or access to archived communications.

Session Persistence

Medium
Category
Rogue Agent
Content
### 3. 创建隧道
```bash
# 创建隧道
cloudflared tunnel create wework-tunnel

# 查看隧道ID
cloudflared tunnel list
Confidence
60% 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.

Session Persistence

Medium
Category
Rogue Agent
Content
### 3. 创建隧道
```bash
# 创建隧道
cloudflared tunnel create wework-tunnel

# 查看隧道ID
cloudflared tunnel list
Confidence
60% 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.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This SQL file contains natural-language comments and output strings entirely in Chinese, including operator-facing status messages. Because the file does not provide an opt-in language choice or document that it is intentionally limited to a Chinese-speaking environment, it violates the language/locale policy criterion.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The inserted log messages and SELECT output strings shown to operators are fixed in Chinese, which forces a specific language experience. Without a documented regional justification or a configurable language option, this is a natural-language policy violation.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The template explicitly enables message storage, attachment storage, user tracking, and room tracking by default in a service whose stated purpose includes enterprise chat archiving. Even if this is intended functionality, collecting and retaining communications and identifiers without documented consent, notice, access controls, or jurisdiction-specific privacy safeguards creates real privacy and compliance risk if deployed as-is.

Description-Behavior Mismatch

Medium
Confidence
89% confidence
Finding
The manifest description says the skill is an enterprise WeWork integration service focused on normal callbacks and conversation archive functionality. This file claims additional operational features such as one-click deployment, Cloudflare Tunnel public exposure, and database management/maintenance tooling, which go beyond that functional description rather than serving as unavoidable implementation details of callback/archive handling.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The deployment instructions encourage exposing the service to the public internet and running convenience scripts without any warning about authentication, network hardening, rate limiting, secret handling, or attack surface. In the context of an enterprise callback and archive service that processes sensitive WeCom data, omission of these warnings can lead operators to deploy an internet-reachable service insecurely.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
# Ubuntu/Debian
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

# CentOS/RHEL
sudo yum install https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
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
```bash
# Ubuntu/Debian
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

# CentOS/RHEL
sudo yum install https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
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
```bash
# Ubuntu/Debian
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

# CentOS/RHEL
sudo yum install https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
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
```bash
# Ubuntu/Debian
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

# CentOS/RHEL
sudo yum install https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
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
```bash
# Ubuntu/Debian
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

# CentOS/RHEL
sudo yum install https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
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
```bash
# Ubuntu/Debian
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

# CentOS/RHEL
sudo yum install https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
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
```bash
# Ubuntu/Debian
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

# CentOS/RHEL
sudo yum install https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
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
```bash
# Ubuntu/Debian
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

# CentOS/RHEL
sudo yum install https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
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
```bash
# Ubuntu/Debian
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb

# CentOS/RHEL
sudo yum install https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm
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
sudo dpkg -i cloudflared-linux-amd64.deb

# CentOS/RHEL
sudo yum install https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm

# 通用二进制安装
curl -L --output cloudflared https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64
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
sudo dpkg -i cloudflared-linux-amd64.deb

# CentOS/RHEL
sudo yum install https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm

# 通用二进制安装
curl -L --output cloudflared https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Static analysis

No suspicious patterns detected.