T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/handle-stop.sh:176
- Finding
- TOCTOU Symlink Race in Stop-Flag Creation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/handle-stop.sh`, lines 176–193 **Vulnerability Type**: Predictable temporary file and time-of-check/time-of-use symlink race **Risk Level**: High ### Vulnerable Code ```bash # 安全检查:拒绝符号链接(防止覆盖任意文件攻击) if [ -L "${FLAG_FILE}" ]; then echo "[STOP] 安全拒绝: FLAG_FILE是符号链接: ${FLAG_FILE}" audit_log "REJECTED" "N/A" "Symlink FLAG_FILE rejected" exit 1 fi cat > "${FLAG_FILE}" << EOF { "sessionId": "${SESSION_ID}", "timestamp": $(date +%s%3N), "reason": "${REASON_ESCAPED}", "signal": "SIGINT", "createdBy": "${CURRENT_USER}", "version": "1.0.2" } EOF chmod 0600 "${FLAG_FILE}" ``` ### Technical Analysis The stop flag uses a predictable path under the shared `/tmp` directory. Although the script rejects a symbolic link before writing, the symbolic-link check and the subsequent file creation are separate filesystem operations. A local attacker can replace the checked path with a symbolic link after the `-L` test succeeds but before the shell processes the output redirection. This is a classic time-of-check/time-of-use race. Shell redirection follows symbolic links, so the destination file would be opened and truncated using the privileges of the account running the skill. The subsequent `chmod 0600` can also follow the substituted path and modify the target file's permissions. ### Attack Path 1. The attacker predicts or learns the session ID. 2. The attacker monitors `/tmp/agent-stop-<session-id>.flag`. 3. The interruption script checks that the path is not a symbolic link. 4. Before the `cat > "${FLAG_FILE}"` redirection occurs, the attacker creates or replaces that path with a symbolic link to a victim file. 5. The script follows the symbolic link and truncates or overwrites the victim file. 6. The script may set the victim file's permissions to `0600`. Successful exploitation requires local filesystem access and the ability to win the race. The target must be writable by the accou ...[truncated 528 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Store all runtime state in a private per-user directory, such as `${XDG_RUNTIME_DIR}/task-interrupt-pro`, after verifying that the directory is owned by the current user and has mode `0700`. 2. Create the flag with exclusive, no-follow semantics equivalent to `open(O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600)`. 3. Do not rely on a separate `-L` check because it cannot make a later write atomic. 4. Write the data to a securely created temporary file in the same private directory and atomically rename it to the final path. 5. Verify the opened object with `fstat` before writing, rather than validating only the pathname. 6. Avoid running this process-management helper with elevated privileges unless strictly necessary. ]]>
