代码编织梦想


前言

概念:
这其实是为了解决 PHP 对象传递的一个问题,因为 PHP 文件在执行结束以后就会将对象销毁,那么如果下次有一个页面恰好要用到刚刚销毁的对象就会束手无策,总不能你永远不让它销毁,等着你吧,于是人们就想出了一种能长久保存对象的方法,这就是 PHP 的序列化,那当我们下次要用的时候只要反序列化一下就 ok 啦
序列化的目的是方便数据的传输和存储. json 是为了传递数据的方便性.。


一、序列化与反序列化

序列化:
函数 : serialize()
把复杂的数据类型压缩到一个字符串中 数据类型可以是数组,字符串,对象等
序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。
反序列化:
函数: unserialize()
恢复原先被序列化的变量

二、魔术函数

__construct   当一个对象创建时被调用,
__destruct   当一个对象销毁时被调用,
__toString   当一个对象被当作一个字符串被调用。
__wakeup()   使用unserialize时触发
__sleep()    使用serialize时触发
__destruct()    对象被销毁时触发
__call()    在对象上下文中调用不可访问的方法时触发
__callStatic()    在静态上下文中调用不可访问的方法时触发
__get()    用于从不可访问的属性读取数据
__set()    用于将数据写入不可访问的属性
__isset()    在不可访问的属性上调用isset()empty()触发
__unset()     在不可访问的属性上使用unset()时触发
__toString()    把类当作字符串使用时触发,返回值需要为字符串
__invoke()   当脚本尝试将对象调用为函数时触发

1.序列化

class people{

    public $name = 'sam';  

    private $sex = 'man';  

    protected $age = '20';

}

$people1 = new people();

$object = serialize($people);

print_r($object);

定义一个people类,含有公共属性name,私有属性sex,保护属性age。随后实例化一个people1,并对其进行序列化,最后输出结果为:
O:6:"people":3:{s:4:"name";s:3:"sam";s:11:" people sex";s:3:"man";s:6:" * age";s:2:"20";}
在这里插入图片描述

2.反序列化

<?php
class people{

    public $name = 'sam';

    private $sex = 'man';

    protected $age = '20';

}

$people1 = new people();

$object = serialize($people1);
$a=unserialize($object);
#print_r($object);
var_dump($a);

输出为:
object(people)#2 (3) { ["name"]=> string(3) "sam" ["sex":"people":private]=> string(3) "man" ["age":protected]=> string(2) "20" }
这里需要说明一下,对于public的属性无需其他特殊操作,但是对于private属性,描述的时候需要在前后添加空格,或者带上其所在的类名,对于protected属性需要加" * "。

3.几个魔法函数的调用

1. __wakeup() 当unserialize()函数反序列化时,在数据流还未被反序列化未对象之前会调用该函数进行初始化.
2. __destruct() 当对象销毁时触发,也就是说只要你反序列化或者实例化一个对象,当你调用结束后都会触发该函数。

<?php
class star{
    public $a;
    function __wakeup(){
        echo "hi";
    }
    function __destruct(){
        echo "结束了";
    }
}
$s='O:4:"star":1:{s:1:"a";N;}';
unserialize($s);

结果:hi结束了
可以看到,先输出了hi,说明__wakeup()函数先被调用,随后整个反序列化结束,对象被销毁,触发__destruct()函数,输出结束了。
3. __toString() 当一个对象被当作字符串使用时触发。

<?php
class star{
    public $a;
    function __wakeup(){
        echo $this->a;
    }
}
class next{
    function __toString(){
        echo "我在这";
    }
}
$t='O:4:"star":1:{s:1:"a";O:4:"next":0:{}}';
unserialize($t);

结果:我在这 Catchable fatal error: Method next::__toString() must return a string value in /tmp/41bac5636b55eff5c8abea138d605489916c2612abc45fd39fdaa87a827a0e00/main.php on line 5
这里没有retrun,也没有忽略报错,所有有一条报错信息,无关紧要,但是要说的是,__toString()是要又return的,不然会报错。结果显示当类star中的

echo $this->a;

执行时,a被当作一个字符串,此时我将a设置为类next,此时类next作为字符串被调用,所以触发类next中的__toString()函数,输出“我在这”。
4. __invoke() 当类被当作函数调用时触发,看实例。

<?php
class star{
    public $a;
    function __wakeup(){
        $function=$this->a;
        return $function();
    }
}
class next{
    function __invoke(){
        echo "我在这";
    }
}
$t='O:4:"star":1:{s:1:"a";O:4:"next":0:{}}';
unserialize($t);

结果:我在这
分析过程和上面那个函数一样,也是通过反序列化给a赋值,只是赋的不是字符串而是其他类,然后

return $function();

的时候,将类当作函数调用,触发了__invoke()函数输出了“我在这”。
5. __get() 这个函数是当访问不可访问的属性的时候触发,不可访问的属性有两种

  • 私有属性或者保护属性,这种访问受限的属性的时候会触发__get()
  • 属性不存在的时候,也会触发__get()
<?php
class star{
    public $a;
    function __wakeup(){
        return $this->str['str']->source;
    }
}
class next{
    function __get($name){
        echo "我在这";
        return;
    }
}
$t='O:4:"star":2:{s:1:"a";N;s:3:"str";a:1:{s:3:"str";O:4:"next":0:{}}}';
unserialize($t);

结果:我在这
通过str[‘str’]赋值为类next,访问next的source,但是类next中不存在属性source所以触发__get()函数,访问保护属性等同理。
小结:反序列化的过程通过这些魔法函数可以达到我们想到要的操作,尤其是后面3个函数,大家会发现,这三个函数可以达到多个类的连续使用,从而达到链的效果,这也就是反序列化中的pop链的编写,具体pop链的知识可以看下面这篇:

POP链的构造
接下来我们讲一下反序列化的漏洞

三、反序列化漏洞

1.__wakeup( )绕过

(CVE-2016-7124)
反序列化时,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup( )的执行。
影响版本:
PHP before 5.6.25
7.x before 7.0.10

<?php
class star{
    public $a;
    function __wakeup(){
        echo $this->a;
    }
}
$t='O:4:"star":1:{s:1:"a";s:9:"我在这";}';
unserialize($t);

结果:我在这

O:4:"star":2:{s:1:"a";s:9:"我在这";}

结果:无输出
结果显示,当表示属性个数大于真实个数时,__wakeup()函数不执行,被绕过了,通常题目中,__wake()中含有很多限制,通过这个漏洞绕过__wake()可以达到绕过限制的目的。

2.POP链

POP链的构造

总结

1.构造反序列化链
2.魔法函数
3.pop链
4.__wakeup()绕过

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn.net/m0_46587008/article/details/109373942