由Roarctf Easy Calc引起对http走私和分块传输绕过waf的思考

之前在做roarctf第一个web时候读到过关于php解析漏洞的文章。

https://www.freebuf.com/articles/web/213359.html

但是用的是在num参数前面构造一个%20来绕过waf。

image

但是后来看群里有个师傅提起了http走私协议。于是就去学习了一下。

HTTP走私原理:

HTTP请求走私漏洞的原因是由于HTTP规范提供了两种不同方式来指定请求的结束位置,它们是Content-Length标头和Transfer-Encoding标头,Content-Length标头简单明了,它以字节为单位指定消息内容体的长度。

Transfer-Encoding标头用于指定消息体使用分块编码(Chunked Encode),也就是说消息报文由一个或多个数据块组成,每个数据块大小以字节为单位(十六进制表示) 衡量,后跟换行符,然后是块内容,最重要的是:整个消息体以大小为0的块结束,也就是说解析遇到0数据块就结束。

相当于我发送请求,包含Content-Length,前端服务器解析后没有问题发送给后端服务器,但是我在请求时后面还包含了Transfer-Encoding,这样后端服务器进行解析便可执行我写在下面的一些命令,这样便可以绕过前端的waf。

走私的四种方法:

CL不为0时

这种情况一般发生在get请求下,当前端允许content-length,后端不允许时,此时后端服务器就不会解析content-length,这样就可能绕过waf。

CL-CL型

这种利用的前提条件是服务器不会返回400错误。但是前端服务器按照第一个Content-Length的值对请求进行处理,而后端源站服务器按照第二个Content-Length的值进行处理,这样便有可能引发请求走私。比如出现下面这种情况时:

1
2
3
4
5
Content-Length:68
Content-Length:5


num=1GET /calc.php?num=1 HTTP/1.1

前端先解析第一个cl为68,就会把下面的“num=1GET /calc.php?num=1 HTTP/1.1”都当做请求的内容。但是后端会解析第二个cl,也就是说后端只解析“num=1”
后面的请求体就作为下次请求的请求头(前提是Connection: keep-alive)。这样就会引发走私。

CL-TE

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

1
2
Content-Length:123
Transfer-Encoding: chunked

这里的chunked对请求内容进行分块编码,并且直到有\r\n的时候才结束。

TE-CL

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

在calc的题目中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>

这里后端对特殊符号做了一些过滤,而且除了这个过滤,还有waf拦截字符串的输入。然后就想利用http走私来绕过。

因为这里是get请求,而content-length是在post才会有用,所以这里跟cl其实关系不大,但是利用起来也不会报错。

这里我最开始就是想用cl和chunked结合起来去绕过。

image

这里的cl设置多少都无所谓,只要设置了chunked,并且最后由\r\n那么就能成功执行,如果没有\r\n那么就会一直waiting。

于是我想着能不能重新构造一个请求头来试试:

image

这里第一个请求头那一定得是keep-alive,然后设置content-length为5。目的是让tcp能够持续响应,而且这里服务器会因为cl为5只识别num=1,后面的一大串就被安排到了下一轮的请求里。(最后的chunked后面还是得跟两次\r\n来控制块)

说到这里,感觉这道题其实没怎么利用到http走私,主要还是用分块传输chunked方法来绕过waf。但是毕竟也是通过这个点学习了http走私的知识,所以就记录下来。

如果各位有看到不对的地方,还请斧正!