LCTF2017——复现Write Up

LCTF2017—…

这次CTF做的可以说很憋屈了。唉,辛辛苦苦两天,愣是没做出来一道题,最后全靠学弟做出来一道逆向才避免了光头的尴尬。

Web弱鸡也就做做Web、MISC,以后再学学可能做做Crypto。
太菜了。

MISC-拿去当壁纸吧朋友

题目给了一篇论文
《Keyless dynamic optimal multi-bit image steganography using energetic pixels》Goutam Paul著

Google了一下,发现有一个Github项目叫BusySteg。
down下来之后,看了一下,要安opencv,然后cmake编译运行即可。

然后我就死在了安opencv的过程中。
看过Write up之后,发现。。。我果然好菜。。QAQ

Web-签到题

我没做出来签到题,所以这次比赛算没打的。。。。。
QAQ

题目是一道test.php

稍微扫了一下,发现网站参数就这么一个。www.baidu.com

然后一想,肯定是用协议做文章,很自然的想到file协议,而后。。。我就陷入了试127.0.0.1 、localhost的误区。
也曾想到网站后加子目录,但始终没想明白为何wp里payload会是file://www.baidu.com/etc/flag?
尤其是那个etc。

现在算是明白了,问了出题人,说随便放的,因为都会去试etc/passwd文件。。。。。然后就有线索了,这个文件的最后会有路径。。。我怎么就没想着试试。。。。。

Web-Simple blog

扫一下目录知道了存在login.php、admin.php两个文件,访问admin.php发现有权限控制,login.php是登录界面。
还扫到了弱口令,账号密码admin,可以进admin.php

这里,wp里说,可以通过login.php和admin.php的备份文件即.php.swp获得备份文件。
以此获得源码。
显然,我没找到。。。。

login.php源码:

function get_identity(){
    global $id;
    $token = get_random_token();
    $c = openssl_encrypt($id, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token);
    $_SESSION['id'] = base64_encode($c);
    setcookie("token", base64_encode($token));
    if($id==='admin'){
        $_SESSION['isadmin'] = 1;
    }else{
        $_SESSION['isadmin'] = 0;
    }
}

function test_identity(){
    if (isset($_SESSION['id'])) {
        $c = base64_decode($_SESSION['id']);
        $token = base64_decode($_COOKIE["token"]);
        if($u = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token)){
            if ($u === 'admin') {
                $_SESSION['isadmin'] = 1;
                return 1;
            }
        }else{
            die("Error!");
        } 
    }
    return 0;
}

可以看到在session中也做了身份验证,但是由于加密模式是aes-128-cbc,且$token在cookie里,可控,所以这里可以进行Pading Oracle Attack,通过修改$token可以把$_SESSION['isadmin']改为1,如此,即可登入admin.php。
学习了,Pading Oracle Attack。出题大佬的blog此文:http://f1sh.site/2017/08/04/%E5%88%9D%E5%AD%A6padding-oracle-attack/

admin.php源码:

if(isset($_GET['id'])){
    $id = mysql_real_escape_string($_GET['id']);
    if(isset($_GET['title'])){
        $title = mysql_real_escape_string($_GET['title']);
        $title = sprintf("AND title='%s'", $title);
    }else{
        $title = '';
    }
    $sql = sprintf("SELECT * FROM article WHERE id='%s' $title", $id);
    $result = mysql_query($sql,$con);
    $row = mysql_fetch_array($result);
    if(isset($row['title'])&&isset($row['content'])){
        echo "<h1>".$row['title']."</h1><br>".$row['content'];
        die();
    }else{
        die("This article does not exist.");
    }

wp说此处可以利用PHP格式化字符串的漏洞。在PHP的sprintf这个函数中%\会被当成一个格式化字符串。

%\会被认为是个tan 90'的格式化字符串,输出空,so,可以传入title=%' or 1#,转义,拼接

sprintf("SELECT * FROM article WHERE id='%s' AND title='%\' or 1#'", $id);

这样%就会吃掉后面的\组成一个格式化字符串,单引号就成功逃逸了出来。

娘希匹,想不到,真的想不到。。。。

只是这样的话还是会报错参数不足,因为这条代码里有两个格式化字符串但是只有一个参数。不过PHP的格式化字符串还有另一种表示方法%1$s,其中%后面的数字就表示引用第几个参数,$后面是格式化字符串的类型。
传入title=%1$' or 1#时:

sprintf("SELECT * FROM article WHERE id='%s' AND title='%1$\' or 1#'", $id);

最终payload:

?id=0&title=%251%24'%20union%20select%201%2C2%2C3%23

注入时也有一个小坑,key这个表名是MYSQL保留字,我们把它当做表名带入查询时必须用反引号包起来。

harmoc

发表评论

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