当我看到unserialize的时候 我知道了 这是一道PHP反序列化的题目了 在这里我先说一下反序列化的常年事故,借用一下freebuf的文章: 什么是反序列化: php程序为了保存和转储对象,提供了序列化的方法,php序列化是为了在程序运行的过程中对对象进行转储而产生的。序列化可以将对象转换成字符串,但仅保留对象里的成员变量,不保留函数方法。 php序列化的函数为serialize。反序列化的函数为unserialize。 基本上都是围绕着 这两个函数来展开的 通俗的说反序列化 和 序列化的意思就是: 序列化:将对象转换成字符串 反序列化:将序列化后的字符串转换为对象 还原 这两个关系相当于一正一反 接下来我们来介绍对象中的魔术方法: __construct: 在创建对象时候初始化对象,一般用于对变量赋初值。 __destruct: 和构造函数相反,当对象所在函数调用完毕后执行。 __toString:当对象被当做一个字符串使用时调用。 __sleep:序列化对象之前就调用此方法(其返回需要一个数组) __wakeup:反序列化恢复对象之前调用该方法 __call:当调用对象中不存在的方法会自动调用该方法。 __get:在调用私有属性的时候会自动执行
__isset()在不可访问的属性上调用isset()或empty()触发
__unset()在不可访问的属性上使用unset()时触发
这道题目我们主要介绍这两个魔术方法: __construct 构造方法 在对象调用的时候触发 在对象调用的时候 会执行__construct里面的代码
__destruct 恰好跟构造方法相反 在对象销毁的时候调用 查看一下
在对象销毁的时候就是结束的时候会调用这个析构方法 我们再来看一下反序列化 和 序列化的这个函数 序列化: 输出为: 为什么输出这么一个字符串 O:2:"AB":1:{s:4:"name";N;} 这个字符串的意思就是: O 代表 object 对象的意思 2 代表2个元素 AB 是对象的名字 S 代表string类型
接下来我来解开这道题:
可以看到isset判断get传过来的参数有没有数据 然后if判断,这里用到了一个过滤的函数 我们不用管 $obj = unserialize($str); 这里使用到了一个反序列化的函数 反序列化 GET传过来的参数 然后我们可以看到这里 当对象结束(销毁)的时候会调用这个函数 我们可以看到if 判断 op === “2” 注意:这是三个等于号 强制给他转换成”1” 在这里我们可以绕过这个限制 利用php弱类型的特性 我们可以想象把op想象成 “ 2” (空格2) 这样子就不会执行后面的这个语句了 然后他调用了$this->process() 这个函数 我们可以看到 如果op == “1” 他会调用write 写入函数 如果op == “2” 的话他会调用查看函数 既然我们要拿flag 当然查看比较好了 上面说过 op == “ 2”(空格2) 因为他会自动转换 注意这次是二个等于号 php比较的时候如果和数字比较会把不是数字的转换成数字在进行比较 相互之间会互相转换! 这样我们会执行这个函数: $this->read(); 这样子就可以拿到flag 肯定有人会问filename要怎么构造? 我们来构造这个语句: 整体的语句是这样子的 我来解释一下: 定义这个类 必须和题目的类名是一样的 Public 这是权限修饰符 最高权限 谁都可以访问的 公共的 我们定义 op = ‘ 2’(空格2) Filename = “flag.php” Contennt 给随便的值都是可以的 然后我们来new 这个对象 这时候我们来 serialize 序列化这个对象 $flag01 = serialize($flag); 因为题目要反序列化 我们写的化当然是要来序列化 序列化的结果就是: O:11:"FileHandler":3:{s:2:"op";s:2:" 2";s:8:"filename";s:8:"flag.php";s:8:"contennt";s:2:"nc";} 序列化之后的结果就是这样子的 这就是我们最终的payload 我们直接把payload放上去执行: 这时候我右键源代码: 这就是flag的值
|