PHP反序列化漏洞

​* 最近再次遇见了反序列化漏洞,感觉还是很有意思,就又又又学习了一下。😅

PHP对象序列化 (手册)

  • 序列化(serialize()) 对象->字符串
  • 反序列化(unserialize()) 字符串->对象

反序列化漏洞

  序列化与反序列化本身没有漏洞,触发漏洞的点如下:

1
2
反序列化函数 unserialize() 中存在可控参数
魔术方法中有敏感操作

魔术方法

  php中的魔术方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
函数名 		函数作用 	
__construct() 类的构造函数
__destruct() 类的析构函数
__call() 在对象中调用一个不可访问方法时调用
__callStatic() 用静态方式中调用一个不可访问方法时调用
__get() 获得一个类的成员变量时调用
__set() 设置一个类的成员变量时调用
__isset() 当对不可访问属性调用isset()或empty()时调用
__unset() 当对不可访问属性调用unset()时被调用
__sleep() 执行serialize()时,先会调用这个函数
__wakeup() 执行unserialize()时,先会调用这个函数
__toString() 类被当成字符串时的回应方法
__invoke() 调用函数的方式调用一个对象时的回应方法
__set_state() 调用var_export()导出类时,此静态方法会被调用
__clone() 当对象复制完成时调用
__debugInfo() 当调用var_dump()打印对象时被调用(当你不想打印所有属性)适用于PHP5.6版本

示例

代码:

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
57
58
59
60
61
62
63
64
65
66
<?php
class test{
public $nae;
public function __construct(){
echo '__construct is ok';
echo '<br/>';
}
public function __call($name,$arguments){
echo '__call is ok';
echo '<br/>';
}
public static function __callStatic($name, $arguments){
echo '__callStatic is ok';
echo '<br/>';
}
public function __get($name){
echo '__get is ok';
echo '<br/>';
}
public function __set($name,$value){
echo '__set is ok';
echo '<br/>';
}
public function __isset($name){
echo '__isset is ok';
echo '<br/>';
}
public function __unset($name){
echo '__unset is ok';
echo '<br/>';
}
public function __sleep(){
echo '__sleep is ok';
echo '<br/>';
return array('nae');
}
public function __wakeup(){
echo '__wakeup is ok';
echo '<br/>';
}
public function __toString(){
echo '__toString is ok';
echo '<br/>';
return '111<br/>';
}
public function __invoke(){
echo '__invoke is ok';
echo '<br/>';
}
public static function __set_state($arr){
return '__set_state is ok';
// return '';
}
public function __clone(){
echo '__clone is ok';
echo '<br/>';
}
public function __destruct(){
echo '__destruct is ok';
echo '<br/>';
}
public function __debugInfo(){
echo '__debugInfo is ok';
echo '<br/>';
}
}

执行语句:

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
#实例化对象 __construct 被调用
$a=new test();
#设置类中没有的值时。__set 被调用
$a->xvalue='test';
#调用类中不可调用的方法时 __call被调用
$a->nofun();
#读取不可访问或不存在属性时 __get 被调用
echo $a->xvalue;
#当对不可访问属性调用isset()或empty()时调用 __isset 被调用
isset($a->xvalue);
#当对不可访问属性调用unset()时被调用
unset($a->xvalue);
#被当成函数调用时 __invoke被调用
$a();
#调用类中不存在的静态方法时,__callStatic被调用
$a::nofo();
#对象被复制的时候 __clone被调用。对象消失时__destruct也会被调用
$a1 = clone $a;
#序列化对象 __sleep 被调用
$c=serialize($a);
#对象被当成字符串时, __toString被调用
echo $a;
#调用var_export()导出类时 __set_state 被调用
// var_export($a);
eval( '$a2 = ' . var_export ( $a , true ) . ';' );var_dump($a2);
#反序列化时 __wakeup 被调用 将字符串转化成 对象。对象消失 __destruct也会被调用
echo '<br/>';
unserialize($c);
#打印调试信息 __debugInfo被调用
var_dump($a);

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
__construct is ok
__set is ok
__call is ok
__get is ok
__isset is ok
__unset is ok
__invoke is ok
__callStatic is ok
__clone is ok
__sleep is ok
__toString is ok
111
C:\phpStudy\WWW\test2.php:91:string '__set_state is ok' (length=17)
__wakeup is ok
__destruct is ok
__debugInfo is ok
C:\phpStudy\WWW\test2.php:96:
object(test)[1]
__destruct is ok
__destruct is ok