HTTP2降级请求走私
2023-11-30 17:32:56 Author: Yak Project(查看原文) 阅读量:11 收藏

请求走私简介

HTTP请求走私(HTTP Request Smuggling)这个概念第一次在安全会议上提出是在2005年。当时,Chetan Kunte和Amit Klein两位研究人员在Black Hat安全会议上详细介绍了这种攻击技术。揭示了HTTP/1.1协议在处理请求时的一些歧义,特别是在使用“Transfer-Encoding: chunked”头部时,不同的服务器可能会以不同的方式解析相同的HTTP请求。导致可以让攻击者插入恶意的HTTP请求,使得后续的正常请求被错误地处理。

对于HTTP/1.1协议来说,请求走私可以分为如下三种情况。

CL.TE

在CL.TE类型的请求走私中,攻击者发送一个精心构造的HTTP请求,该请求同时包含Content-LengthTransfer-Encoding: chunked头。由于HTTP/1.1规定当这两个头同时存在时,应该优先考虑Transfer-Encoding头,但并非所有服务器都遵循这一规则。攻击者利用这一点,向处于前端的代理服务器发送一个请求,该代理遵循HTTP/1.1规则,忽略Content-Length头,而将请求转发到后端服务器时,后端服务器可能会优先考虑Content-Length头。这样,后端服务器解析出的请求边界和前端代理解析出的请求边界就不同,导致后续的请求可能被错误地解析,从而实现攻击。

TE.CL

在TE.CL类型的请求走私中,情况正好相反。攻击者发送的请求使得前端代理优先考虑Content-Length头,而后端服务器优先考虑Transfer-Encoding头。这通常发生在前端代理不支持Transfer-Encoding: chunked或者不正确地实现了HTTP/1.1规范的情况下。

TE.TE

除了CL.TE和TE.CL之外,还有一种较少见的TE.TE类型,这种情况下,攻击者发送包含多个Transfer-Encoding头的请求。如果前后端服务器对这些头的处理方式不一致,也有可能导致请求走私。

上述几个请求走私的情况都与内容长度头部(Content-Length)和传输编码头部(Transfer-Encoding)两个头部的处理密切相关。那么对于不使用这两个头的HTTP2协议来说,请求走私是否还能实现?

HTTP2简介

在讨论HTTP2的请求走私之前,先来简单介绍一下HTTP2协议。HTTP/2是HTTP协议的重大升级,它通过引入二进制分帧、多路复用、头部压缩、服务器推送等高级功能,显著提高了网络通信的效率和速度。

其中二进制分帧是HTTP/2中最核心的升级,也是这篇文章主要需要了解的要点。在HTTP/2中,通信信息不再作为一个整体被发送,而是分解为多个较小的帧,每个帧携带着特定类型的数据,如头部信息、负载数据等。


从上面这张图中可以直观地看出,HTTP数据包在HTTP/2中会被拆分成若干个 HEADER frame(头部过长会使用 CONTINUATION frame 补充) 和 DATA frame,当数据发送完毕的时候,即发送最后一个帧的时候会在帧中附带一个END_STREAM 标志位,不再需要其他的信息来指明此HTTP数据包何时结束。

HTTP/2的分帧机制改变了请求和响应的封装方式。请求和响应被分解为多个帧,并且帧之间的界限比HTTP/1.x中的请求界限更加明确。理论上,这应该减少请求走私的可能性,因为帧的界限不容易被模糊。

但是,当HTTP/2流量通过代理时,代理可能需要将其转换回HTTP/1.x以与不支持HTTP/2的旧服务器通信。在这个转换过程中,如果代理没有正确地重建请求边界,或者在将HTTP/2帧转换为HTTP/1.x请求时出现错误,就可能引入请求走私的漏洞。而实际情况中大多数CDN厂商都支持HTTP/2,但是很多后端服务并不支持,所以当转化流量这一步出现问题就会导致HTTP2降级请求走私漏洞。

HTTP降级请求走私成因

假设现在有这样一个畸形HTTP数据包

POST / HTTP/2.0Host: www.example.comContent-Length: 1
aGET / HTTP/1.1Host: www.example.com

对于HTTP/2协议Content-Length头是没有意义的,无论设置成多少,HTTP/2服务端都能正常解析成一个请求。结构类似下面的图:


HTTP/2 引入了一种称为“伪头字段”(Pseudo-header fields)的特殊头部字段,它们用于传递对于HTTP/2连接非常重要的信息。这些伪头字段以冒号(":")开头,以区别于正常的HTTP头部字段。它们必须在所有常规头部字段之前发送,并且每个HTTP/2请求或响应只能包含一份。

伪头字段包括:

  • :method:这个字段包含HTTP请求的方法(例如GET或POST)。
  • :scheme:表示请求的URI方案(例如http或https)。
  • :path:请求的路径信息(例如/index.html)。对于HTTP/2连接来说,:path不能是空的,至少要是一个正斜杠("/")。
  • :authority:包含了请求的目标主机和端口信息(例如www.example.com:443)。在HTTP/1.1中,这通常由“Host”头部字段提供。
  • :status:仅在响应中出现,包含了响应的状态码(例如200或404)。

伪头字段是HTTP/2协议的关键组成部分,它们使得HTTP/2能够更有效地封装和传输HTTP语义。通过这种方式,HTTP/2保持了与HTTP/1.x系列的兼容性,同时提高了性能和效率。

而对于HTTP/1.x协议来说它不太符合规范,因为HTTP/1.x解析请求的边界需要受Content-Length的控制,而实际的body长度和Content-Length标注的长度不相等。如果按照Content-Length来读取请求,先读取到一个POST请求

POST / HTTP/2.0Host: www.example.comContent-Length: 1
a

接着解析后面的数据,会发现也是一个合格的HTTP请求数据包,这样就解析到了第二个请求。

GET / HTTP/1.1Host: www.example.com

那么就像上面提到的,如果有HTTP/2代理HTTP/1.x的场景就有可能出现,代理接收到一个请求,转发给后端服务器被处理成两个请求的情况。就像下面这张图一样,红色的方块代表走私的请求,经过HTTP/2代理的处理,转给HTTP/1.1的队列里被当成独立的新请求了。



极客大挑战-EZ_Smuggling

接下来通过一道CTF,来进一步了解此漏洞的利用和危害。

访问网页,入口是一个登录页面。正常注册登录即可,登录成功之后是一个blog页面。


整个blog一共有四篇文章,其中有三篇普通用户可以访问,秘密文章只有admin可以访问。

那么显然flag和秘密文章有关系,再根据其余三篇文章中的提示,可以知道目标应用是一个http2服务反向代理http1服务的应用,并且秘密文章的用户认证是在http2代理服务层实现的。那么可以尝试通过http2和http1之前解析数据包差异构造请求走私,绕过http2代理服务的认证。

使用Yakit开启http2支持抓取http2请求包


点击秘密文章页面,抓取访问数据包,发送到重发数据包模块(webfuzzer)。除去数据包的一些无用头,需要的信息就只有cookiehost。那么简化一下,需要绕过HTTP/2代理层走私给后端HTTP/1.x服务的请求就应该如下:

GET /admin HTTP/1.1Host: x.x.x.x:8080cookie: session=MTcwMTMxMzIxMXxnOHh3MDZ6U0FVenVneGw0UGZpaGR3Y3d6dm9aQXZfQzdKMFdaa1VtQ1RWLTJGd1QzXzYyT3NBemY1Q0I3TFl6TERnN3luMzhOcjg1VFdacWx0Wm9WblF3bEVTaTlPMGp8ij9QWfpY8_quuD_dCQeeaK6L5oEX6f3SL3AwkD23FNs=

那么按照上面介绍的走私成因,构造一个畸形数据包:

POST /articles?id=2 HTTP/2.0Host: x.x.x.x:8080Content-Length: 1
aGET /admin HTTP/1.1Host: x.x.x.x:8080cookie: session=MTcwMTMxMzIxMXxnOHh3MDZ6U0FVenVneGw0UGZpaGR3Y3d6dm9aQXZfQzdKMFdaa1VtQ1RWLTJGd1QzXzYyT3NBemY1Q0I3TFl6TERnN3luMzhOcjg1VFdacWx0Wm9WblF3bEVTaTlPMGp8ij9QWfpY8_quuD_dCQeeaK6L5oEX6f3SL3AwkD23FNs=

HTTP/2代理层解析的请求结构就是这样的:


是一个对普通文章的请求,能够通过HTTP/2的用户认证。而经过转化后后端HTTP/1.x服务会解析成两个请求,第一个还是一个对普通文章的请求:

POST /articles?id=2 HTTP/2.0Host: x.x.x.x:8080Content-Length: 1
a

而第二个则是一个对admin文章的请求:

GET /admin HTTP/1.1Host: x.x.x.x:8080cookie: session=MTcwMTMxMzIxMXxnOHh3MDZ6U0FVenVneGw0UGZpaGR3Y3d6dm9aQXZfQzdKMFdaa1VtQ1RWLTJGd1QzXzYyT3NBemY1Q0I3TFl6TERnN3luMzhOcjg1VFdacWx0Wm9WblF3bEVTaTlPMGp8ij9QWfpY8_quuD_dCQeeaK6L5oEX6f3SL3AwkD23FNs=

这样就成功绕过了架设在HTTP/2代理层的用户认证,成功走私了一个admin请求给HTTP/1.x后端服务器。

使用WebFuzzer发送畸形数据包,注意需要设置发包不修复content-length。成功获取到flag


扩展

本文简单介绍了HTTP/2降级到HTTP/1.x可能会导致的请求走私问题。虽然只着重讲解了H2.CL形式的漏洞,但是这种类型的HTTP/2降级走私漏洞的原理都是一致的:利用HTTP/2的帧传输转化成HTTP/1.x文本传输过程中的纰漏,想方设法向请求中夹带可以模糊请求边界的信息。

那么,很容易地可以想象一个H2.TE形式的走私畸形数据包就应该如下:

POST / HTTP/2.0Host: www.example.comTransfer-Encoding: chunked
a0GET / HTTP/1.1Host: www.example.com

HTTP2的结构就是这样的


而后端HTTP/1.x服务器解析到的请求则会是两个请求。

当然还有一些更多的绕过技巧,同样也是使用HTTP/2的帧传输性质,HTTP/2中请求头的划分不再需要使用\r\n,也就是意味着\r\n不再是特殊意义的字符可放置在HTTP请求头中,那么自然可以是通过这样结构的HTTP2,塞一个TE头进入请求中,可以绕过一些仅仅只对特定请求头名过滤的情况。



END

  YAK官方资源 

Yak 语言官方教程:
https://yaklang.com/docs/intro/
Yakit 视频教程:
https://space.bilibili.com/437503777
Github下载地址:
https://github.com/yaklang/yakit
Yakit官网下载地址:
https://yaklang.com/
Yakit安装文档:
https://yaklang.com/products/download_and_install
Yakit使用文档:
https://yaklang.com/products/intro/
常见问题速查:
https://yaklang.com/products/FAQ

长按识别添加工作人员
开启Yakit进阶之旅


文章来源: http://mp.weixin.qq.com/s?__biz=Mzk0MTM4NzIxMQ==&mid=2247516951&idx=1&sn=7297aa9f7fa49c3fdc79225f42e441f0&chksm=c2d1ffb3f5a676a57c053b8917bf9d0b012c3b4995658feb8a4cc2a3670486f6dc0634e76205&scene=0&xtrack=1#rd
如有侵权请联系:admin#unsafe.sh