[SFTP] TOCTOU竞争条件漏洞分析:上传恢复逻辑导致任意文件附加
报告摘要
一个时间检查与使用(TOCTOU)竞争条件存在于libcurl的SFTP上传恢复功能中。当使用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)。 - 如果漏洞利用成功,客户端有效载荷文件的内容将被附加到敏感文件中。
受影响的代码流分析
漏洞的核心位于以下文件的sftp_upload_init函数内:
lib/vssh/libssh2.clib/vssh/libssh.c
具体是以下逻辑分支:
if(data->state.resume_from < 0)→ 执行一个STAT操作else if(data->state.resume_from > 0)→ 执行一个非截断的OPEN操作
影响
主要影响是完整性丧失。拥有SFTP服务器认证访问权限的攻击者可以利用此漏洞,向受害者用户具有写权限的任何文件附加任意数据。
根据目标文件的不同,这可能导致更严重的后果:
- 远程代码执行(RCE):如果攻击者将恶意命令附加到用户的启动脚本(例如
.bashrc、.profile)或由cron作业执行的脚本。 - 拒绝服务(DoS):如果攻击者破坏了关键的系统或应用程序配置文件。
- 日志注入/篡改:攻击者可以注入虚假的日志条目以误导管理员或隐藏其他恶意活动。
该漏洞的严重性被评为中危,因为利用它需要攻击者事先拥有服务器的认证访问权限,并且依赖于赢得竞争条件。
建议的缓解措施
根本原因在于缺少对打开的文件与之前检查的文件是否为同一文件的验证。最稳健的解决方案是在文件被打开后执行一个检查。
在sftp_upload_init中调用libssh2_sftp_open_ex()之后,应对返回的文件句柄进行后续的libssh2_sftp_fstat_ex()调用。
来自此fstat调用的属性(例如inode号,如果SFTP服务器扩展提供的话)应与初始stat调用收集到的属性进行比较。
如果属性不匹配,则表明文件已被交换。必须中止传输,并向用户返回错误。
相关讨论节选
攻击者 vs 项目维护者讨论要点:
- 维护者观点:如果攻击者拥有在服务器上替换文件/创建符号链接的权限,那么他们已经可以造成大量破坏。curl无法防范这种已具备相应权限的攻击者。
- 报告者观点:关键区别在于,普通的符号链接替换允许攻击者重定向写入目标,但不提供偏移量控制。而此TOCTOU漏洞通过使用过时的STAT元数据,使得攻击者可以让受害者的进程在攻击者无法直接访问的文件中以可预测的偏移量进行写入。这是在跨越一个权限边界。
- 报告者进一步解释:竞争条件的作用在于,它允许绕过那些通常会在操作前进行校验(如检查文件是否存在、大小、所有者等)的应用程序级安全检查。STAT操作看到了一个合法的文件,而符号链接是在STAT之后才被交换进来的,这使得curl可以继续执行那些本应被阻止的操作。
- 维护者结论:利用此漏洞需要攻击者已经存在于服务器上,并且需要对客户端有深入的了解,还需要一定的运气(赢得竞争)。这使已有的攻击手段变得更糟,但并非一个根本性的安全问题。curl无法保护用户免受那些能够在服务器目标目录中替换文件和插入符号链接的攻击者的侵害。
最终,项目维护者将此报告状态标记为 Informative(信息性),认为其本身不构成安全问题,原因在于漏洞利用的前提是攻击者已具备在服务器上进行文件替换的能力,而这本身已构成攻击,且非curl所能防范。他们计划在相关的"已知风险"文档中加入对此类情况的说明。