虎符杯final_hatenum复现

预计阅读时间: 2 分钟

虎符杯final hatenum复现

本文是对buu一道题目的复现,其中也有个人的一些思考和对原解法的优化

题目给了源码,先把源码下载下来看一下

源码分析

很明显是考的sql注入,那就来看一下waf:

function sql_waf($str){
    if(preg_match('/union|select|or|and|\'|"|sleep|benchmark|regexp|repeat|get_lock|count|=|>|<| |\*|,|;|\r|\n|\t|substr|right|left|mid/i', $str)){
        die('Hack detected');
    }
}
function num_waf($str){
    if(preg_match('/\d{9}|0x[0-9a-f]{9}/i',$str)){
        die('Huge num detected');
    }
}

​ 有两个限制,首先是ban掉了一些关键字,其次是限制数字的长度不能大于9位,16进制的数字长度也不能大于9位,也就是最多可以有四个字符。然后来看一下哪里可以注入,发现在login处逻辑如下:

    function login($username,$password,$code){
        //$res = $this->conn->query("select * from users where username='\' and password='or 1 = 1#'");
        $res = $this->conn->query("select * from users where username='$username' and password='$password'");
        if($this->conn->error){
            return 'error';
        }
        else{
            $content = $res->fetch_array();
            if($content['code']===$_POST['code']){
                $_SESSION['username'] = $content['username'];
                return 'success';
            }
            else{
                return 'fail';
            }
        }

    }

​ 先根据username和password去取数据,然后验证code是否正确,正确后设置session。那如何获得flag呢?观察到在home.php中,存在如下逻辑:

if($_SESSION['username']=='admin'){
    echo file_get_contents('/flag');
}

​ 也就是说我们要以admin为用户名登录,数据库中只有admin一条记录(伏笔.jpg),也就是需要我们注入出这条记录中code的值。

注入流程

​ 再次观察waf,首先看能不能引入单引号,发现虽然单引号被过滤,但是反斜杠没有被过滤,可以在username处引入反斜杠,在password处引入payload。其次,发现select被过滤,在mysql5的条件下无法引入其他表,也就是说只能在这张表中注入(虽然不影响我们拿code)。

​ 然后再次观察login的流程,发现如果$this->conn->error为true,也就是出现报错就会返回error,不出现报错就会返回false。根据这个差异,要是我们能够影响他是否报错,就可以根据这个布尔逻辑进行判断,从而形成布尔盲注。

​ 那如何引入呢?考虑到select exp(709)会达到最大值,即如果select exp(710)及以上就会报错,如图

image-20210511230821741

​ 那这里参考第三篇参考文章中的内容,出现了两种思路。

思路1

就是文章中提到的

"username": "admin\\",
            "password": "||i&&exp(999)#",
            "code": "1"

由于执行exp(999)会溢出,再加上短路与。所以如果i=0,则不会报错,返回login fail;i=1,则造成溢出返回error。而事实确实如此。又学到rlike语句匹配返回1,否则返回0。心想这下成了!激动的把i换成rlike语句。却发现,笑死,根本不执行rlike语句,返回的全是error。

​ 这个逻辑看上去十分合理,也很符合我们之前考虑布尔注入的逻辑,那是否真的如原文所说,rlike不会影响结果么?我们按照原文进行测试。

​ 表中内容如下,仅有一条数据,符合原题目条件

image-20210511231319265

​ 其中hex(t)=0x73。我们先按照原文进行测试:

image-20210511231417903

image-20210511231509201

​ 嗯。。。果然如原文所说,无论rlike的结果如何,都会报错。那是不是真的不能用呢?再进行以下测试,用and 0去断路试试

image-20210511231627964

​ 并没有报错。。。那这是为什么呢?这显然是跟mysql处理where子句的各个条件的方式有关。一开始,我猜测mysql会把where子句根据and或者or进行分割,将各个分割的子条件分别执行,都执行一遍后再拼接进行断路。但是,按照这种思路,上面的这个例子中and 0并不能将exp断路,应该会出现报错,所以这种 说法不成立。

​ 为了探索这个问题,我去百度找了一篇文章,去看一下where子句中各个条件的处理条件。根据引文2

image-20210511232046353

​ 可以看到,mysql在处理where子句的各个子条件中,是有一个优先级顺序的。按照文章说法,会先进行索引的读取,然后再交给服务层进行Table Filter的过滤。可以发现,where子句不仅处理顺序有先后,而且也不同归一个模块处理。由此我猜测(仅仅是猜测,并没有实际验证),mysql会优先计算常数表达式,并对其进行过滤。这个想法的源头很简单,因为对于计算机来说,所有部分都确定的子条件是最好算出结果的,也不需要进行表的查询,放在第一顺位进行处理也很正常。

​ 那凡事都要讲证据,我这么菜,凭什么猜的就是对的?于是我编写了一个例子来佐证我的想法。首先,上面我们已经验证过,按照原文的语句是行不通的。那我稍微修改一下,把后面的exp常数表达式转换为筛选条件,来进行测试,结果如图:

image-20210511232814939

​ 野生的布尔条件出现了.jpg

​ 这个例子配合上面的例子,我认为能在很大程度上为我的猜想做证据。也就是说,文章中的思路一是可以盲注出数据的。

思路2

​ 思路二就是文章中的解法

||exp(710-(··· rlike ···))

​ Eki大佬的这种思路很巧妙地应用了mysql的隐式转换,把布尔条件用减法运算符转换为0/1,若为true,转换为exp(710-1)后不会溢出;若为false,转换为exp(710-0)后则会溢出。针不戳.jpg

后续步骤

​ 由于rlike无法引入%和^,所以没有办法在正则表达式中确定首位的位置,也就是说虽然我们可以知道有这么几个连续的字符,但是不知道在哪儿。。。原文中给出的解答如下:

​ 再就是因为过滤了引号,所以要把字段转成十六进制。但题目对十六进制的长度进行了限制,天真的我一开始构造了一个^.{n}x想用这个把code爆出来。然而毫无疑问的超长了。题目限制的长度是4个十六进制数,那只能先简单爆一下前三位,然后再通过前3位爆第4位,再通过2,3,4位爆第5位······。但这种方法不100%可靠。事实也证明code也确实是精心构造的运行结果如下

img

​ 这个。。好像确实是精心构造的。。。

​ 这里我也只能猜一下和多测几个code的值,并没有什么太好的方法,如果有更好的方法,烦请联系我。qq:NzQ1MjMxMjE0(b64)

Refrence:

对MYSQL注入相关内容及部分Trick的归类小结 - 先知社区 (aliyun.com)

mysql where条件执行顺序_MySQL复杂where条件分析_weixin_39842682的博客-CSDN博客

[HFCTF 2021 Finalweb | zhblog (naman.cool)](https://naman.cool/2021/05/01/[HFCTF 2021 Final]web/)

以及感谢p牛知识星球中的abcd师傅给予的帮助

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注