这道题真的是涨了我的知识,也确实interesting
而且,因为我想要加大刷题的量,不想码字占用我太多的时间,所以未来的writeup
的格式可能会很简陋,但是一定会有重点,会有该有的东西。
开始~
题目:http://ctf5.shiyanbar.com/web/pcat/index.php
首先,还是web题的老套路,抓包,看源码,找hint,审计代码......
我们在网页的注释中发现了source.txt
里面放着源码,然后开始审计:
<?php
error_reporting(0);
if (!isset($_POST['uname']) || !isset($_POST['pwd'])) {
echo '<form action="" method="post">'."<br/>";
echo '<input name="uname" type="text"/>'."<br/>";
echo '<input name="pwd" type="text"/>'."<br/>";
echo '<input type="submit" />'."<br/>";
echo '</form>'."<br/>";
echo '<!--source: source.txt-->'."<br/>";
die;
}
function AttackFilter($StrKey,$StrValue,$ArrReq){
if (is_array($StrValue)){
$StrValue=implode($StrValue);
}
if (preg_match("/".$ArrReq."/is",$StrValue)==1){
print "水可载舟,亦可赛艇!";
exit();
}
}
$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";
foreach($_POST as $key=>$value){
AttackFilter($key,$value,$filter);
}
$con = mysql_connect("XXXXXX","XXXXXX","XXXXXX");
if (!$con){
die('Could not connect: ' . mysql_error());
}
$db="XXXXXX";
mysql_select_db($db, $con);
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
$query = mysql_query($sql);
if (mysql_num_rows($query) == 1) {
$key = mysql_fetch_array($query);
if($key['pwd'] == $_POST['pwd']) {
print "CTF{XXXXXX}";
}else{
print "亦可赛艇!";
}
}else{
print "一颗赛艇!";
}
mysql_close($con);
?>
比较引人注意的是,这次的过滤,很全面,把我们常规思路大概都过滤掉了。
而且,后面的mysql_num_rows($query) == 1
也让我们看不到结果,于是没有办法,我只能猜测是基于时间的盲注,但是我们发现sleep
benchmark
以及括号都被过滤了。而且,就算前面的限制都过去,你也不知道pwd。
那么,这是用到了sql语句中的一个叫做rollup
的东西,他是用在groupby
后进行数据统计的,与他类似的有一个叫做cube
的,他和rollup
的不同之处也就是我们要利用的地方,讲道理,用cube
也是可以的,因为对于单一分组来说,也就一组数据报表。但是尝试发现不可以,这里不知道为什么。
rollup
和cube
呢,其实就是groupby
的一个扩展功能,叫做超级聚合,也就是对于分组数据进行一下统计报表。讲道理他是和聚合函数连用的,来产生一个合计
的数据,但是,同时他也会产生一个NULL的数据。
后来,我想了一下,为什么是这样呢?
首先,我们构造的sql里面groupby 却没有聚合,所以得到的就是单独的一列内容,然后在这单独的一列上rollup得到的就是null
所以,只要我们的password不传值进去,我们就通过验证了。