反序列化漏洞(Deserialization Vulnerability)是一种在应用程序处理数据的过程中,因不安全的反序列化操作引发的安全漏洞;反序列化是指将序列化的数据(通常是字节流或字符串)转换回对象的过程,如果反序列化操作未进行适当的验证或消毒,攻击者可以通过精心构造的恶意数据进行攻击,执行任意代码、获取敏感信息、或破坏数据的完整性。本文我们就来探讨一下反序列化漏洞产生的原因和漏洞利用方法。
序列化与反序列化
要掌握反序列化漏洞的产生原理肯定首先要明白序列化和反序列究竟是什么样的过程,这会儿我们就先针对这两个概念进行说明:
①序列化:序列化(Serialization)是一种将对象的状态转换为可存储或可传输格式的过程。
通过序列化,程序中的复杂数据结构(如对象、数组、数据结构等)可以转换为字节流、字符串或其他格式,从而能够存储到文件中、发送到远程服务器或者传输通过网络。
②反序列化:反序列化(Deserialization)是序列化的逆过程,即将序列化的字节流或字符串恢复成原来的对象或数据结构。
接下来我们就通过PHP代码来针对这两个过程进行演示:
①序列化例子
<?php
class Person {
public $name;
public $age;
public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
}
$person = new Person("Alice", 30);
$serializedData = serialize($person);
echo "Serialized Data: " . $serializedData;
?>
class Person
:定义一个名为Person
的类;$name、$age
:定义Person类的公共属性name和age
。
public function __construct($name, $age)
:定义一个构造函数,用于初始化对象的name
和age
属性。
接着使用new
关键字创建一个Person
类的实例,传入名字Alice
和年龄30
作为参数,初始化name
和age
属性;在初始化对象完成后调用serialize
函数,将Person
对象转换为一个字符串。这一过程称为序列化,生成的字符串可以用于存储或传输。
解释我们使用echo
语句输出序列化后的字符串,输出结果:运行这段代码将输出类似以下内容的字符串,这个字符串表示序列化后的Person
对象:
Serialized Data: O:6:"Person":2:{s:4:"name";s:5:"Alice";s:3:"age";i:30;}
O:6:"Person":2:
表示一个Person
对象,类名长度为6,包含2个属性。
{s:4:"name";s:5:"Alice";s:3:"age";i:30;}
是对象的属性及其对应的值:
-
s:4:"name";s:5:"Alice";
表示字符串属性name
的值为Alice
。 -
s:3:"age";i:30;
表示整数属性age
的值为30
。
②反序列化例子
<?php
class Person {
public $name;
public $age;
public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
}
$serializedData = 'O:6:"Person":2:{s:4:"name";s:5:"Alice";s:3:"age";i:30;}';
$person = unserialize($serializedData);
echo "Name: " . $person->name . "\n";
echo "Age: " . $person->age;
?>
这个示例中,我们使用unserialize
函数将序列化的字符串恢复为原始的Person
对象,并输出该对象的Name属性值和Age属性值;最后输出的结果为:
Name: Alice Age: 30
序列化和反序列化是软件开发中非常重要且常用的技术,主要用于数据的持久化存储和跨网络传输;正因为常用所以有着不好的编程习惯的开发者们可能经常会在代码中编写一些不安全的序列化代码;不安全的序列化和反序列化主要是指在处理用户输入或不受信任的数据时,没有进行适当的验证和消毒,导致安全漏洞----反序列化漏洞,接下去我们就以pikachu
靶场中的反序列化漏洞关卡针对反序列化漏洞的产生原理进行讨论。
示例:
1.打开关卡可以看到页面中存在一个输入框用以接收序列化数据:
输入带有恶意代码的序列化数据:
O:1:"S":1:{s:4:"test";s:29:"<script>alert("xss")</script>";}
此时页面出现弹窗:
payload构造:
这个关卡构造攻击语句的前提是要知道源码中存在的数据结构,而这些内容在当前页面中是没有的,我们需要从站点源码中去获取该数据结构以构造我们的序列化攻击语句,这个时候我们就来看一下当前关卡的相关代码:
class S{
var $test = "pikachu";
function __construct(){
echo $this->test;
}
}
$html='';
if(isset($_POST['o'])){
$s = $_POST['o'];
if(!@$unser = unserialize($s)){
$html.="<p>大兄弟,来点劲爆点儿的!</p>";
}else{
$html.="<p>{$unser->test}</p>";
}
}
class S
:定义一个名为S
的类;var $test = "pikachu";
:定义一个公共变量$test
,并初始化为字符串"pikachu"
。function __construct()
:定义类的构造函数。在创建S
类的实例时会自动调用此函数。echo $this->test;
:构造函数输出类的属性$test
的值(即"pikachu"
)。
$html = '';
:初始化一个空字符串变量$html
,用于存储最终的HTML输出。
if (isset($_POST['o']))
:检查是否通过POST请求传递了参数o
。
$s = $_POST['o'];
:将POST请求中的参数o
的值赋给变量$s
。
if (!@$unser = unserialize($s))
:
-
unserialize($s)
:尝试反序列化变量$s
。反序列化是将序列化的字符串转换回对象。 -
@$unser
:使用@
符号抑制反序列化过程中可能产生的警告或错误。 -
if (!@$unser = unserialize($s))
:如果反序列化失败($unser
为false
),则执行大括号内的代码块。
如果反序列化失败,向$html
追加一段HTML内容,显示“大兄弟,来点劲爆点儿的!”;如果反序列化成功,将反序列化得到的对象的test
属性值插入到HTML中,并追加到$html;
这个时候我们来看一下我们构造的Payload:
O:1:"S":1:{s:4:"test";s:29:"<script>alert("xss")</script>";}
根据源码我们构造了序列化数据;这是一个S对象,他的成员变量test的值为<script>alert("xss")</script>
;在后端程序接收到我们的输入后会将数据进行反序列化,接着将test值输入至页面中并进行解析执行产生弹窗;至此攻击成功!
这个代码存在反序列化漏洞;因为它直接对用户提供的数据进行反序列化,攻击者在获得代码中相关的数据结构后就可以构造恶意的序列化数据,导致任意代码执行或其他安全问题,要防止这种漏洞,应该避免对不可信数据进行反序列化,或者严格限制反序列化的类,并进行数据验证。
安全建议
-
避免反序列化用户输入的数据。
-
如果必须反序列化,使用允许列表限制可反序列化的类。
-
对输入数据进行严格的验证和消毒。