libcurl SMB协议堆缓冲区越界读取漏洞分析

本文详细分析了libcurl SMB协议实现中的一个堆缓冲区越界读取漏洞。恶意SMB服务器可发送特制的READ_ANDX响应,导致curl读取并输出高达28KB的未初始化堆内存,造成敏感信息泄露。

curl漏洞报告 #3470095 - 通过恶意SMB服务器READ_ANDX响应实现堆缓冲区越界读取

时间线

  • strokep 向curl提交报告(12天前)
  • bagder (curl staff) 评论:“感谢您的报告!我们将花时间调查您的报告,并尽快向您反馈详细信息。”
  • strokep 澄清漏洞细节,修改分类为CWE-20(输入验证不当)
  • icing (curl staff) 评论:“这不是’堆缓冲区越界读取’,它只是读取服务器作为填充发送的数据。”
  • bagder 将报告状态更改为"不适用"并关闭
  • strokep 进一步解释该问题的安全影响,提供新的概念验证(PoC)
  • jimfuller2024 (curl staff) 运行测试并评论:“没有发现任何安全问题…”
  • bagder 同意披露此报告(10天前)
  • 报告被披露(10天前)

漏洞描述

概要

在libcurl的SMB协议实现中发现了一个堆缓冲区越界读取漏洞。恶意SMB服务器可以发送特制的READ_ANDX响应,导致curl每次请求读取并输出高达28KB的未初始化堆内存。这会导致攻击者的信息泄露。

该漏洞存在于lib/smb.c文件的smb_request_state()函数中,具体位置在第1110-1137行的SMB_DOWNLOAD状态处理器。

技术分析

易受攻击的代码 (lib/smb.c 第1116-1128行)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
case SMB_DOWNLOAD:
    if(h->status || smbc->got < sizeof(struct smb_header) + 15) {
      req->result = CURLE_RECV_ERROR;
      next_state = SMB_CLOSE;
      break;
    }

    // [1] 直接从攻击者控制的服务器响应中读取的值
    len = Curl_read16_le(((const unsigned char *)msg) +
                         sizeof(struct smb_header) + 11);
    off = Curl_read16_le(((const unsigned char *)msg) +
                         sizeof(struct smb_header) + 13);

    if(len > 0) {
      // [2] 边界检查针对smbc->got(接收的总字节数)进行验证
      if(off + sizeof(unsigned int) + len > smbc->got) {
        failf(data, "Invalid input packet");
        result = CURLE_RECV_ERROR;
      }
      else
        // [3] 越界读取发生在此处 - 读取堆内存
        result = Curl_client_write(data, CLIENTWRITE_BODY,
                                   (char *)msg + off + sizeof(unsigned int),
                                   len);

根本原因

  1. 攻击者控制NetBIOS头部长度字段,允许他们声明一个大的消息大小(例如0x7100字节)
  2. 攻击者用空字节填充响应,使smbc->got匹配声明的大小
  3. 攻击者设置data_offsetdata_length以读取接收缓冲区内的任意位置
  4. 由于off + 4 + len <= smbc->got,[2]处的边界检查通过
  5. curl从msg + off + 4读取len字节,其中包括超出实际SMB响应数据的未初始化堆内存

结果:当服务器仅发送256字节的合法数据时,curl输出约28KB数据。额外的约28KB是泄露的堆内存。

受影响版本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ curl -V
curl 8.15.0 (x86_64-pc-linux-gnu) libcurl/8.15.0 OpenSSL/3.5.4 zlib/1.3.1 
brotli/1.1.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.2 libssh2/1.11.1 
nghttp2/1.64.0 nghttp3/1.12.0 librtmp/2.3 OpenLDAP/2.6.10
Release-Date: 2025-07-16
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs 
ipns ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps 
telnet tftp ws wss
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTP3 HTTPS-proxy 
IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP 
UnixSockets zstd

Platform: Linux x86_64 (Kali GNU/Linux Rolling)

启用了SMB支持(定义了USE_CURL_NTLM_CORE)的所有curl版本都可能受到影响。

复现步骤

  1. 将附加的PoC文件解压到一个目录
  2. 运行自动化漏洞利用脚本:
    1
    2
    
    chmod +x run_exploit.sh
    ./run_exploit.sh
    
  3. 或者手动操作:
    • 终端1 - 启动恶意服务器:
      1
      
      python3 smb_exploit_server.py 4455
      
    • 终端2 - 使用curl连接:
      1
      
      curl -u anyuser:anypass -o leaked.bin smb://127.0.0.1:4455/share/file.txt
      
    • 终端2 - 验证泄露:
      1
      2
      3
      4
      5
      
      ls -la leaked.bin
      # 显示:28672字节(服务器仅发送了256字节)
      
      xxd leaked.bin | head -20
      # 前256字节是'A'(0x41),其余是堆内存
      

预期输出

1
2
3
4
5
6
7
8
9
$ ls -la leaked.bin
-rw-rw-r-- 1 user user 28672 Dec 18 13:00 leaked.bin

$ xxd leaked.bin | head -20
00000000: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
00000010: 4141 4141 4141 4141 4141 4141 4141 4141  AAAAAAAAAAAAAAAA
... (256字节的'A')
00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................
... (堆内存 - 泄露28416字节)

支持材料/参考

影响

影响

严重程度:中(CVSS 6.5) CWE-125:越界读取

运行恶意SMB服务器的远程攻击者可以利用此漏洞实现:

  1. 堆内存泄露 攻击者可以从curl客户端进程的每个SMB READ请求中泄露高达28KB的堆内存。此内存可能包含:

    • 来自先前HTTP/FTP请求的身份验证凭据
    • 会话令牌和API密钥
    • 来自其他网络操作的私有数据
    • 对进一步利用有用的内存布局信息
  2. 攻击向量

    • 网络钓鱼:向受害者发送指向smb://attacker-server/share/file.txt的链接

    • 中间人攻击:拦截SMB连接并注入恶意响应

    • 基础设施被攻陷:攻击者攻陷内部SMB服务器

  3. 利用要求

    • 受害者必须连接到攻击者控制的SMB服务器
    • curl必须编译有SMB支持(在Linux发行版中很常见)
    • 无需身份验证 - 服务器接受任何凭据
    • 除了点击链接外,无需用户交互
    • 攻击是静默的 - 受害者不会收到错误消息
  4. 利用证明 附加的PoC演示了:

    • 服务器发送256字节的合法数据
    • curl接收并输出28,672字节
    • 泄露28,416字节的堆内存

建议的修复

验证data_offset指向实际的SMB READ_ANDX响应结构内,而不仅仅是总接收字节内。应针对预期的消息布局和byte_count字段检查偏移量。

附件

  • run_exploit.sh (F5131384)
  • smb_exploit_server.py (F5131385)
  • leaked.bin (F5131386)

后续讨论与PoC

在报告被标记为"不适用"后,提交者提供了更详细的分析和额外的概念验证,说明该问题可能导致应用程序级的缓冲区溢出:

合法READ响应

  • SMB头部 + 响应:59字节
  • 实际文件数据:100字节
  • 总计:159字节
  • data_offset = 59
  • data_length = 100

恶意READ响应

  • SMB头部 + 响应:59字节
  • 实际文件数据:100字节
  • 填充:28000字节 ← 攻击者添加
  • 总计:28159字节
  • data_offset = 59
  • data_length = 28096 ← 攻击者在此处撒谎

检查off + 4 + len <= smbc->got 59 + 4 + 28096 = 28159 <= 28159 通过

问题: 检查验证了offset+length是否在接收的字节内,但没有验证data_length是否匹配:

  • NT_CREATE响应中的实际文件大小
  • SMB READ响应中的byte_count字段
  • 任何合理的SMB协议约束

合法的服务器设置data_length = 实际文件字节数。 恶意服务器设置data_length = 它想要的任何值(直到缓冲区大小)。 curl信任攻击者控制的data_length,并将所有这些数据输出为"文件内容" - 包括攻击者的填充,其中可能包含精心制作的恶意数据,以利用下游文件消费者。

提交者提供了第二个PoC(smb_overflow_server.pyvulnerable_app.crun_overflow_poc.sh),演示了"大小不匹配"攻击如何可能导致依赖curl报告的文件大小来分配缓冲区的应用程序发生缓冲区溢出。

curl开发人员的回应

bagder

“请停止这些巨大的文本转储。它们没有帮助。这里的第一个案例似乎是同一个问题陈述又重复了一遍?我们已经驳斥了它。第二个是您发布的应用程序中的问题,而不是libcurl中的问题。应用程序是分配缓冲区并使其溢出的那个。”

jimfuller2024

“运行curl客户端漏洞利用示例对抗邪恶的SMB服务器…没有看到任何安全问题…也许我遗漏了什么。”

最终,curl团队认为这不是一个安全问题,但同意披露此报告以供社区审查。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计