libcurl SFTP 上传恢复功能中的TOCTOU竞争条件漏洞分析

本文详细分析了libcurl在SFTP上传恢复功能中存在的一个TOCTOU(检查时与使用时差)竞争条件漏洞。该漏洞允许已认证的攻击者通过替换符号链接,导致curl进程将数据追加到非预期的系统文件中,可能引发权限提升、远程代码执行等严重后果。

[SFTP] TOCTOU 竞争条件导致上传恢复逻辑中的任意文件追加

摘要

在 libcurl 的 SFTP 上传恢复功能中存在一个“检查时与使用时差”(TOCTOU)竞争条件漏洞。当使用 CURLOPT_RESUME_FROM 设置为 -1(相当于 curl -C - 命令行标志)恢复上传时,libcurl 首先执行一个 STAT 操作来确定远程文件的大小以及恢复上传的正确偏移量。随后,它执行一个 OPEN 操作开始写入。

STATOPEN 调用之间存在一个时间窗口。拥有 SFTP 服务器认证访问权限的攻击者可以利用此漏洞,将目标文件替换为指向系统上其他文件的符号链接。由于用于恢复操作的 OPEN 操作不使用截断标志(例如 O_TRUNC),curl 随后将寻址到先前确定的偏移量,并将数据追加到符号链接指向的目标文件中。这使得攻击者可以向用户拥有写权限但本无意修改的文件追加任意数据。

受影响版本

此漏洞在 curl 8.18.0-DEV(截至 2025 年 11 月的 master 分支)上复现。该易受攻击的逻辑似乎是 SFTP “从末尾恢复”功能(-C -)的基础,并且很可能也影响许多早期版本。 一个典型的可复现此漏洞的系统的 curl -V 输出示例如下:

1
2
3
curl 8.18.0-DEV (x86_64-pc-linux-gnu) libcurl/8.18.0-DEV OpenSSL/3.2.1 zlib/1.2.13 libssh2/1.11.0
Protocols: sftp https ...
Features: AsynchDNS libssh2 ...

复现步骤

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
  • race_server.sh
  • Vulnerable_Code_Flow_Analysis.txt

易受攻击的代码指针

漏洞的核心位于以下文件的 sftp_upload_init 函数中:

  • lib/vssh/libssh2.c
  • lib/vssh/libssh.c

具体是以下逻辑分支:

1
2
if(data->state.resume_from < 0)  performs a STAT
else if(data->state.resume_from > 0)  performs a non-truncating OPEN

影响

主要影响是完整性丧失。拥有 SFTP 服务器认证访问权限的攻击者可以利用此漏洞,向受害者用户拥有写权限的任何文件追加任意数据。

根据目标文件的不同,这可能引发更严重的后果:

  • 远程代码执行 (RCE):如果攻击者将恶意命令追加到用户的启动脚本(例如 .bashrc.profile)或由 cron 作业执行的脚本中。
  • 拒绝服务 (DoS):如果攻击者破坏了关键的系统或应用程序配置文件。
  • 日志注入/篡改:攻击者可以注入虚假的日志条目以误导管理员或隐藏其他恶意活动。

严重性等级为中等,因为利用需要攻击者事先拥有对服务器的认证访问权限,并且依赖于赢得竞争条件。

建议的缓解措施

根本原因在于缺少对打开的文件是否与检查的文件是同一个文件的验证。最稳健的解决方案是在打开文件后执行检查。

sftp_upload_init 中调用 libssh2_sftp_open_ex() 之后,应在返回的文件句柄上后续调用 libssh2_sftp_fstat_ex()。 此 fstat 调用获取的属性(例如,inode 号,如果 SFTP 服务器扩展提供的话)应与初始 stat 调用收集的属性进行比较。 如果属性不匹配,则表明文件已被交换。必须中止传输,并向用户返回错误。

讨论要点(来自报告交互)

项目维护者(bagder)与报告者(cainvsilf)就漏洞的实质影响进行了深入讨论:

关键争议点: 维护者认为,攻击者既然已经拥有在服务器上替换文件/创建符号链接的权限,本身就具备造成严重破坏的能力,curl 无法防范这种情况。报告者则认为,此漏洞的关键区别在于它提供了偏移量控制

技术区别澄清:

  • 普通的符号链接替换:只能重定向写入目标,攻击者无法控制写入的偏移量。
  • 此 TOCTOU 漏洞:由于 curl 使用了陈旧的 STAT 元数据(来自被替换前的原始文件),攻击者可以控制受害进程在目标文件中的写入偏移量。这是攻击者通常不具备的能力,构成了权限边界的跨越。

实际利用场景: 报告者强调了“困惑的副手”(Confused Deputy)问题,并举了两个例子说明其危害性:

  1. 多用户 SFTP 服务器场景:低权限攻击者诱使高权限的自动化进程(如 victim_app)向攻击者无法直接访问的关键配置文件(如 /opt/app/config.ini)在特定偏移量处写入数据,实现权限提升。
  2. 可控追加 vs. 简单破坏:普通符号链接可能导致文件被覆盖(偏移量 0)或截断,造成“破坏”。而此漏洞允许攻击者根据已知的原始文件大小,将数据精准地追加到目标文件(如 .bashrc)的特定位置(例如偏移量 1024 字节之后),从而实现可控的恶意代码注入(如追加反弹 shell 命令),而不一定破坏文件的主要功能。

关于竞争条件窗口的必要性: 报告者指出,许多实际应用在调用 curl 前会进行预检查(如检查文件大小、所有者等)。永久性的符号链接会被这些检查阻止。而竞争条件允许攻击者在 STAT 检查通过后、OPEN 发生前的一瞬间替换为符号链接,从而绕过应用层的安全检查。

项目方最终裁定

curl 项目维护团队经过评估后,将此报告状态标记为“信息性”(Informative)并关闭,认为其不构成一个安全问题

主要理由如下:

  1. 攻击前提已具备高权限:利用此漏洞要求攻击者已经存在于服务器上,并且能够在目标目录中插入恶意符号链接。这本身已经是一种攻击,curl 无法防范这种根本性的服务器端攻击。
  2. 可控性有限:攻击者虽然可以控制写入偏移量和目标文件,但无法控制上传的具体内容。实际利用需要非常了解客户端的行为,并且需要赢得精确的竞争条件。
  3. 政策排除:curl 的漏洞披露政策明确排除了“攻击者对 curl 保存文件的目录具有写访问权限”的情况。

项目方表示,虽然不认为这是一个需要修复的安全漏洞,但会致力于完善相关文档,将此类风险作为“已知风险”告知用户,相关工作已在 GitHub 的 PR #19631 中跟进。

附件

  • race_client.sh:用于重复触发 curl 中易受攻击逻辑的客户端 PoC。
  • race_server.sh:用于创建竞争条件的服务器端 PoC。
  • Vulnerable_Code_Flow_Analysis.txt:易受攻击的代码流分析。
  • perform_race.sh / setup_environment.sh:报告者后期提供的更清晰、完全可复现的演示脚本,用于展示权限提升场景。
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计