[SFTP] TOCTOU 竞态条件漏洞:上传续传逻辑导致任意文件追加
报告摘要
libcurl 的 SFTP 上传续传功能中存在一个“检查-使用”时间差(TOCTOU)竞态条件漏洞。
当使用 CURLOPT_RESUME_FROM 设置为 -1(相当于命令行标志 curl -C -)来恢复上传时,libcurl 首先执行一个 STAT 操作来确定远程文件的大小和正确的恢复偏移量。随后,它执行一个 OPEN 操作来开始写入。
在 STAT 和 OPEN 调用之间存在一个时间窗口。对 SFTP 服务器具有认证访问权限的攻击者可以利用此漏洞,将目标文件替换为指向系统上其他文件的符号链接。由于恢复操作的 OPEN 未使用截断标志(例如 O_TRUNC),curl 随后会定位到先前确定的偏移量,并将数据追加到符号链接指向的目标文件中。这使得攻击者能够将任意数据追加到用户具有写入权限(但无意修改)的文件中。
受影响版本
此漏洞在 curl 8.18.0-DEV(截至 2025 年 11 月的 master 分支)上重现。易受攻击的逻辑似乎是 SFTP “从末尾恢复”功能(-C -)的基础组成部分,并且很可能也影响许多早期版本。
可重现此漏洞的典型 curl -V 输出示例如下:
|
|
复现步骤
1. 环境设置
- 需要一个 SFTP 服务器。对于本地测试,可以在 Linux 机器上使用
openssh-server,允许连接到localhost。 - 攻击者需要在服务器上拥有一个经过身份验证的用户帐户,并在特定目录中具有写入权限。
- 受害者是将在该特定目录中使用续传标志上传文件的 curl 用户。
2. 准备攻击脚本
- 在 SFTP 服务器上创建服务端脚本
race_server.sh。该脚本将快速将目标文件与指向敏感文件的符号链接进行交换。 - 在客户端机器上创建客户端脚本
race_client.sh。该脚本将重复尝试 curl 上传以触发竞态条件。
3. 执行攻击
- 在服务器上运行
./race_server.sh。它将开始在一个紧密循环中创建和删除符号链接。 - 在客户端上,使用正确的凭据和路径配置
race_client.sh,然后运行./race_client.sh。它将开始发送上传请求。 - 让两个脚本运行 15-30 秒。客户端会显示一些“No such file or directory”错误,这是预期的。
4. 验证结果
- 停止两个脚本(Ctrl+C)。
- 在服务器上,检查符号链接所指向的敏感文件(例如
/tmp/test_vuln.log)的内容。 - 如果攻击成功,客户端 payload 文件的内容将被追加到该敏感文件中。
漏洞代码流程分析
漏洞的核心位于 lib/vssh/libssh2.c 和 lib/vssh/libssh.c 文件中的 sftp_upload_init 函数内。具体逻辑分支如下:
|
|
影响
主要影响是 完整性破坏。对 SFTP 服务器具有认证访问权限的攻击者可利用此漏洞将任意数据追加到受害用户具有写入权限的任何文件中。
根据目标文件的不同,可能导致更严重的后续影响:
- 远程代码执行 (RCE):如果攻击者将恶意命令追加到用户的启动脚本(例如
.bashrc、.profile)或由 cron 作业执行的脚本中。 - 拒绝服务 (DoS):如果攻击者破坏了关键系统或应用程序配置文件。
- 日志注入/篡改:攻击者可以注入虚假日志条目以误导管理员或隐藏其他恶意活动。
该漏洞被评为 中危 (Medium),因为利用需要攻击者事先具有对服务器的认证访问权限,并且依赖于赢得竞态条件。
建议的缓解措施
根本原因是在打开文件后,没有验证所打开的文件与之前检查的是否是同一个文件。最稳健的解决方案是在打开文件后执行检查。
在 sftp_upload_init 中调用 libssh2_sftp_open_ex() 之后,应对返回的文件句柄进行后续的 libssh2_sftp_fstat_ex() 调用。
应比较来自此 fstat 调用的属性(例如,如果 SFTP 服务器扩展提供了 inode 号)与初始 stat 调用收集的属性。
如果属性不匹配,则表明文件已被交换。必须中止传输,并向用户返回错误。
讨论与澄清
curl 开发者反馈: 攻击者已经在服务器上拥有这些权限,可以通过替换目标文件为符号链接来造成严重破坏,curl 无法防范这种情况。这只是一个让现有攻击变得更糟的问题。
报告者澄清: 正常的符号链接替换仅能重定向写入操作。它不允许攻击者控制写入偏移量。而此 TOCTOU 漏洞利用了 curl 恢复逻辑中的陈旧元数据(来自先前的 STAT 调用),使得攻击者可以控制写入偏移量,从而能够进行可预测的、受控的追加操作。这是通常的符号链接滥用所无法实现的。
漏洞的独特之处在于:
- 偏移量控制:基于原始文件大小的陈旧元数据,攻击者可以控制数据在目标文件中的写入位置。
- 绕过验证:许多应用程序在调用 curl 之前会进行预检查(例如,检查文件大小、所有者等)。竞态条件窗口允许攻击者在
STAT检查通过后、OPEN发生前进行文件交换,从而绕过这些安全检查。 - 权限边界穿越:在涉及权限分离的场景中(例如,低权限用户和高权限自动化进程共享目录),低权限攻击者可以利用此漏洞迫使高权限进程向攻击者无法直接访问的敏感文件写入数据,实现权限提升。
最终,curl 项目方认为此问题 不是一个安全问题,因为其前提是攻击者已经能够在服务器上插入恶意符号链接,这本身已经是一种攻击。curl 的行为可能会使攻击变得更糟,但这已经是 curl 无法防范的攻击。该报告状态被标记为 信息性 (Informative)。