Back to skill

Security audit

Dingtalk Document

Security checks for vulnerabilities and agentic risk

Overview

This DingTalk document skill is mostly coherent, but it stores sensitive credentials persistently and includes an unsafe configuration-writing helper that can enable local command execution from crafted input.

Install only after the helper is fixed to validate allowed config keys, reject control characters, avoid sed interpolation, and store secrets with owner-only permissions or a credential store. Until then, avoid using this skill with real DingTalk app secrets or accounts that can delete documents or change member permissions.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/dt_helper.sh:79
Finding
Insecure File Permissions for Stored DingTalk Credentials and Access Tokens<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dt_helper.sh:8`, `scripts/dt_helper.sh:79-88`, `scripts/dt_helper.sh:140-141`, and `scripts/dt_helper.sh:198-199` **Vulnerability Type**: Plaintext sensitive-data storage with permissions inherited from the process environment **Risk Level**: Medium ### Vulnerable Code ```bash CONFIG="${DINGTALK_CONFIG:-$HOME/.dingtalk-skills/config}" ``` ```bash cfg_set() { local key="$1" local value="$2" mkdir -p "$(dirname "$CONFIG")" touch "$CONFIG" if grep -q "^${key}=" "$CONFIG" 2>/dev/null; then sed -i "s|^${key}=.*|${key}=${value}|" "$CONFIG" else echo "${key}=${value}" >> "$CONFIG" fi } ``` ```bash cfg_set DINGTALK_ACCESS_TOKEN "$token" cfg_set DINGTALK_TOKEN_EXPIRY "$((now + expire_in - 200))" ``` ```bash cfg_set DINGTALK_OLD_TOKEN "$token" cfg_set DINGTALK_OLD_TOKEN_EXPIRY "$((now + expires_in - 200))" ``` ### Technical Analysis The helper stores the DingTalk application secret and reusable access tokens in a plaintext configuration file. When the file and its parent directory are created, the script does not set a restrictive `umask`, assign explicit permissions, or verify the security of an existing configuration path. Consequently, the effective permissions depend on the environment in which the script runs. For example, under a conventional `umask 022`, `touch "$CONFIG"` can create a file readable by other local users. The parent directory may likewise be created without owner-only access. The stored values include: - `DINGTALK_APP_SECRET` - `DINGTALK_ACCESS_TOKEN` - `DINGTALK_OLD_TOKEN` These values can authorize calls to DingTalk document, workspace, contact, or other APIs according to the permissions granted to the application. ### Attack Path 1. A user or Agent invokes `dt_helper.sh --set` or a token command in an environment with permissive default file-creation permissions. 2. `cfg_set` creates `$HOME/.dingtalk-skills/config` using `touch`, without applying owne ...[truncated 1153 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply restrictive permissions before creating any credential file: ```bash umask 077 mkdir -p -m 700 "$(dirname "$CONFIG")" touch "$CONFIG" chmod 600 "$CONFIG" ``` 2. Validate existing paths before using them: - Reject symbolic links. - Confirm that the configuration is a regular file. - Confirm that the file and parent directory are owned by the current user. - Reject group-readable or world-readable permissions. 3. Write configuration changes atomically: - Create a temporary file in the same protected directory. - Set mode `600`. - Write and validate the complete configuration. - Atomically rename it over the original file. 4. Prefer an operating-system credential store or dedicated secret manager for the application secret and long-lived credentials. 5. Minimize token lifetime and cached scope where DingTalk supports it. Clear cached tokens when they are no longer required. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/dt_helper.sh:79
Finding
Command Execution Through Unsanitized Input Embedded in a sed Program<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dt_helper.sh:79-88` and `scripts/dt_helper.sh:323-333` **Vulnerability Type**: Command injection through dynamically constructed GNU sed expressions **Risk Level**: High ### Vulnerable Code The configuration-writing sink constructs a `sed` program from unescaped input: ```bash cfg_set() { local key="$1" local value="$2" mkdir -p "$(dirname "$CONFIG")" touch "$CONFIG" if grep -q "^${key}=" "$CONFIG" 2>/dev/null; then sed -i "s|^${key}=.*|${key}=${value}|" "$CONFIG" else echo "${key}=${value}" >> "$CONFIG" fi } ``` The `--set` handler accepts unrestricted keys and values and passes them directly to `cfg_set`: ```bash cmd_set() { local kv="$1" if [ -z "$kv" ] || [[ "$kv" != *"="* ]]; then echo "❌ 格式错误,用法: --set KEY=VALUE" >&2 exit 1 fi local key="${kv%%=*}" local value="${kv#*=}" cfg_set "$key" "$value" echo "✅ 已设置 ${key}" } ``` ### Technical Analysis Both `key` and `value` are attacker-controlled input to the `--set` command. They are inserted into a GNU `sed` substitution without escaping: ```bash sed -i "s|^${key}=.*|${key}=${value}|" "$CONFIG" ``` This creates several injection primitives: - The key is interpreted as a regular expression. - The value is interpreted as a sed replacement. - A pipe character can terminate the replacement section. - Newline characters can introduce additional sed commands. - On GNU sed, the `e` substitution flag executes the resulting replacement text through a shell. Shell metacharacters contained in an ordinary variable are not re-evaluated directly by Bash during expansion. However, the injected GNU sed `e` flag creates a separate command-execution stage, allowing the substituted output to be interpreted by a shell. The vulnerable update branch is reached when the supplied key already exists in the configuration. An attacker able to invoke `--set` can first create a benign entry and then update it with a malicio ...[truncated 2150 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict configuration keys to an explicit allowlist: ```bash case "$key" in DINGTALK_APP_KEY|DINGTALK_APP_SECRET|DINGTALK_MY_USER_ID|\ DINGTALK_MY_OPERATOR_ID|DINGTALK_ACCESS_TOKEN|DINGTALK_TOKEN_EXPIRY|\ DINGTALK_OLD_TOKEN|DINGTALK_OLD_TOKEN_EXPIRY) ;; *) echo "Invalid configuration key" >&2 exit 1 ;; esac ``` 2. Reject control characters, especially carriage returns and newlines, in both keys and values. 3. Do not interpolate input into a sed program. Replace the implementation with a parser that compares keys as literal strings and writes an atomic replacement file. For example, use `awk` with values passed through environment variables or file descriptors rather than embedded into source text, while still validating newlines and key syntax. 4. If sed must be retained: - Escape regular-expression metacharacters in the key. - Escape `&`, backslashes, and the selected delimiter in the replacement value. - Reject all newline characters. - Do not rely on escaping alone where GNU sed's `e` extension is available. 5. Use a protected temporary file in the same directory, verify successful output, set mode `600`, and atomically rename it into place. 6. Add regression tests covering delimiters, backslashes, ampersands, newlines, sed flags, regular-expression characters, and shell metacharacters. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (29)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill description claims document-management functionality, but the behavior described includes undeclared configuration management, token acquisition/caching, and identity conversion. This mismatch obscures the true security-sensitive capabilities of the skill, making users and reviewers less likely to recognize that it can handle secrets, persist credentials, and perform privileged actions.

Ae1

High
Category
analysis-evasion
Content
> `dt_helper.sh` 位于本 `SKILL.md` 同级目录的 `scripts/dt_helper.sh`。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill instructs use of shell commands but does not declare any tool scope or allowed-tools boundary. This weakens least-privilege controls and makes it harder for the runtime or reviewer to constrain execution, increasing the risk of unintended command execution or credential handling through shell access.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger description uses broad phrases such as general references to documents and knowledge bases, which can cause accidental invocation in normal conversation. In a skill that can read, write, delete, and modify member permissions, mis-triggering materially raises the chance of unintended sensitive operations.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill documentation omits clear risk disclosures for write, delete, and member-permission management actions. Without prominent warnings and confirmation requirements, users may not appreciate that the skill can alter data or access control, increasing the risk of accidental destructive or privilege-changing operations.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation exposes a destructive delete capability for documents but does not include any explicit user-confirmation, recovery, or safety guidance, even though nearby write operations do include stronger warnings. In an agent skill context, this increases the chance that an automated workflow or prompt misunderstanding could irreversibly delete user data without adequate friction.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The file’s natural-language interface, including the title, help output, and usage guidance, is entirely in Chinese. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the skill clearly documents a justified region-specific constraint, which this file does not.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The helper advertises and implements generic DingTalk credential management for multiple API domains, plus configuration persistence for app secrets and tokens, which exceeds the stated document/knowledge-base scope. This overbroad capability increases attack surface and makes credential misuse easier if the skill is invoked in unintended contexts or chained with other actions.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

  # 过期或无缓存,重新获取
  resp=$(curl -s -X POST "https://api.dingtalk.com/v1.0/oauth2/accessToken" \
    -H "Content-Type: application/json" \
    -d "{\"appKey\":\"${app_key}\",\"appSecret\":\"${app_secret}\"}")
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.