curl · Report #3432833
[SFTP] TOCTOU Race Condition in Upload Resume Logic Leads to Arbitrary File Append
How it was found
Summary:
A Time-of-check to Time-of-use (TOCTOU) race condition exists in the SFTP upload resume functionality of libcurl. When resuming an upload with CURLOPT_RESUME_FROM set to -1 (the equivalent of the curl -C - command-line flag), libcurl first performs a STAT operation to determine the remote file's size and the correct offset to resume from. Subsequently, it performs an OPEN operation to begin writing.
A window of time exists between the STAT and OPEN calls. An attacker with authenticated access to the SFTP server can exploit this by replacing the target file with a symbolic link to a different file on the system. Because the OPEN operation for a resume operation does not use a truncation flag (e.g., O_TRUNC), curl will then proceed to seek to the previously determined offset and append data to the symlink's target file. This allows an attacker to append arbitrary data to files the user has write access to, but did not intend to modify.
Affected version
This was reproduced on curl 8.18.0-DEV (the master branch as of November 2025). The vulnerable logic appears to be fundamental to the SFTP resume-from-end feature (-C -) and likely affects many previous versions as well.
A typical curl -V output for a system where this can be reproduced would be: curl 8.18.0-DEV (x86_64-pc-linux-gnu) libcurl/8.18.0-DEV OpenSSL/3.2.1 zlib/1.2.13 libssh2/1.11.0 Protocols: sftp https ... Features: AsynchDNS libssh2 ...
Steps To Reproduce:
-
Setup the Environment: * An SFTP server is required. For local testing, openssh-server can be used on a Linux machine, allowing connections to localhost. * The attacker needs an authenticated user account on the server with write permissions in a specific directory. * The victim is a curl user who will upload a file to this specific directory using the resume flag.
-
Prepare the Attack Scripts:
- Create the server-side script race_server.sh on the SFTP server. This script will rapidly swap the target file with a symlink to a sensitive file.
- Create the client-side script race_client.sh on the client machine. This script will repeatedly attempt the curl upload to trigger the race condition.
-
Execute the Attack:
- On the server, run ./race_server.sh. It will begin creating and deleting the symlink in a tight loop.
- On the client, configure race_client.sh with the correct credentials and paths, then run ./race_client.sh. It will start sending upload requests.
- Let both scripts run for 15-30 seconds. The client will show some "No such file or directory" errors, which is expected.
-
Verify the Result:
- Stop both scripts (Ctrl+C).
- On the server, inspect the contents of the sensitive file targeted by the symlink (e.g., /tmp/test_vuln.log).
- If the exploit was successful, the contents of the client's payload file will be appended to the sensitive file.
Supporting Material/References:
-
race_client.sh (F5023270: race_client.sh)
-
race_server.sh (F5023271: race_server.sh)
-
Vulnerable Code Flow Analysis.txt (F5023279: Vulnerable Code Flow Analysis.txt)
Vulnerable Code Pointer
The core of the vulnerability is in the sftp_upload_init function within:
lib/vssh/libssh2.clib/vssh/libssh.c
Specifically the logic branch:
if(data->state.resume_from < 0)→ performs a STATelse if(data->state.resume_from > 0)→ performs a non-truncating OPEN
Attached PoC Scripts
race_server.sh: Server-side PoC to create the race condition.race_client.sh: Client-side PoC used to repeatedly trigger the vulnerable logic in curl.
Impact
The primary impact is a loss of Integrity. An attacker with authenticated access to an SFTP server can leverage this vulnerability to append arbitrary data to any file that the victim user has write permissions for.
This can lead to further, more severe impacts depending on the targeted file:
- Remote Code Execution (RCE): If the attacker appends malicious commands to a user's startup script (e.g., .bashrc, .profile) or a script executed by a cron job.
- Denial of Service (DoS): If the attacker corrupts a critical system or application configuration file.
- Log Injection / Tampering: The attacker could inject false log entries to mislead administrators or hide other malicious activity.
The severity is rated as Medium, as exploitation requires the attacker to have prior authenticated access to the server and relies on winning a race condition.
Suggested Mitigation
The root cause is the lack of verification that the file opened is the same one that was checked. The most robust solution is to perform a check after the file is opened.
1. After libssh2_sftp_open_ex() is called in sftp_upload_init, a subsequent call to libssh2_sftp_fstat_ex() should
be made on the returned file handle.
2. The attributes from this fstat call (e.g., inode number, if the SFTP server extension provides it) should be
compared to the attributes gathered from the initial stat call.
3. If the attributes do not match, it indicates the file was swapped. The transfer must be aborted, and an error
should be returned to the user.