T09 · Insecure Skill Coding Practices
- Location
scripts/download.sh:21- Finding
Symlink and TOCTOU-Based Arbitrary File Overwrite
- Content
View full analysis
Vulnerability Details
File Location:
scripts/download.sh, lines 21–36 and 43–45
Vulnerability Type: Symlink following and time-of-check/time-of-use race
Risk Level: MediumVulnerable Code
bash # Check if output file already exists if [ -e "$OUTPUT_PATH" ]; then echo "ERROR: Output path already exists: $OUTPUT_PATH" echo "Please choose a different output path or confirm overwrite." exit 1 fi # Ensure output directory exists OUTPUT_DIR=$(dirname "$OUTPUT_PATH") if [ ! -d "$OUTPUT_DIR" ]; then mkdir -p "$OUTPUT_DIR" echo "Created output directory: $OUTPUT_DIR" fi echo "Downloading: $FILE_HASH" echo "Destination: $OUTPUT_PATH" # Try SPFS HTTP API first try_spfs_download() { local http_code http_code=$(curl -s -w "%{http_code}" -X POST \ "${SPFS_GATEWAY}/api/v0/get?arg=${FILE_HASH}" \ -o "$OUTPUT_PATH" 2>/dev/null)Technical Analysis
The script checks whether the destination exists and later asks
curlto open that path. These are separate filesystem operations, creating a time-of-check/time-of-use race.The check also uses
test -e, which ordinarily returns false for a dangling symbolic link. Consequently, a dangling symlink can pass the existence check. Whencurl -osubsequently opens the destination, it follows the symbolic link and writes the downloaded content to its target.Even when no symlink exists during the check, an attacker with write access to the destination directory can replace the checked path with a symbolic link before
curlopens it. Quoting"$OUTPUT_PATH"prevents shell word splitting and command injection, but it does not prevent symbolic-link traversal or the race.Attack Path
- A victim invokes the download script with an output path located in a directory writable by an attacker.
- The attacker creates a dangling symbolic link at the requested output path that points to another file writable by the victim or Agent. Alternatively, the attacker ...[truncated 1096 chars]
- Remediation
View remediation
Remediation Suggestions
- Reject symbolic-link destinations explicitly with
[ -L "$OUTPUT_PATH" ], while recognizing that this check alone does not eliminate the race. - Restrict downloads to a trusted directory that is not writable by other users.
- Create a temporary file securely in the destination directory using
mktemp, with restrictive permissions and a trusted fixed directory. - Download into the secure temporary file rather than directly into the final path.
- Validate the completed download, including the HTTP status and expected content integrity where such metadata is available.
- Install the temporary file at the destination using an atomic no-clobber operation. Ensure that the operation fails if the destination already exists and does not follow symbolic links.
- Clean up the temporary file through a shell
trapon normal exit and signals. - Validate that the destination directory itself is not a symlink and remains within the approved download root after canonicalization.
- If implementation portability permits, use a helper that opens the final destination with operating-system flags equivalent to
O_CREAT | O_EXCL | O_NOFOLLOW.
A safer workflow is:
bash OUTPUT_DIR=$(dirname -- "$OUTPUT_PATH") if [ -L "$OUTPUT_PATH" ] || [ -e "$OUTPUT_PATH" ]; then echo "ERROR: Output path already exists or is a symbolic link: $OUTPUT_PATH" exit 1 fi TEMP_FILE=$(mktemp "${OUTPUT_DIR}/.stratos-download.XXXXXX") trap 'rm -f -- "$TEMP_FILE"' EXIT HUP INT TERM http_code=$(curl -sS -w "%{http_code}" -X POST \ "${SPFS_GATEWAY}/api/v0/get?arg=${FILE_HASH}" \ -o "$TEMP_FILE") if [ "$http_code" != "200" ] || [ ! -s "$TEMP_FILE" ]; then echo "ERROR: Download failed" exit 1 fi # Use a platform-appropriate atomic no-clobber installation mechanism here.The final installation step must preserve the no-overwrite guarantee atomically; merely repeating an existence check before
mvis insufficient.- Reject symbolic-link destinations explicitly with
