Back to skill

Security audit

CMCC Digital Credential

Security checks for vulnerabilities and agentic risk

Overview

The skill is purpose-aligned, but it stores and exposes powerful credentials too casually for a tool that gates sensitive operations.

Review before installing. This skill is not obviously malicious, but it handles app secrets, phone numbers, and approvals for destructive or secret-revealing actions. Use only in a controlled workspace, avoid committing the memory directory, rotate exposed appKey values, and do not rely on this alone as a strong approval boundary for high-impact operations.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/load_credentials.py:117
Finding

Application Secret Stored in Plaintext and Exposed Through an Unrestricted Retrieval Command

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/request_authorization.py:191
Finding

Application Secrets and Phone Numbers Are Exposed Through Process Command-Line Arguments

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/request_authorization.py:24
Finding

Phone Numbers Are Protected with Deterministic AES-ECB and MD5-Based Key Derivation

Content
View full analysis
bytes: """Generate MD5 hash of text.""" return hashlib.md5(text.encode('utf-8')).digest() def encrypt_phone_aes_ecb(phone_no: str, app_key: str) -> str: """ Encrypt phone number using AES/ECB/PKCS5Padding. Based on Java AESUtil.encodeAES: 1. MD5 hash of appKey as 16-byte key 2. AES/ECB/PKCS5Padding encryption 3. Base64 encode result """ # Derive 16-byte key from appKey using MD5 key_bytes = md5_hash(app_key) # Convert phone number to bytes phone_bytes = phone_no.encode('utf-8') # Encrypt using AES/ECB/PKCS5Padding cipher = AES.new(key_bytes, AES.MODE_ECB) encrypted = cipher.encrypt(pad(phone_bytes, AES.block_size)) # Return Base64-encoded result return base64.b64encode(encrypted).decode('utf-8') ``` ### Technical Analysis AES-ECB does not use an initialization vector or nonce. Therefore, the same phone number encrypted with the same application key always produces the same ciphertext. This leaks equality and permits records to be correlated. The encryption is also unauthenticated, so it does not independently detect ciphertext modification. MD5 is used as direct key derivation without a salt, context separation, or work factor. If `appKey` has insufficient entropy, captured ciphertext can assist offline key-guessing attempts. Base64 only converts binary ciphertext to text and provides no additional confidentiality. The network transmission itself is declared by the Skill and is performed over HTTPS, so the Base64 output is not evidence of a covert exfiltration channel. The finding concerns the inadequate application-layer cryptographic design. ### Attack Path 1. An attacker obtains multiple encrypted authorization records, logs, or req ...[truncated 1024 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/request_authorization.py:125
Finding

Authorization Approval Is Not Bound to the Requested Sensitive Operation

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/request_authorization.py:136
Finding

Arbitrary Redirect and Callback URLs Are Forwarded to the Authorization Service

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/poll_authorization.py:109
Finding

HTTP Requests Have No Per-Request Deadline, Allowing Authorization Workflows to Block Indefinitely

Content
View full analysis
= timeout: if verbose: print(f"Timeout after {timeout}s") raise Exception(f"Authorization polling timeout after {timeout}s") time.sleep(interval) ``` ### Technical Analysis `urllib.request.urlopen` is called without an explicit timeout. A connection that stalls during connection establishment, TLS negotiation, or response reading can block the call for an environment-dependent period and potentially indefinitely. The advertised ten-minute timeout does not protect an in-progress `query_authorization` call because elapsed time is checked only after that function returns or raises. The binding ...[truncated 1138 chars]
Remediation
View remediation
0`, `timeout > 0`, and `interval <= timeout`. - Add bounded retry behavior with exponential backoff and jitter for transient failures. - Place an upper bound on response size before parsing JSON. - Treat deadline expiration as a hard authorization failure and never proceed with the sensitive operation. - Consider a maintained HTTP client that provides explicit connection, TLS, read, and total timeout controls. ]]>
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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (10)

Credential Access

High
Category
Privilege Escalation
Confidence
92% confidence
Finding

The skill instructs storing the long-lived appId and especially appKey in a plaintext memory file at memory/cmcc-digital-credential.json for reuse across future sensitive operations. Persisting reusable secrets in an agent-accessible file materially increases the chance of credential disclosure through other skills, logs, backups, or filesystem access, and those credentials can then be abused to request or spoof authorization flows.

Content

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

  • appId: Application ID (24 characters)
    • appKey: Application secret key
  1. Store credentials to memory/cmcc-digital-credential.json:
    json
    {
      "appId": "...",
    

Credential Access

High
Category
Privilege Escalation
Confidence
84% confidence
Finding

This example reinforces and normalizes the practice of persisting credentials to a predictable local file path, which can encourage insecure operational handling of secrets. Even though the example masks appKey in the displayed output, the underlying behavior still leaves the real secret stored on disk where it may be accessible to other components or users.

Content

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

appKey: *** (hidden) appName: Javis (predefined) templateId: qfx9pkizs42up7y61jsehs9v8e1xms4m (predefined) Stored to: memory/cmcc-digital-credential.json

text

### Example 2: Phase 2 - Performing Sensitive Operation

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/load_credentials.py (reported line 13)May include surrounding context.

python
import re
import sys

MEMORY_PATH = "memory/cmcc-digital-credential.json"


def parse_credentials_file(file_content: str) -> dict:

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The credential file format requires users to supply fields named 智能体DID and 智能体密钥, and nearby headings/instructions also switch to Chinese, but the document does not state that the skill is China-region-only or provide an alternative language/locale option. This creates a language/locale policy issue because the skill effectively imposes a specific language convention without user opt-in or clear justification.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script persists sensitive credentials to a predictable plaintext JSON file on disk without warning the user or applying any protection such as restrictive permissions or encryption. In a skill context, stored appKey material can be recovered later by other local users, processes, backups, or accidental commits, enabling unauthorized use of the credential.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · scripts/load_credentials.py (reported line 200)May include surrounding context.

python
load_parser = subparsers.add_parser('load', help='Load credentials from file')
    load_parser.add_argument('file', help='Path to credential file')
    load_parser.add_argument('--force', '-f', action='store_true',
                             help='Overwrite existing credentials without confirmation')

    # Check command
    check_parser = subparsers.add_parser('check', help='Check if credentials exist')

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The get command prints the full credential object when no field is specified, which includes appKey in plaintext. This can leak secrets to terminal history, logs, screenshots, calling wrappers, or other systems that capture stdout, making credential compromise straightforward.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script accepts the application secret key via the --appKey command-line argument, which can expose the credential through shell history, process listings, audit logs, and orchestration tooling. In a credential/authorization polling skill, this is more dangerous because the secret is directly used to generate authentication signatures for API requests, so disclosure could enable unauthorized API access or impersonation of the client application.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation for load_credentials_from_file, save_credentials_to_memory, and load_credentials_from_memory says the credential dictionary includes templateId, but the actual parser only returns appId and appKey and no code injects templateId before saving or returning data. This is an active contradiction between the inline documentation and runtime behavior, not just an omission.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The script's user-facing description, help text, success messages, and error messages are all hardcoded in English. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is explicitly documented and justified, which it is not here.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.