php反序列化冷知识复现
前提概要
https://zhuanlan.zhihu.com/p/405838002
之前比赛遇到的题目,根据师傅的回答复现一下
__PHP_Incomplete_Class
当反序列化 __PHP_Incomplete_Class
这个类后,再对其进行序列化时,其属性会消失。
0x01:
首先__PHP_Incomplete_Class
是当反序列化一个不存在的类时出现的类,而__PHP_Incomplete_Class_Name
属性就是反序列化时不存在的类的类名:
1 |
|
而当再次反序列化的时候,内容并没有发生变化,这就说明了当序列化__PHP_Incomplete_Class
时会先去查找属性__PHP_Incomplete_Class_Name
的值,然后进行序列化为相对应的类
而我们可以根据该特点构造相对应的字符串,即构造存在__PHP_Incomplete_Class
类但类中却不存在__PHP_Incomplete_Class_Name
属性,当该字符串经过反序列化和序列化之后就会丢弃类里面的其他属性:
1 |
|
n^3ctf_Ezunser
反序列化未定义的类
直接给了源码:
index.php
1 |
|
0x01:
先审计index.php
第一个点在于spl_autoload_register
,当调用index.php
中没有定义的类的时候就会自动调用myAutoloader($classname)
,其中$classname
就是我们想实例化的类
想当然地我们想调用EvilClass.php
里面的类,所以要include EvilClass.php
,但是文件里还是没有定义EvilClass
,这里就涉及到一个[php反序列的冷知识](PHP序列化冷知识 - 知乎 (zhihu.com))
我们还发现之后的if判断语句中ban掉了Evil
,这就说明在序列化之后的字符串中不能再出现EvilClass
,不过可以直接拿里面的payload
所以payload1:
1 | a:1:{i:0;O:22:"__PHP_Incomplete_Class":1:{s:3:"qwb";O:9:"EvilClass":0:{}}} |
……
fast __destruct
在__wakeup前触发 __destruct
1、如果单独执行
unserialize
函数进行常规的反序列化,那么被反序列化后的整个对象的生命周期就仅限于这个函数执行的生命周期,当这个函数执行完毕,这个类就没了,在有析构函数的情况下就会执行它。
2、如果反序列化函数序列化出来的对象被赋给了程序中的变量,那么被反序列化的对象其生命周期就会变长,由于它一直都存在于这个变量当中,当这个对象被销毁,才会执行其析构函数。
而有时候我们如果提前执行__destruct
(析构函数)就会绕过题目中某些限制,从而产生利用点,比如就可以bypass __wakeup(不过与php版本有关)
提前触发的方法:
1 | 修改属性个数值: |
例子:可以明显看到var_dump执行的顺序是不一样的
反序列化函数闭包
闭包函数也就是匿名函数,而在定义闭包函数的时候就会自动实例化Closure
类
但是直接用php自带的serialize
函数对这个类进行序列化的时候会报错,但是我们可以通过工具去实现闭包函数的序列化
0x01:安装
0x02:使用
之后直接测试吧,可以发现闭包函数直接用unserialize
反序列化之后和opis\closure\unserialize
反序列化的时候的返回值是不一样的但是都可以直接当作函数使用:
1 |
|