Back to skill

Security audit

Dingtalk Contact

Security checks for vulnerabilities and agentic risk

Overview

This DingTalk directory skill is mostly coherent, but it stores credentials and tokens persistently and uses unsafe shell/script patterns around sensitive employee data.

Review before installing. Use only with a least-privilege DingTalk app, avoid shared machines, lock down or replace the plaintext config storage, do not paste untrusted names or IDs into generated shell scripts, and avoid displaying or logging full employee records unless there is a clear business need.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/dt_helper.sh:87
Finding
Persistent credentials and access tokens are stored without restrictive file permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dt_helper.sh`, lines 87-99 **Vulnerability Type**: Plaintext sensitive-data storage with unsafe default permissions **Risk Level**: High ### Vulnerable Code ```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 affected function is used to persist sensitive values, including access tokens: ```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 configuration file stores the DingTalk AppSecret and cached access tokens in plaintext. The script creates the directory and file using `mkdir -p` and `touch`, but does not establish a restrictive `umask` or explicitly apply secure permissions. Consequently, permissions are inherited from the process environment. Under a common `umask` of `022`, a newly created configuration file can be readable by other local users. The masking performed by `--config` and `--get` only affects command output; it does not protect the contents of the file itself. The script also accepts an alternative path through `DINGTALK_CONFIG`. The same insecure creation behavior applies to that path. ### Attack Path 1. A user invokes `--set` to store `DINGTALK_APP_SECRET`, or invokes `--token` or `--old-token`. 2. `cfg_set` creates the configuration file using inherited default permissions. 3. The file receives the application secret and reusable DingTalk access tokens in plaintext. 4. Another local account or compromised process reads the configuration file if its permissions permit access. 5. The attacker submits the stolen credentials or tokens to the documented DingTal ...[truncated 739 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Set a restrictive process mask before creating or modifying sensitive files: ```bash umask 077 ``` - Create the configuration directory with mode `700` and the configuration file with mode `600`: ```bash install -d -m 700 "$(dirname "$CONFIG")" install -m 600 /dev/null "$CONFIG" ``` - If the file already exists, verify that it is a regular file owned by the current user and enforce `chmod 600`. - Reject symbolic links and unsafe ownership before reading or writing the configuration. - Store long-lived application secrets in an operating-system credential store or dedicated secret manager rather than a plaintext file. - Minimize token lifetime and revoke any credentials suspected of exposure. - Write updates atomically through a securely created temporary file in the same directory, then rename it into place. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:21
Finding
User-controlled values may be embedded into generated shell source and JSON without safe encoding<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 21-49 **Vulnerability Type**: Potential shell command injection and malformed JSON construction **Risk Level**: High ### Vulnerable Code ```markdown 4. **执行操作** → 凡是包含变量替换、管道或多行逻辑的命令,写入 `/tmp/<task>.sh` 再 `bash /tmp/<task>.sh` 执行。不要把多行命令直接粘到终端里(终端工具会截断),也不要用 `<<'EOF'` 语法(heredoc 在工具中同样会被截断导致变量丢失) ``` ```bash #!/bin/bash set -e HELPER="<THE_SKILL_MD_PATH>/scripts/dt_helper.sh" NEW_TOKEN=$(bash "$HELPER" --token) # api.dingtalk.com 接口用 OLD_TOKEN=$(bash "$HELPER" --old-token) # oapi.dingtalk.com 接口用 # USER_ID=$(bash "$HELPER" --get DINGTALK_MY_USER_ID) # 以当前操作用户为起点时启用 # 在此追加具体 API 调用,例如按姓名搜索用户并获取详情: KEYWORD="张三" SEARCH=$(curl -s -X POST https://api.dingtalk.com/v1.0/contact/users/search \ -H "x-acs-dingtalk-access-token: $NEW_TOKEN" \ -H 'Content-Type: application/json' \ -d "{\"queryWord\":\"$KEYWORD\",\"offset\":0,\"size\":20}") echo "搜索结果: $SEARCH" ``` ### Technical Analysis The Skill directs the Agent to generate shell source under `/tmp` and execute it with Bash. The demonstrated template places a search keyword directly inside a shell assignment and manually interpolates it into JSON. Search keywords and user or department identifiers can originate from user input. If an Agent substitutes such input directly into the generated assignment, shell metacharacters, quotation marks, command substitutions, or line breaks can terminate the intended assignment and introduce executable shell syntax. Even when shell execution is not achieved, manual JSON interpolation does not escape quotation marks, backslashes, control characters, or line breaks according to JSON rules. This can alter the request structure or cause the request to fail. The risk arises from generating executable source code from untrusted values rather than passing those values as inert command arguments or data. ### Attack Path 1. An attacker asks the Skill to search for a contact or department using a cr ...[truncated 1479 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not generate shell source code by substituting user-controlled values into script text. - Pass values as positional parameters: ```bash #!/bin/bash set -euo pipefail keyword=$1 payload=$(jq -n --arg queryWord "$keyword" \ '{queryWord: $queryWord, offset: 0, size: 20}') curl --fail-with-body --silent --show-error \ -X POST 'https://api.dingtalk.com/v1.0/contact/users/search' \ -H "x-acs-dingtalk-access-token: $NEW_TOKEN" \ -H 'Content-Type: application/json' \ --data-binary "$payload" ``` - Invoke the script with the keyword as a separately quoted argument rather than inserting it into the script. - Construct all JSON with a proper JSON encoder such as `jq -n --arg` instead of manual string interpolation. - Validate structured identifiers against narrow allowlists where applicable, such as numeric department IDs or documented user-ID character sets. - Add `set -euo pipefail` and use `curl --fail-with-body --silent --show-error` for reliable error handling. - Avoid displaying complete employee records or API error bodies when they may contain sensitive data. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:21
Finding
Predictable scripts in the shared temporary directory permit symlink and replacement attacks<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, line 21 **Vulnerability Type**: Unsafe temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```markdown 4. **执行操作** → 凡是包含变量替换、管道或多行逻辑的命令,写入 `/tmp/<task>.sh` 再 `bash /tmp/<task>.sh` 执行。不要把多行命令直接粘到终端里(终端工具会截断),也不要用 `<<'EOF'` 语法(heredoc 在工具中同样会被截断导致变量丢失) ``` ### Technical Analysis The Skill recommends writing executable scripts to a predictable `/tmp/<task>.sh` path and later executing that pathname. Shared temporary directories are normally writable by all local users. A task-derived filename may be predictable, and the instructions do not require exclusive file creation, ownership checks, restrictive permissions, symlink rejection, or atomic handling. A local attacker may pre-create the expected path as a symbolic link, potentially causing the Agent to overwrite another file writable by the Agent account. Alternatively, the attacker may replace the script after it is written but before Bash opens it, creating a time-of-check/time-of-use race that can result in execution of attacker-controlled content. ### Attack Path 1. A local attacker predicts or observes the task-based temporary filename. 2. Before the Agent writes the script, the attacker creates a symbolic link at that pathname to another file writable by the Agent; or the attacker waits for script creation. 3. The Agent writes to `/tmp/<task>.sh` without exclusive creation or symlink validation. 4. In the symlink scenario, the linked destination is overwritten with generated script content. 5. In the replacement scenario, the attacker renames or replaces the temporary script before the subsequent `bash /tmp/<task>.sh` operation. 6. Bash opens and executes the attacker's replacement script with the Agent account's privileges. ### Impact Assessment A successful replacement attack enables arbitrary command execution with the privileges of the Agent process. The attacker may read the DingTalk credential file, steal cached ...[truncated 311 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Prefer direct, argument-safe command execution and avoid temporary executable scripts entirely. - If a script is unavoidable, create it atomically with `mktemp`: ```bash tmp_script=$(mktemp "${TMPDIR:-/tmp}/dingtalk-task.XXXXXXXX.sh") chmod 600 "$tmp_script" trap 'rm -f -- "$tmp_script"' EXIT HUP INT TERM ``` - Ensure the temporary file is owned by the current user, is a regular file, and is not a symbolic link before use. - Keep creation, writing, and execution within one controlled process, minimizing the interval in which replacement can occur. - Use a private temporary directory with mode `700` when multiple temporary artifacts are required. - Never derive temporary filenames solely from user input or predictable task names. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared purpose is contact lookup, but the instructions also cover persistent local configuration management and token retrieval/caching behavior that are not prominently disclosed in the skill description. This mismatch weakens informed consent and can mislead users or orchestrators about secret storage and operational side effects, which is especially risky in a directory skill handling employee data.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill clearly directs use of shell and external network calls but does not declare any tool scope such as allowed-tools or permissions. That increases the chance an agent executes powerful shell actions without transparent authorization boundaries, especially since the workflow includes credential handling and script execution.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill is designed to retrieve employee names, phone numbers, IDs, department paths, and related directory data, but it does not warn that these operations may expose sensitive personal or organizational information. In a contact-directory context, lack of disclosure can lead to overbroad queries and inadvertent privacy violations.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The workflow instructs the agent to write AppKey and AppSecret through a helper and states the configuration persists across sessions, but it does not require a clear user warning or consent for long-term secret storage. Persisting credentials without explicit notice increases the risk of unintended retention, later misuse, or compromise on shared systems.

External Transmission

Medium
Category
Data Exfiltration
Content
# 在此追加具体 API 调用,例如按姓名搜索用户并获取详情:
KEYWORD="张三"
SEARCH=$(curl -s -X POST https://api.dingtalk.com/v1.0/contact/users/search \
  -H "x-acs-dingtalk-access-token: $NEW_TOKEN" \
  -H 'Content-Type: application/json' \
  -d "{\"queryWord\":\"$KEYWORD\",\"offset\":0,\"size\":20}")
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
echo "搜索结果: $SEARCH"

TARGET_UID=$(echo "$SEARCH" | grep -o '"list":\["[^"]*"' | grep -o '"[^"]*"$' | tr -d '"')
DETAIL=$(curl -s -X POST "https://oapi.dingtalk.com/topapi/v2/user/get?access_token=${OLD_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d "{\"userid\":\"$TARGET_UID\",\"language\":\"zh_CN\"}")
echo "用户详情: $DETAIL"
Confidence
80% confidence
Finding
The example places the access token in the URL query string when calling the legacy DingTalk endpoint. Query-string secrets are more likely to leak through logs, proxies, browser/history equivalents, telemetry, or debugging output than header-based credentials.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The document tells users how to obtain and use both new and old DingTalk access tokens but does not warn that these credentials grant access to organizational directory data and must be treated as secrets. Without guidance against exposing tokens in logs, scripts, shell history, or chat responses, the skill materially increases the chance of credential leakage and unauthorized access to employee records.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This section documents retrieval of employee PII and employment data, including mobile numbers, email addresses, job numbers, department memberships, and role information, but provides no privacy warning, access-control expectations, or data-minimization guidance. In a skill explicitly designed for directory lookup, this omission increases the risk of over-collection, inappropriate disclosure, and downstream misuse of sensitive organizational personnel data.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The script's natural-language interface, including the title, usage/help output, and explanatory text, is entirely in Chinese. This imposes a specific language on users without any opt-in or documented locale constraint, which matches the language/locale policy violation criteria.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest describes a DingTalk contact/directory skill focused on searching users/departments and retrieving contact information. This script additionally exposes generic DingTalk platform token lifecycle management and persistent credential/config storage commands, which are broader platform-administration capabilities rather than contact-query behavior itself.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The script allows arbitrary persistent writes to the config file via --set KEY=VALUE without constraining which keys may be modified. In an agent setting, this can let untrusted input persistently alter authentication material, redirect behavior through DINGTALK_CONFIG, or poison future runs by overwriting stored secrets and tokens.

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.

External Transmission

Medium
Category
Data Exfiltration
Content
old_token=$(cmd_old_token)

  resp=$(curl -s -X POST \
    "https://oapi.dingtalk.com/topapi/v2/user/get?access_token=${old_token}" \
    -H "Content-Type: application/json" \
    -d "{\"userid\":\"${user_id}\"}")
Confidence
97% confidence
Finding
The script places the access token in the request URL query string when calling the DingTalk API. Query-string tokens can leak through process listings, shell history, proxy logs, monitoring tools, and HTTP client debug output, exposing reusable credentials beyond the intended scope.

External Transmission

Medium
Category
Data Exfiltration
Content
old_token=$(cmd_old_token)

  resp=$(curl -s -X POST \
    "https://oapi.dingtalk.com/topapi/user/getbyunionid?access_token=${old_token}" \
    -H "Content-Type: application/json" \
    -d "{\"unionid\":\"${union_id}\"}")
Confidence
97% confidence
Finding
As above, the old_token is embedded in the URL query string for the unionId-to-userId lookup. This increases the chance of credential disclosure via local process inspection and infrastructure logs, potentially enabling unauthorized API calls with the leaked token.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The example request sets `"language":"zh_CN"` in the API payload, which forces a specific locale in natural-language content. The file does not state that the locale is optional, user-selected, or required for a region-specific compliance reason.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
Multiple request examples set `language` to `zh_CN`, which imposes a specific locale in natural-language-facing API behavior. SQP-3 applies to all file types and flags forced language/locale choices unless the skill offers opt-in or clearly justifies the locale constraint; this document does neither.

Static analysis

No suspicious patterns detected.