cURL FTP协议状态机无限循环漏洞分析

本文详细分析了一个cURL项目FTP功能中的无限循环漏洞。当cURL尝试从恶意的FTP服务器下载文件时,特定的服务器响应序列会触发其内部状态机陷入无休止的循环,导致进程无法正常退出,造成CPU资源占用。

报告 #3442060 - cURL项目中状态机的无限循环问题

报告者: kak1 报告对象: cURL 提交时间: 8天前

摘要

漏洞影响:当cURL尝试从恶意的FTP服务器下载文件时,会触发代码执行中的无限循环。 我在cURL项目的FTP功能中发现了这个问题。

技术细节

根据 cURL文档,cURL默认使用EPSV模式作为FTP文件传输方法。简单来说,EPSV模式的工作流程是:FTP服务器打开一个TCP端口并等待客户端连接,然后通过该端口向客户端发送数据。

在cURL的状态机中,/lib/multi.c文件中的state_performing函数在[1]处调用Curl_sendrecv函数从对等方接收数据:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
static CURLMcode state_performing(struct Curl_easy *data,
                                  struct curltime *nowp,
                                  bool *stream_errorp,
                                  CURLcode *resultp)
{
...
  /* read/write data if it is ready to do so */
  result = Curl_sendrecv(data, nowp); // [1] call Curl_sendrecv
...
}

Curl_sendrecv函数在[2]处调用sendrecv_dl

1
2
3
4
5
6
7
CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp)
{
...
  if(k->keepon & KEEP_RECV) {
    result = sendrecv_dl(data, k); //[2] call sendrecv_dl
...
}

sendrecv_dl函数进一步调用xfer_recv_resp来接收数据。如果我们的恶意FTP服务器打开一个EPSV端口但不向客户端发送任何数据,xfer_recv_resp将返回-1,并且result的值将被设置为CURLE_AGAIN。随后,在[4]处,result被重置为CURLE_OK。这意味着即使xfer_recv_resp未能接收到任何数据,sendrecv_dl函数仍然返回CURLE_OK

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
static CURLcode sendrecv_dl(struct Curl_easy *data,
                            struct SingleRequest *k)
{
...
    nread = xfer_recv_resp(data, buf, bytestoread, is_multiplex, &result); // [3] call xfer_recv_resp
    if(nread < 0) {
      if(CURLE_AGAIN != result)
        goto out; /* real error */
      rcvd_eagain = TRUE;
      result = CURLE_OK; //[4]set result to CURLE_OK
...
}

继续查看state_performing函数: 由于没有接收到数据,data->req.done标志保持为0。此外,因为resultCURLE_OK,在[5][6][7]处的条件检查结果都为假。代码随后执行到[8]处(not errored, not done)。

这导致cURL的当前状态机标志(data->mstate)保持不变,仍然设置为MSTATE_PERFORMING。随后,cURL代码反复进入state_performing函数,导致无限循环。

受影响版本

1
2
3
4
5
➜  src ./curl -V
curl 8.17.0-DEV (x86_64-pc-linux-gnu) libcurl/8.17.0-DEV zlib/1.2.11 libpsl/0.19.1
Release-Date: [unreleased]
Protocols: dict file ftp gopher http imap ipfs ipns mqtt pop3 rtsp smtp telnet tftp ws
Features: alt-svc AsynchDNS IPv6 Largefile libz PSL threadsafe UnixSockets

复现步骤

  1. 下载附件 ./ftp_poc.py 并运行:sudo python3 ./ftp_poc.py。这将在当前机器的21端口启动一个恶意FTP服务。
  2. 运行以下curl命令,将命令中的192.168.23.1替换为你自己的恶意FTP服务器地址:
    1
    
    ./curl -u anonymous:123 'ftp://192.168.23.1/test' -o ./test
    
    curl程序将进入无限代码循环,并且不会自行退出。

影响

总结:当cURL尝试从恶意的FTP服务器下载文件时,会触发代码执行中的无限循环。

时间线与讨论

  • bagder (cURL staff) 发表评论 (8天前): 感谢报告。我们将花些时间调查并尽快回复细节和可能的后续问题!很可能在24小时内。我们总是努力尽快修复报告的问题。
  • bagder (cURL staff) 发表评论 (8天前): @kak1 这与服务器只是停止发送任何数据,并且cURL永远等待更多数据(除非给出额外的超时选项)有什么不同?这似乎是cURL设计的工作方式?
  • dgustafsson (cURL staff) 发表评论 (8天前): 如果文件传输是无限的,那么cURL的正确行为就是无限期地下载它。
  • kak1 发表评论 (8天前): 不同之处在于,在这个问题中,即使手动设置了超时,curl进程也不会终止。不过,我没有在循环中找到任何内存分配代码,因此不会有内存泄漏。目前,它只能导致CPU使用。如果此类问题不被视为安全漏洞,请关闭此报告。
  • bagder (cURL staff) 发表评论 (8天前): (针对“即使手动设置了超时,curl进程也不会终止”的回复)这对于我针对你的POC服务器运行的cURL来说并不匹配。它可以正常超时:
    1
    2
    3
    4
    5
    6
    
    $ curl ftp://localhost:9021/ -v -m2
    ...
    curl: (28) Operation timed out after 2002 milliseconds with 0 bytes received
    $ curl ftp://localhost:9021/RE -v -m2
    ...
    curl: (28) Operation timed out after 2002 milliseconds with 0 out of 4 bytes received
    
  • kak1 发表评论 (8天前): 请使用我提供的PoC文件和命令,因为触发此问题需要特定的协议交互。我只是为此目的调整了提供的命令。
  • kak1 发表评论 (8天前): 添加了 -m 2 参数后,curl超时了。看来这个问题是可以缓解的。请关闭此报告。
  • bagder (cURL staff) 发表评论 (更新于8天前): 你需要使用 -m (--max-time)。--connect-timeout 对你的情况不起作用,因为它能及时连接。
  • bagder (cURL staff) 关闭报告并将状态更改为“不适用” (8天前): 认为不是安全问题。
  • bagder (cURL staff) 请求披露此报告 (8天前): 根据项目的透明度政策,我们希望所有报告都被披露并公开。
  • bagder (cURL staff) 披露了此报告 (8天前)

报告于: November 26, 2025, 8:34am UTC 报告者: kak1 报告给: curl 严重性: 无评级 (—) 披露于: November 26, 2025, 9:32am UTC 弱点: 无 CVE ID: 无 赏金: 无

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