PHP反序列化

目录

php类与对象

魔术方法

序列化 / 反序列化

序列化

反序列化

漏洞分析

php反序列化漏洞---对象注入

php类与对象

类是定义一系列属性和操作的模板,而对象,就是把属性进行实例化,完事交给类里面的方法,进行处理。

<?php
class people{
   //定义类属性(类似变量),public 代表可见性(公有)
    public $name = 'joker';
   //定义类方法(类似函数)
   public function smile(){
        echo $this->name." is smile...\n";
   }
}

$psycho = new people(); //根据people类实例化对象
$psycho->smile();
?>

通过运行代码可以看到内容如下

代码定义类一个people类,并在在类中定义了一个public类型的变量$name和类方法smile。然后实例化一个对象$psycho,去调用people类里面的smile方法,打印出结果。

魔术方法

魔术方法是在触发了某个事件之前或之后,魔法函数会自动调用执行,而其他的普通函数必须手动调用才可以执行。PHP 将所有以__(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以 __ 为前缀。

方法名作用
__construct构造函数,在创建对象时候初始化对象,一般用于对变量赋初值;
__destruct析构函数,和构造函数相反,在对象不再被使用时(将所有该对象的引用设为null)或者程序退出时自动调用;
__toString当一个对象被当作一个字符串被调用,把类当作字符串使用时触发,返回值需要为字符串,例如echo打印出对象就会调用此方法;
__wakeup()使用unserialize时触发,反序列化恢复对象之前调用该方法
__sleep()使用serialize时触发 ,在对象被序列化前自动调用,该函数需要返回以类成员变量名作为元素的数组(该数组里的元素会影响类成员变量是否被序列化。只有出现在该数组元素里的类成员变量才会被序列化);
__destruct()对象被销毁时触发;
__call()在对象中调用不可访问的方法时触发,即当调用对象中不存在的方法会自动调用该方法;
__callStatic()在静态上下文中调用不可访问的方法时触发;
__get()读取不可访问的属性的值时会被调用(不可访问包括私有属性,或者没有初始化的属性);
__set()在给不可访问属性赋值时,即在调用私有属性的时候会自动执行;
__isset()当对不可访问属性调用isset()或empty()时触发;
__unset()当对不可访问属性调用unset()时触发;
__invoke()当脚本尝试将对象调用为函数时触发。

__toString的具体触发场景:

  • echo($obj) / print($obj) 打印时会触发;

  • 反序列化对象与字符串连接时;

  • 反序列化对象参与格式化字符串时;

  • 反序列化对象与字符串进行==比较时(PHP进行==比较的时候会转换参数类型);

  • 反序列化对象参与格式化SQL语句,绑定参数时;

  • 反序列化对象在经过php字符串函数,如 strlen()、addslashes()时;

  • 在in_array()方法中,第一个参数是反序列化对象,第二个参数的数组中有toString返回的字符串的时候toString会被调用;

  • 反序列化的对象作为 class_exists() 的参数的时候。

魔术方法使用举例

序列化 / 反序列化

在开发的过程中常常遇到需要把对象或者数组进行序列号存储,反序列化输出的情况。特别是当需要把数组存储到mysql数据库中时,我们时常需要将数组进行序列号操作。

  • php序列化(serialize):是将变量转换为可保存或传输的字符串的过程

  • php反序列化(unserialize):就是在适当的时候把这个字符串再转化成原来的变量使用

这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性。

常见的php系列化和反系列化方式主要有:serializeunserializejson_encodejson_decode

序列化

php的序列化:变量转换为可保存和传输的字符串过程。

<?php
class object{
    public $team = 'joker';
    private $team_name = 'hahaha';
    protected $team_group = 'biubiu';

    function hahaha(){
        $this->$team_members = '777';
    }
}
$object = new object();
echo serialize($object);
?>

以上是序列化之后的结果,o代表是一个对象,6是对象object的长度,3的意思是有三个类属性,后面花括号里的是类属性的内容,s表示的是类属性team的类型,4表示类属性team的长度,后面的以此类推。值得一提的是,类方法并不会参与到实例化里面。

即格式为:O:对象名的长度:"对象名":对象属性个数:{s:属性名的长度:"属性名";s:属性值的长度:"属性值";}

序列化中个字母的含义

 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

需要注意的是变量受到不同修饰符(public,private,protected)修饰进行序列化时,序列化后变量的长度和名称会发生变化

  • 使用public修饰进行序列化后,变量$team的长度为4,正常输出。

  • 使用private修饰进行序列化后,会在变量$team_name前面加上类的名称,在这里是object,并且长度会比正常大小多2个字节,也就是9+6+2=17。

  • 使用protected修饰进行序列化后,会在变量$team_group前面加上*,并且长度会比正常大小多3个字节,也就是10+3=13。

通过对比发现私有变量和公有变量的储存方法不一样,在受保护的成员前都多了两个字节,受保护的成员在序列化时规则:

1.受Private修饰的私有成员,序列化时: \x00 + [私有成员所在类名] + \x00 [变量名]

2.受Protected修饰的成员,序列化时:\x00 + * + \x00 + [变量名]

其中,"\x00"代表ASCII为0的值,即空字节," * " 必不可少

反序列化

反序列化的话,就依次根据规则进行反向复原。这边定义一个字符串,然后使用反序列化函数unserialize进行反序列化处理,最后使用var_dump进行输出:

<?php
    $ser = 'O:6:"object":3:{s:4:"team";s:5:"joker";s:17:"objectteam_name";s:6:"hahaha";s:13:"*team_group";s:6:"biubiu";}';
    $ser = unserialize($ser);
    var_dump($ser);
?>

漏洞分析

php反序列化漏洞---对象注入

在反序列化过程中,其功能就类似于创建了一个新的对象(复原一个对象可能更恰当),并赋予其相应的属性值。如果让攻击者操纵任意反序列数据, 那么攻击者就可以实现任意类对象的创建,如果一些类存在一些自动触发的方法(魔术方法),那么就有可能以此为跳板进而攻击系统应用。

挖掘反序列化漏洞的条件是:

  1. 代码中有可利用的类,并且类中有__wakeup()__sleep()__destruct()这类特殊条件下可以自己调用的魔术方法。

  2. unserialize()函数的参数可控。

<?php
class A{
    var $test = "demo";
    function __destruct(){
        @eval($this->test);
    }
}
$test = $_POST['test'];
$len = strlen($test)+1;
$p = "O:1:\"A\":1:{s:4:\"test\";s:".$len.":\"".$test.";\";}"; // 构造序列化对象
$test_unser = unserialize($p); // 反序列化同时触发_destruct函数
?>

如上代码,最终的目的是通过调用__destruct()这个析构函数,将恶意的payload注入,导致代码执行。根据上面的魔术方法的介绍,当程序跑到unserialize()反序列化的时候,会触发__destruct()方法,同时也可以触发__wakeup()方法。但是如果想注入恶意payload,还需要对$test的值进行覆盖,题目中已经给出了序列化链,很明显是对类A的$test变量进行覆盖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值