1.序列化是什么意思呢?
序列化就是把本来不能直接存储的数据转换成可存储的数据,并且不会丢掉数据格式 serialize();
2.反序列化是什么意思呢?
其实就是字面的意思,把序列化的数据,转换成我们需要的格式 unserialize();
那么什么是序列化呢,序列化说通俗点就是把一个对象变成可以传输的字符串。举个例子,不知道大家知不知道json格式,这就是一种序列化,有可能就是通过array序列化而来的。而反序列化就是把那串可以传输的字符串再变回对象。
相关网站
https://www.cnblogs.com/junyi-bk/p/11631685.html
直接上例子便于理解:
我们先讲一讲比较简单的序列化,我们就用序列化json来举例子吧。虽然序列化Json和我们讲PHP反序列化的漏洞没有什么关系。但是在理解序列化这个概念和之后的内容会有所帮助
json_encode()
json_decode()
json_decode( ) ---- json 转 对象/数组
当第二个参数为true返回 array ,默认是false返回object。
json_encode( ) ---- 对象/数组 转 json
成功返回 json 编码的 string ,失败返回 false 。
相应网站
https://www.runoob.com/php/php-json.html
上代码
<?php
$book = array('book1'=>'1','book2'=>'2','Book3'=>'3','Book4'=>'4');
$json = json_encode($book);
var_dump($book);
echo $json;
?>
这边有一个book的数组
‘book1′=>’1’,
‘book2′=>’2’,
‘Book3′=>’3’,
‘Book4′=>’4’
如果我们想传输这个数组怎么办呢,我们就可以请json_encode()这个函数帮助我们将这个数组序列化成一串字符串
所以在这里,我们将数组序列化成json格式的字串的目的就是为了方便传输。我们可以看见,这里json格式来保存数据主要是使用键值对的形式。
好啦,接下来我们要开始深入一步,来讲讲如何把一个对象序列化成一串字符串。
假设,我们写了一个class,这个class里面存有一些变量。当这个class被实例化了之后,在使用过程中里面的一些变量值发生了改变。以后在某些时候还会用到这个变量,如果我们让这个class一直不销毁,等着下一次要用它的时候再一次被调用的话,浪费系统资源。当我们写一个小型的项目可能没有太大的影响,但是随着项目的壮大,一些小问题被放大了之后就会产生很多麻烦。这个时候PHP就和我们说,你可以把这个对象序列化了,存成一个字符串,当你要用的时候再放他出来就好了。
那么,怎么才能把一个对象序列化呢?
<?php
class DemoClass
{
public $name = "";
public $sex = "";
public $age = "";
}
$example = new DemoClass();
$example ->name ="111";
$example ->sex ="woman";
$example ->age ="18";
echo(一:);
var_dump($example);
$val = serialize($example);
echo(二:);
echo($val);
echo(三:);
var_dump($val);
$Newex = unserialize($val);
echo(四:);
var_dump($Newex);
echo(五:);
echo $Newex ->age;
?>
这里,我们先创了个DemoClass,里面存了点信息,后来我们new了一个实例$example的时候,将这个class里的一些信息给改变了。
如果我们之后还要用到这个实例怎么办呢,我们就先将他序列化存起来,到时候用的时候再放出来就好啦。
只要用serialize()这个函数就行了,反之利用unserialize()就可以将其进行反序列化回来。
serialize() 函数用于序列化对象或数组,并返回一个字符串。
serialize() 函数序列化对象后,可以很方便的将它传递给其他需要它的地方,且其类型和结构不会改变。
如果想要将已序列化的字符串变回 PHP 的值,可使用 unserialize()。
相应网站
https://www.runoob.com/php/php-serialize-function.html
这个时候,我们发现这次序列化出来的格式,和我们上一个序列化json的格式有点不同呢,解释一波:
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
private 的属性序列化后变成 <0x00>对象<0x00>属性名
public 没有任何变化
protected 的属性序列化后变成 <0x00>*<0x00>属性名
特殊十六进制<0x00>表示一个坏字节,就是空字节
3. 为什么会产生这个漏洞?
那么,问题来了,这么序列化一下然后反序列化,为什么就能产生漏洞了呢?
这个时候,我们就要了解一下PHP里面的魔术方法了,魔法函数一般是以__开头,通常会因为某些条件而触发不用我们手动调用:
在研究反序列化漏洞的时候,碰见这几个魔法函数就要仔细研究研究了:
__construct() // 当一个对象创建时被调用,
__destruct() // 当一个对象销毁时被调用,
__toString() // 当一个对象被当作一个字符串被调用。
__wakeup() // 使用unserialize()会检查是否存在__wakeup()方法,如果存在则会先调用,预先准备对象需要的资源
__sleep() // 使用serialize()会检查是否存在__wakeup()方法,如果存在则会先调用,预先准备对象需要的资源
__destruct() // 对象被销毁时触发
__call() // 在对象上下文中调用不可访问的方法时触发
__callStatic() // 在静态上下文中调用不可访问的方法时触发
__get() // 用于从不可访问的属性读取数据
__set() // 用于将数据写入不可访问的属性
__isset() // 在不可访问的属性上调用isset()或empty()触发
__unset() // 在不可访问的属性上使用unset()时触发
__toString() // 把类当作字符串使用时触发,返回值需要为字符串
__invoke() // 当脚本尝试将对象调用为函数时触发
相关网站
https://www.php.net/manual/zh/language.oop5.magic.php
如果服务器能够接收我们反序列化过的字符串、并且未经过滤的把其中的变量直接放进这些魔术方法里面的话,就容易造成很严重的漏洞了。
举个别人的例子:
<?php
class A{
var $test = "demo";
function __destruct(){
echo $this->test;
}
}
$a = $_GET['test'];
$a_unser = unserialize($a);
?>
这里我们只要构造payload:
test=O:9:"DemoClass":3:{s:4:"name";s:5:"Harry";s:3:"sex";s:5:"woman";s:3:"age";s:2:"18";}
就能控制echo出的变量,比如你能拿这个来进行反射型xss。(听你扯了半天你就给我看这个,别打我!)
参考网站:http://www.manongjc.com/detail/12-lbohplauhhknatd.html#!/h
https://www.cnblogs.com/xiaoqiyue/articles/10951836.html
注:本博客所引用的博客仅用与学术交流。