1.前言:总结一下序列化和反序列化的入门知识

__sleep 与 __wakeup
序列化: 将一个对象转化成字符串
反序列化:将一个字符串转换成对象

2.魔术方法:

__construct: 在创建对象时候初始化对象,一般用于对变量赋初值。
__destruct: 和构造函数相反,当对象所在函数调用完毕后执行。
__toString:当对象被当做一个字符串使用时调用。
__sleep:序列化对象之前就调用此方法(其返回需要一个数组)
__wakeup:反序列化恢复对象之前调用该方法
__call:当调用对象中不存在的方法会自动调用该方法。
__get:在调用私有属性的时候会自动执行
__isset():在不可访问的属性上调用isset()或empty()触发
__unset():在不可访问的属性上使用unset()时触发

3.PHP序列化

PHP序列化的函数为:serialize()

<?php
class peak{
    public $name = "lishirong";
    protected $sex = "man";
    private $age = "18";
    }
$a = new peak;
echo serialize($a);
?>

PHP序列化

序列化结果:
O:4:"peak":3:{s:4:"name";s:9:"lishirong";s:6:"*sex";s:3:"man";s:9:"peakage";s:2:"18";}
0:表示对象(object)
  4:对象名称长度
     peak:对象名称
           3:表示对象里属性的个数
              s:属性名类型
                4:属性名长度
                   name:属性的名称
                              lishirong:属性值

注:这个属性的值可有可无,没有赋值,那么序列化后就没有该值
这里可能不理解序列化后的sex和age,这个是由访问控制修饰符导致的(访问修饰符的不同,序列化后的属性名的长度和属性名会有所不同。
举个例子:

public(公有)
protected(受保护)
private(私有)
各访问修饰符序列化后的区别:
public:属性被序列化的时候属性名还是原来的属性名,没有任何改变
protected:属性被序列化的时候属性名会变成%00*%00属性名,长度跟随属性名长度而改变
private:属性被序列化的时候属性名会变成%00类名%00属性名,长度跟随属性名长度而改变

注:这里的%00也就是表示空字符,在url中用%00表示,在hex中表示\x00
Tip:%00和\x00也就是我们常说的00截断

4.serialize()函数:

serialize()函数有个规定,在序列化对象时,如果对象存在有__sleep魔术方法,那么,在序列化对象时__sleep魔术方法会优先调用,然后再继续执行序列化操作

<?php
class peak{
    public $name = "1stPeak";
    public $sex = "man";
    public $age = "18";
    public function __sleep(){
        return array('age');
    }
}
$a = new peak;
echo serialize($a);
?>

结果:O:4:"peak":1:{s:3:"age";s:2:"18";}

如上所示:只要序列化时执行了__sleep,就只会序列化__sleep中的属性

5.PHP反序列化

反序列化,顾名思义,就是将序列化后的字符串还原
unserialize()也有和serialize()相对应的魔术方法__wakeup,反序列化时,会优先检查是否存在__wakeup魔术方法,如果存在,就会优先调用__wakeup方法

<?php
class Demo
{
    public $flag = 'flag{aaaaaa}';
    public function __wakeup()
    {
        $this->flag = "bbbbbb";
        echo $this->flag;
    }
}
$a = new Demo();
$b = serialize($a);
echo $b."\n";
echo unserialize($b);
?>

输出:O:4:"Demo":1:{s:4:"flag";s:12:"flag{aaaaaa}";}
bbbbbb

一般做CTF题目时绕过的方法就是:先序列化字符串,然后使序列化后字符串中属性的个数大于真实对象中属性的个数,即可绕过。

6.例题(来源XCTF)

class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=

我们对其进行序列化:

<?php
class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
    }
}
$a = new xctf;
echo serialize($a);
?>

结果为:O:4:"xctf":1:{s:4:"flag";s:3:"111";}

将序列化后字符串中属性的个数由1改为2,大于真实属性个数1,即可绕过__wakeup
注:修改s(属性类型)或属性名长度也可绕过执行__wakeup,因为你构造的属性和真实的属性不符;
但修改属性值是不会绕过__wakeup的,所以综述:只有改变属性个数、属性类型、属性名才可以绕过!

最后修改:2021 年 09 月 21 日 07 : 37 PM
如果觉得这篇文章不错,不妨赏我碎银几两。