0x1 定义
php使用serialize()这个过程被称为序列化,使用unserialize()这个过程被称作反序列化。
0x2 serialize()
用于序列化对象或数组,并返回一个字符串。序列化会保存对象中的所有变量,不会保存对象的方法,只会保存类的名字。PHP序列化就是将内存的变量数据“保存”到文件的持久数据的过程
例子:
<?php
class User
{
/*类的变量*/
public $name = '';
public $age = 0;
/*方法:打印数据*/
public function PrintData(){
echo 'user'.' name:'.$this->name.' age:'.$this->age;
}
}
//创建一个对象
$user = new User();
$user->name = 'iring';
$user->age = 18;
//输出数据
$user->PrintData();
echo "\n";
//输出序列化后的数据
echo serialize($user);
可以发现对于对象,序列化后的格式为:
O:strlen(类名):类名:类的变量个数:{类型:长度:值;类型:长度:值…}
注:对象中的常量在序列化后是不会保留的。如果存在类的继承,会保留父类中的变量
其他类型的数据序列化后的格式为:
String类型 : s:size:value;
Integer类型 : i:value;
Boolean类型 : b:value;
(保存1或0)
Null型 : N;
Array : a:size:{key definition;value definition}
0x3 unserialize()
函数用于将通过serialize()函数序列化后的对象或者数组进行反序列化,并返回原始对象的结构。可以将反序列化理解为就是:将文件中的持久数据转变成内存中的变量数据
//输出序列化后的数据
$temp = serialize($user);
echo $temp."\n";
//反序列化
$ans = unserialize($temp);
echo $ans->PrintData();
0x4 PHP反序列化漏洞
需要了解PHP的“魔法函数”:以__(两个下划线)开头的类方法被保留为魔法方法。这些方法会在特定的时候被调用。例子
__construct() 当一个对象被创建时候被调用(构造方法)
__destruct() 当一个对象被销毁的时候调用(析构方法)
__toString() 当一个对象被当做字符串时被调用
__wakeup() 当使用unserialize时候触发
__sleep() 当使用serialize时候触发
对象注入
因为PHP允许对象序列化,因此用户可以提交序列化的字符串,若没有对请求进行严格的过滤,在传递给unserialize()函数后就可能造成PHP对象注入漏洞。
对象漏洞需要满足的前提:
- unserialize()的参数可控的。
- 代码中含有一个包含魔法方法的类,并且该类方法里出现一些使用类成员变量作为参数的存在安全问题的函数。
例子:
<?php
error_reporting(0);
class Test
{
var $test = "demo";
function __destruct()
{
@eval($this->test);
}
}
$a = $_POST['test'];//POST方法获得前端数据
print_r($a);
$b = unserialize($a);//对传入的字符串反序列化
程序结束时会销毁变量,会触发__destruct()函数,我们可以通过反序列化构造出变量的值,使得其中的内容被当做代码去执行。构造一个Payload测试一下:
test=O:4:"Test":1:{s:4:"test";s:10:"phpinfo();";}
绕过魔法函数的反序列化
wakeup()魔法函数绕过
PHP5<5.6.25
PHP7<7.0.10
当在反序列化字符串中,表示属性个数的值大于真实属性个数时,会绕过__wakeup()函数的执行