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)
步骤:
- 启动模拟内部管理/网关:
|
|
脚本监听127.0.0.1:5001并打印每个传入请求路径。
- 启动易受攻击的服务(受害者),该服务代理/重写IPFS URL:
|
|
脚本监听0.0.0.0:9000。它模拟易受攻击的序列:
- 解码ipfs://的主机(CID)部分(百分比解码)
- 将
protocol_prefix + "/" + decoded_cid + inputpath连接至网关路径(无规范化) - 对生成的网关URL执行HTTP GET
- 从攻击者控制的主机(或访问受害者HTTP API的同一机器)发送恶意IPFS URL:
|
|
受害者将重写URL并使用规范化至内部管理端点的路径联系网关。
- 观察:
- 内部管理(
admin_api.py)记录对/api/v0/shutdown或/api/v0/id的请求。 - 攻击者通过受害者服务接收该内部端点返回的响应(泄露内容)。
支持材料/参考
管理API(终端/PowerShell输出):
admin_api.py (F4846796)
|
|
易受攻击的代理(终端/PowerShell输出):
vulnerable_proxy.py (F4846794)
|
|
攻击者响应(curl输出):
|
|
包含的代码引用(提交时附加确切行和提交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,可以泄露数据或触发管理操作,因此这是一个需要修复的代码级错误。
建议的缓解措施
- 不要解码并将主机(CID)直接插入路径。将主机部分视为标识符,而非路径段。拒绝任何解码后包含路径分隔符或点分段的主机。
- 严格验证CID:仅使用CID解析器/验证器接受语法有效的CID(base32/base58格式)。拒绝任何包含
/、%2F、..或控制字符的值。 - 显式转义/编码路径段,以便用户控制输入中的
/字符不会成为路径分隔符(如果意图是保留字面%2F,则重新编码为%252F)。 - 为网关管理端点添加服务器端ACL/认证,以便即使本地请求到达它们,管理操作也需要授权。
- 单元测试/模糊测试:添加测试,断言如
..%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) 披露此报告。