反序列化漏洞
0x01 序列化相关基础知识
0x01 01 序列化演示
序列化就是把本来不能直接存储的数据转换成可存储的数据,并且不会丢掉数据格式
序列化(Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。 在序列化期间,对象将其当前状态写入到临时或持久性存储区。 以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。 序列化使其他代码可以查看或修改,那些不序列化便无法访问的对象实例数据。
<?php
class test{
private $flag = 'Inactice';
protected $test = 'test';
public $test1 = "test1";
public function set_flag($flag){
$this->flag = $flag;
}
public function get_flag($flag){
return $this->flag;
}
}
$object = new test();
$object->set_flag('Active');
$data = serialize($object);
echo $data;
?>
那么这种将原本的数据通过某种手段进行“压缩”,并且按照一定的格式存储的过程就可以称之为"序列化"
O:4:"test":3:{s:10:"testflag";s:6:"Active";s:7:"*test";s:4:"test";s:5:"test1";s:5:"test1";}
对象类型:长度:"类名":类中变量的个数:{类型:长度:"值";类型:长度:"值";......}

a - array b - boolean
d - double i - integer
o - common object r - reference
s - string C - custom object
O - class N - null
R - pointer reference U - unicode string
0x01 02 php魔法方法
PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法
这里就不得不介绍几个我们必须知道的魔法方法了
__construct 当一个对象创建时被调用,
__destruct 当一个对象销毁时被调用,
__toString 当一个对象被当作一个字符串被调用。
__wakeup() 使用unserialize时触发
__sleep() 使用serialize时触发
__call() 在对象上下文中调用不可访问的方法时触发
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发
__invoke() 当脚本尝试将对象调用为函数时触发
其中我想特别说明一下第四点:
这个 __toString 触发的条件比较多,也因为这个原因容易被忽略,常见的触发条件有下面几种
(1)echo (
$obj
) / print($obj
) 打印时会触发(2)反序列化对象与字符串连接时
(3)反序列化对象参与格式化字符串时
(4)反序列化对象与字符串进行比较时(PHP进行比较的时候会转换参数类型)
(5)反序列化对象参与格式化SQL语句,绑定参数时
(6)反序列化对象在经过php字符串函数,如 strlen()、addslashes()时
(7)在in_array()方法中,第一个参数是反序列化对象,第二个参数的数组中有toString返回的字符串的时候toString会被调用
<?php
class test{
public $varr1="abc";
public $varr2="123";
public function echoP(){
echo $this->varr1."<br>";
}
public function __construct(){
echo "__construct<br>";
}
public function __destruct(){
echo "__destruct<br>";
}
public function __toString(){
return "__toString<br>";
}
public function __sleep(){
echo "__sleep<br>";
return array('varr1','varr2');
}
public function __wakeup(){
echo "__wakeup<br>";
}
}
$obj = new test(); //实例化对象,调用__construct()方法,输出__construct
// $obj->echoP(); //调用echoP()方法,输出"abc"
// echo $obj; //obj对象被当做字符串输出,调用__toString()方法,输出__toString
$s =serialize($obj); //obj对象被序列化,调用__sleep()方法,输出__sleep
echo unserialize($s); //$s首先会被反序列化,会调用__wake()方法,被反序列化出来的对象又被当做字符串,就会调用_toString()方法。
// 脚本结束又会调用__destruct()方法,输出__destruct
?>
参考:
http://blog.evalshell.com/2020/12/26/风炫安全web安全学习第三十九节课-反序列化漏洞基/