PHP反序列化漏洞基础
需要特别声明:所有技术探讨均限定于网络安全学习与防御研究范畴,旨在帮助开发者理解漏洞形成机制、掌握安全编码规范,进而提升系统抗风险能力。严禁将相关技术用于未授权的攻击活动,网络安全的核心价值在于构建防御体系,而非滥用技术造成危害。请各位读者遵守《网络安全法》及相关法律法规,共同维护健康的网络环境。
序列化与反序列化
1.语言结构
序列化的定义是,将对象的状态信息转换为可以存储或传输的形式(字符串)的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。简单的说,序列化就是把一个对象变成可以传输的字符串,可以以特定的格式在进程之间跨平台、安全的进行通信。
2.序列化的用途
- 方便对象在网络中的传输与存储
3.序列化与反序列化的过程
- 序列化:将对象转换为流,利于储存和传输的格式
- 函数:serialize 将对象以及对象的成员转变为字符串
- 反序列化:将流转换为对象
- 函数:unserialze 将字符串还原为对象以及对象成员

序列化的过程

代码解析创建Stu类的对象$stuinfo,给对象的name、sex、age、score属性分别赋值,最后使用serialize函数序列化该对象并输出。

序列化之后结果如上 解释如下
O:3:"Stu":4::O表示这是一个对象;3是类名Stu的长度;4表示对象有 4 个属性。
{s:4:"name";s:8:"zhangsan";:s代表字符串类型,4是属性名name的长度,8是属性值zhangsan的长度。
s:3:"sex";b:0;:s表示属性名sex是字符串类型,长度为 3;b代表布尔类型,0表示false
s:3:"age";i:18;:s表示属性名age是字符串类型,长度为 3;i代表整数类型,值为 18。
s:5:"score";i:99;}:s表示属性名score是字符串类型,长度为 5;i代表整数类型,值为 99。
反序列化流程
引入 serialize.php 文件。接着定义 <pre> 标签方便格式化输出,把序列化后的字符串赋值给 $stu1,再用 unserialize 函数将其反序列化为对象 $student_info,最后用 var_dump 打印该对象信息。

反序列化结果如上
反序列化漏洞
- 概念:PHP 反序列化漏洞也叫 PHP 对象注入
- 危害:这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果。
- 形成原因: 1、用户可控制参数
2、用户传递的反序列化字符串未校验检测
3、使用了魔法函数(最主要原因)
(反序列化不是PHP独有,也存在于java,python中)
常见的魔法函数
__construct():当一个对象创建时被调用(相当于 c++ 的构造函数),示例new tst()
__destruct():当一个对象销毁时被调用(相当于 c++ 的析构函数)
__sleep():在对象被序列化之前被调用,关联函数serialize()
__wakeup():将在unserialize()函数执行反序列化时调用。
__toString():当一个对象被当作一个字符串时使用。
__construct()
当一个对象被创建时调用,类似 c++ 的构造函数
<?php
header("Content-Type: text/html;charset=utf-8");
class Stu{
var $a;
var $b;
function __construct()
{
echo '新的对象被调用,执行了魔术函数:__construct!!!';
}
}
echo '此时object对象没有被创建!';
echo '<br>';
$stu = new Stu(); //新的对象被实例化和调用,触发魔术函数
?>
__destruct()
当一个对象被销毁的时候调用
<?php
header("Content-Type: text/html;charset=utf-8");
class Stu{
var $a;
var $b;
function __construct()
{
echo '对象被实例化,执行了魔术函数:__construct!!!';
}
function __destruct()
{
echo '实例化对象被销毁,执行了魔术函数:__destruct!!!';
}
}
echo '此时对象未被创建....';
echo '<br>';
$stu = new Stu();
echo '<br>';
echo '程序运行结束,对象被销毁....释放内存空间...';
echo '<br>';
?>
__sleep
在对象被序列化之前调用,serialize函数出现时调用
__tostring
当对象作为字符串输出时,触发魔法函数__tostring
<?php
header("Content-Type: text/html;charset=utf-8");
error_reporting(0);
class Stu{
var $a;
var $b;
function __construct(){
echo '对象被实例化,执行了魔术函数:__construct!!!';
}
function __destruct()
{
echo '实例化对象被销毁,执行了魔术函数:__destruct!!!';
}
function __toString()
{
echo '实例化对象被当做字符串输出,执行了魔术函数:__toString!!!';
}
function __sleep()
{
echo '实例对象被序列化,执行了魔术函数:__sleep!!!';
}
}
echo 'object isnot created!!!!';
echo '<br>';
$stu = new Stu();
echo '<br>';
echo 'object is created!!!!';
echo '<br>';
//echo $stu; //当做字符串输出,触发魔术函数__toString
serialize($stu); //序列化对象,触发魔术函数__sleep
echo '<br>';
?>
__wakeup
反序列化函数unserialize()被执行,检测到存在__wakeup魔术函数,执行了魔术函数
<?php
header("Content-Type: text/html;charset=utf-8");
class demo{
var $a=1;
var $b=2;
function __wakeup()
{
echo '反序列化函数unserialize()被执行,检测到存在__wakeup魔术函数,执行了魔术函数:__wakeup!!!';
}
}
$demo1 = new demo();
echo '<br>';
echo '对象demo被实例化创建成功!!!';
echo '<br>';
//echo $stu;
$str = <<<ABC
O:4:"demo":2:{s:1:"a";i:1;s:1:"b";i:2;}
ABC;
$demo1 = unserialize($str); //执行反序列化函数,寻找是否存在__wakeup()魔术函数
?>
反序列化漏洞演示
靶场1如下
代码定义了Test类,包含公共属性$str和析构方法__destruct()。当对象被销毁时,__destruct()会通过system()函数执行$str变量对应的系统命令。程序通过unserialize($_GET['code'])对用户传入的code参数进行反序列化,若code是精心构造的恶意序列化字符串,可篡改$str的值。
<?php
class Test{
public $str='test'; //序列化的变量
function __destruct(){ //通过反序列化,调用A对象,因为PHP魔法函数的缘故,他会自动执行__destruct()
@system($this->str); //--> system("test"); --> system("ipconfig")
}
}
$test = new Test(); //创建一个类的对象,并将其序列化
echo "<pre>";
unserialize($_GET['code']); //反序列化,将得到的序列化字符串反序列化为对象
?>
poc为
?code=O:4:"Test":1:{s:3:"str";s:8:"ipconfig";}

靶场2如下
<?php
header("Content-Type: text/html;charset=utf-8");
class Hello{
var $test = '123';
function __wakeup(){
$fp = fopen("shell.php","w+");
fwrite($fp,$this->test);
fclose($fp);
}
}
$class3 = $_GET['code'];
// print_r($class3);
echo "<br>";
$class3_unser = unserialize($class3);
?>
这段 PHP 代码定义了Hello类,包含test属性和__wakeup魔术方法。__wakeup会在反序列化时被调用,创建并写入shell.php文件,内容为test属性的值。代码通过$_GET['code']获取用户传入的序列化字符串,反序列化后触发上述操作。
poc
?code=O:5:"Hello":1:{s:4:"test";s:24:"<?php echo "vulnerable"; ?>";}

防御方法
1、序列化的参数用户不可以控制
2、过滤用户的输入,设置一些黑名单过滤 0: "" {};d i s
3、序列化后的内容进行检查,确保安全后再输出
需要说明的是,在真实业务环境中,反序列化漏洞的实际利用场景相对有限,其可操作性远不及其他类型漏洞;该类漏洞更多活跃于代码审计环节与 CTF 竞赛题目中。
更多推荐


所有评论(0)