[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月的主分支)上复现。易受攻击的逻辑似乎是SFTP从末端恢复功能(-C -)的基础,很可能影响许多先前版本。
典型的可复现系统的curl -V输出为:
|
|
复现步骤
环境设置
- 需要SFTP服务器。对于本地测试,可以在Linux机器上使用openssh-server,允许连接到localhost
- 攻击者需要在服务器上拥有认证用户账户,并在特定目录中具有写入权限
- 受害者是使用恢复标志将文件上传到此特定目录的curl用户
准备攻击脚本
- 在SFTP服务器上创建服务器端脚本
race_server.sh,该脚本将快速将目标文件与指向敏感文件的符号链接交换 - 在客户端机器上创建客户端脚本
race_client.sh,该脚本将重复尝试curl上传以触发竞争条件
执行攻击
- 在服务器上运行
./race_server.sh,它将在紧密循环中开始创建和删除符号链接 - 在客户端上,使用正确的凭据和路径配置
race_client.sh,然后运行./race_client.sh,它将开始发送上传请求 - 让两个脚本运行15-30秒,客户端将显示一些"没有这样的文件或目录"错误,这是预期的
验证结果
- 停止两个脚本(Ctrl+C)
- 在服务器上,检查符号链接目标的敏感文件内容(例如
/tmp/test_vuln.log) - 如果利用成功,客户端有效负载文件的内容将被追加到敏感文件中
漏洞代码分析
漏洞核心位于以下文件的sftp_upload_init函数中:
lib/vssh/libssh2.clib/vssh/libssh.c
具体逻辑分支:
|
|
影响
主要影响是完整性丧失。具有SFTP服务器认证访问权限的攻击者可以利用此漏洞将任意数据追加到受害者用户具有写入权限的任何文件。
根据目标文件的不同,这可能导致更严重的影响:
- 远程代码执行(RCE):如果攻击者将恶意命令追加到用户的启动脚本(如
.bashrc、.profile)或由cron作业执行的脚本中 - 拒绝服务(DoS):如果攻击者损坏关键系统或应用程序配置文件
- 日志注入/篡改:攻击者可以注入虚假日志条目以误导管理员或隐藏其他恶意活动
严重性评级为中等,因为利用需要攻击者事先具有服务器认证访问权限,并依赖于赢得竞争条件。
建议缓解措施
根本原因是在打开文件后缺乏验证打开的文件与检查的文件是否相同。最强大的解决方案是在文件打开后执行检查。
在sftp_upload_init中调用libssh2_sftp_open_ex()后,应对返回的文件句柄进行后续调用libssh2_sftp_fstat_ex()。
应将此fstat调用的属性(例如inode编号,如果SFTP服务器扩展提供)与从初始stat调用收集的属性进行比较。
如果属性不匹配,表明文件已被交换。必须中止传输,并向用户返回错误。
讨论要点
在漏洞报告讨论中,主要争议点包括:
- 攻击前提:攻击者需要已具有服务器访问权限并能在目标目录创建符号链接
- 实际影响:虽然攻击者已具有一定破坏能力,但此漏洞提供了额外的偏移量控制能力
- 修复可行性:SFTP协议在STAT调用中不暴露inode或类似信息,客户端无法确保两个后续调用的结果是针对相同的基础文件
最终,curl团队认为这不是安全漏洞,因为curl无法保护用户免受已在服务器上存在且能够替换原始目标文件的攻击者的攻击。