IPFS CID未净化漏洞导致SSRF攻击分析

本文详细分析了curl工具中IPFS URL重写功能存在的安全漏洞,攻击者可通过构造恶意IPFS CID实现服务器端请求伪造和路径遍历,访问网关内部管理接口。报告包含完整的复现步骤和修复建议。

IPFS CID未净化漏洞导致SSRF攻击分析

漏洞概述

ipfs_url_rewrite()函数(位于src/tool_ipfs.c)使用CURLU_URLDECODE解码ipfs://和ipns:// URL的主机组件(CID),然后将解码后的值直接拼接至网关路径(aprintf("%s%s/%s%s", ...))而不进行规范化或验证。精心构造的主机值(例如..%2F..%2Fapi/v0/shutdown)解码后变为../../api/v0/shutdown。当该值被附加且网关或反向代理随后规范化点分段时,请求将逃逸/ipfs/...并访问特权内部端点,如/api/v0/shutdown/api/v0/config/api/v0/id

这是IPFS URL重写逻辑中的服务器端请求伪造(SSRF)+路径遍历漏洞,允许攻击者通过向应用程序传递恶意ipfs:// URL(该应用程序将该URL传递给libcurl/curl),强制受害者调用内部网关管理端点。

关于AI使用的声明:我使用了AI辅助来帮助构建和起草报告,但漏洞、PoC和所有测试均在受控实验室环境中手动验证,以下证据通过手动执行生成。

复现步骤

使用的文件(在提交中附加这些文件):

  • admin_api.py — 模拟内部网关/管理接口(监听127.0.0.1:5001)
  • vulnerable_proxy.py — 执行易受攻击重写和获取的受害者服务(监听0.0.0.0:9000)

步骤:

  1. 启动模拟内部管理/网关:
1
python3 admin_api.py

脚本监听127.0.0.1:5001并打印每个传入请求路径。

  1. 启动易受攻击的服务(受害者),该服务代理/重写IPFS URL:
1
2
export IPFS_GATEWAY="http://127.0.0.1:5001/"
python3 vulnerable_proxy.py

脚本监听0.0.0.0:9000。它模拟易受攻击的序列:

  • 解码ipfs://的主机(CID)部分(百分比解码)
  • protocol_prefix + "/" + decoded_cid + inputpath连接至网关路径(无规范化)
  • 对生成的网关URL执行HTTP GET
  1. 从攻击者控制的主机(或访问受害者HTTP API的同一机器)发送恶意IPFS URL:
1
2
curl "http://127.0.0.1:9000/?target=ipfs://..%2F..%2Fapi/v0/shutdown"
curl "http://127.0.0.1:9000/?target=ipfs://..%2F..%2Fapi/v0/id"

受害者将重写URL并使用规范化至内部管理端点的路径联系网关。

  1. 观察:
  • 内部管理(admin_api.py)记录对/api/v0/shutdown/api/v0/id的请求。
  • 攻击者通过受害者服务接收该内部端点返回的响应(泄露内容)。

支持材料/参考

管理API(终端/PowerShell输出):

admin_api.py (F4846796)

1
2
3
4
5
6
7
8
PS C:\User> python .\admin_api.py
Admin API listening on 127.0.0.1:5001
[admin_api] 127.0.0.1:62081 -> /api/v0/shutdown
127.0.0.1 - - [01/Oct/2025 13:24:03] "GET /api/v0/shutdown HTTP/1.1" 200 -
[admin_api] 127.0.0.1:62083 -> /api/v0/shutdown
127.0.0.1 - - [01/Oct/2025 13:24:14] "GET /api/v0/shutdown HTTP/1.1" 200 -
[admin_api] 127.0.0.1:62086 -> /api/v0/id
127.0.0.1 - - [01/Oct/2025 13:24:19] "GET /api/v0/id HTTP/1.1" 200 -

易受攻击的代理(终端/PowerShell输出):

vulnerable_proxy.py (F4846794)

1
2
3
4
5
6
7
8
PS C:\User> python vulnerable_proxy.py
Vulnerable proxy listening on 0.0.0.0:9000
[vulnerable_proxy] Rewritten gateway URL: http://127.0.0.1:5001/api/v0/shutdown
127.0.0.1 - - [01/Oct/2025 13:24:03] "GET /?target=ipfs://..%2F..%2Fapi/v0/shutdown HTTP/1.1" 200 -
[vulnerable_proxy] Rewritten gateway URL: http://127.0.0.1:5001/api/v0/shutdown
127.0.0.1 - - [01/Oct/2025 13:24:14] "GET /?target=ipfs://..%2F..%2Fapi/v0/shutdown HTTP/1.1" 200 -
[vulnerable_proxy] Rewritten gateway URL: http://127.0.0.1:5001/api/v0/id
127.0.0.1 - - [01/Oct/2025 13:24:19] "GET /?target=ipfs://..%2F..%2Fapi/v0/id HTTP/1.1" 200 -

攻击者响应(curl输出):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
StatusCode        : 200
StatusDescription : OK
Content           : internal-admin-response

RawContent        : HTTP/1.0 200 OK
                    Content-Type: text/plain
                    Date: Wed, 01 Oct 2025 18:24:03 GMT
                    Server: BaseHTTP/0.6 Python/3.10.8

                    internal-admin-response

包含的代码引用(提交时附加确切行和提交SHA):

  • curl_url_get(uh, CURLUPART_HOST, &cid, CURLU_URLDECODE); — 主机解码无规范化。
  • pathbuffer = aprintf("%s%s/%s%s", gwpath, protocol, cid, inputpath); — 未验证的连接。
  • curl_url_set(uh, CURLUPART_PATH, pathbuffer, CURLU_URLENCODE); — 保留/的编码。

附加确切的文件src/tool_ipfs.c片段以及用于验证的仓库提交SHA。 https://github.com/curl/curl/blob/master/src/tool_ipfs.c#L134

理由/论证

该函数解码主机(CID)并直接将其连接至网关路径而不进行验证或规范化,因此百分比编码的分隔符如%2F和点分段(..)可以成为真实路径分隔符;在代理/网关规范化之后,这将逃逸/ipfs/沙箱并访问内部管理端点。提供的PoC是最小且可复现的,并演示了攻击者→受害者→内部网关SSRF,可以泄露数据或触发管理操作,因此这是一个需要修复的代码级错误。

建议的缓解措施

  1. 不要解码并将主机(CID)直接插入路径。将主机部分视为标识符,而非路径段。拒绝任何解码后包含路径分隔符或点分段的主机。
  2. 严格验证CID:仅使用CID解析器/验证器接受语法有效的CID(base32/base58格式)。拒绝任何包含/%2F..或控制字符的值。
  3. 显式转义/编码路径段,以便用户控制输入中的/字符不会成为路径分隔符(如果意图是保留字面%2F,则重新编码为%252F)。
  4. 为网关管理端点添加服务器端ACL/认证,以便即使本地请求到达它们,管理操作也需要授权。
  5. 单元测试/模糊测试:添加测试,断言如..%2F..%2Fapi/v0/shutdown的输入不会导致请求内部管理路径。

影响

类型:SSRF + 路径遍历(CWE-918 + CWE-22)

影响总结:能够向应用程序提供ipfs://或ipns:// URL的远程攻击者(该应用程序通过curl/libcurl转发或获取该URL)可以强制应用程序调用任意内部网关端点。这允许:

  • 管理操作(例如/api/v0/shutdown)— 潜在的本地拒绝服务(节点关闭)。
  • 读取敏感内部端点(例如/api/v0/id)— 信息泄露。
  • 在网关端点接受写入的情况下,可能修改配置或其他特权操作。

可利用性:在所述条件下(接受外部URL并使用易受攻击重写的应用程序)易于利用。PoC演示了通过单个恶意URL进行远程利用。

项目方回应

dfandrich (curl staff) 评论:

这个所谓的漏洞基于一个假设,即IPFS网关的基础URL是某种不可侵犯的东西(“逃逸/ipfs/沙箱”)。然而,依赖客户端来限制安全性是危险的。如果应用程序可以使用相对ipfs:路径"逃逸"此URL,那么它可以直接使用HTTP使用IPFS网关URL,并在路径中使用任何它想要的内容。允许未经认证的客户端访问"内部网关管理端点"根本不是安全。

bagder (curl staff) 评论:

首先:我不得不同意@dfandrich:curl中的IPFS重写只是将其变成HTTP URL。您可以直接创建更有创意的HTTP URL,而无需使用IPFS部分。我在这里看不到安全问题。

其次:如果curl及其使用IPFS的方式确实存在问题,请展示一些curl命令行显示问题被使用/利用。通过复杂的Python服务器绕远路感觉最多是分散注意力。

jimfuller2024 (curl staff) 评论:

感谢,我能够运行复现程序[…]期望curl仅为ipfs处理特殊情况(在没有其他安全控制存在的情况下)生成有效的HTTP URL是错误的期望[…]此外,正如其他人已经指出的,可以输入所有其他令人惊讶的URL,这些URL可能对未受保护的系统产生意外后果。

这里可能有修复/PR来收紧ipfs,但我同意其他人的结论,即这里没有(curl可以修复的)安全问题。

bagder (curl staff) 关闭报告并将状态更改为"不适用":

认为不是curl安全问题。

bagder (curl staff) 请求披露此报告:

根据项目的透明政策,我们希望所有报告都被披露并公开。

bagder (curl staff) 披露此报告。

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