魔方魔术:

  • construct(), destruct(), call(), callStatic(), get(), set(), isset(), unset(), sleep(), wakeup(), toString(), invoke(), set_state(), clone() 和 __debugInfo() 等方法在 PHP 中被称为魔术方法(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能
  • 注意:PHP 将所有以 (两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以 为前缀。
  • sleep() 和 wakeup()
    public sleep ( void ) : array wakeup ( void ) : void
    serialize() 函数会检查类中是否存在一个魔术方法 sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。
    Note:
    (1)
    sleep() 不能返回父类的私有成员的名字。这样做会产生一个 E_NOTICE 级别的错误。可以用 Serializable 接口来替代。
    (2)sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。
    (3)与之相反,unserialize() 会检查是否存在一个
    wakeup() 方法。如果存在,则会先调用 wakeup 方法,预先准备对象需要的资源。
    (4)
    wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。
  • 访问控制
    PHP 对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。
    public(公有):公有的类成员可以在任何地方被访问。
    protected(受保护):受保护的类成员则可以被其自身以及其子类和父类访问。
    private(私有):私有的类成员则只能被其定义所在的类访问。
  • unserialize() 将已序列化的字符串还原回 PHP 的值。
    序列化请使用 serialize() 函数。
    语法
    unserialize(str)
    参数 描述
    str 必需。一个序列化字符串。
    wakeup()是用在反序列化操作中。unserialize()会检查存在一个wakeup()方法。如果存在,则先会调用__wakeup()方法。

魔方魔术中的wakeup()和sleep():

先写一串代码:

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/573149/1578736467161-6682176f-abc5-423f-9625-b44ec2b74bd4.png)输出的结果是:_**O:7:"myClass":1:{s:9:"myContent";N;}**_

它竟然把一个类给序列化了,也就是把一个类转成了一个字符串,可以传输或者保存下来。



下面修改代码:

大致介绍魔方魔术(绕过wakeup()) - 图1输出的结果是:O:7:”myClass”:1:{s:9:”myContent”;s:8:”my china”;}


序列化后也对应了相应的值,但是现在有个问题,比如我这个变量是个秘密?而且我要把类传给别的地方呢?


下面的代码:

大致介绍魔方魔术(绕过wakeup()) - 图2输出的结果是:O:7:”myClass”:1:{s:9:”myContent”;s:36:”我爱宋祖英,这是一个秘密”;}

我的秘密序列化后还是存在的,可是我不想我的心里话被别人看到。这个时候PHP很贴心,她知道你的问题,所以设置了魔术方法

__sleep() 就表示当你执行serialize()这个序列化函数之前时的事情,就像一个回调函数,所以在这个回调函数里面我们就可以做点事情,来隐藏我的秘密。

大致介绍魔方魔术(绕过wakeup()) - 图3输出的结果是:O:7:”myClass”:1:{s:9:”myContent”;s:18:”这是我的秘密”;}




两个一起用呢:

大致介绍魔方魔术(绕过wakeup()) - 图4



解题:

进行代码分析:

大致介绍魔方魔术(绕过wakeup()) - 图5

代码中的__wakeup()方法如果使用就是和unserialize()反序列化函数结合使用的,这里没有序列化字符串,何来反序列化呢?于是,我们这里实例化xctf类并对其使用序列化(这里就实例化xctf类为对象peak)

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/573149/1578737319010-a0faef36-9a08-4faf-a952-4431694dc5fa.png)

代码执行结果:

大致介绍魔方魔术(绕过wakeup()) - 图6

执行漏洞:

  1. __wakeup()漏洞就是与序列化字符串的整个属性个数有关。当序列化字符串所表示的对象,
  2. 其序列化字符串中属性个数大于真实属性个数时就会跳过__wakeup的执行,从而造成__wakeup()漏洞

因此,我们要反序列化xctf类的同时还要绕过wakeup方法的执行(如果不绕过wakeup()方法,那么将会输出bad requests并退出脚本),就要修改序列化字符串中的属性个数:

当我们将上述的序列化的字符串中的对象属性个数由真实值1修改为3,即如下所示

大致介绍魔方魔术(绕过wakeup()) - 图7

大致介绍魔方魔术(绕过wakeup()) - 图8