PHP反序列化基础知识
魔术方法:
注:魔术方法只有在类中被定义以后才可以触发
PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法,这些都是 PHP 内置的方法。
__construct() 当一个对象创建时被调用,
__destruct() 当一个对象销毁时被调用,
__wakeup() 使用 unserialize 时触发
__sleep() 使用 serialize 时触发
__call() 在对象上下文中调用不可访问的方法时触发
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用 isset()或 empty()触发
__unset() 在不可访问的属性上使用 unset()时触发
__toString() 把类当作字符串使用时触发,返回值需要为字符串
__invoke() 当脚本尝试将对象调用为函数时触发
一、序列化基础概念
1.什么是序列化,为什么要序列化
PHP文件执行结束以后会将创建的对象自动销毁,对象序列化以后可以将对象存储起来方便下次的直接使用。
2.PHP序列化和反序列化函数
serialize() 将对象转化为字符串
unserialize() 反过来
3.示例
4.解释说明序列化后的具体内容
O:4:"Test":3:{s:7:"Testa";s:7:"private";s:1:"b";s:6:"public";s:4:"*c";s:9:"protected";}
第一部分:O:4:"Test":3:
O 表示一个对象 object
4 对象名称的长度为4
Test 对象的名称
3 对象有3个属性(变量)
第二部分:s:7:" Test a";s:7:"private";(这里不是空格而是空字符即十六进制\x00,不显示,我们需要在16进制编辑器或者bp中修改)
s:7:"Testa";s:7:"private";
s 变量名字符串string
7 变量名的长度为7 \x00Test\x00a
s 变量值字符串string
7 变量值的长度
private 变量值的内容
第三部分:s:1:"b";s:6:"public";
s:1:"b";s:6:"public";
很基础没有什么特别的需要描述
第四部分:s:4:" * c";s:9:"protected";(这里不是空格而是空字符即十六进制\x00,不显示,我们需要在16进制编辑器或者bp中修改
s:4:"*c";s:9:"protected";
4 变量名的长度为4 \x00*\x00c
5.总结
序列化就是把对象转化为字符串从而便于存储和传输
二、反序列化
注:一定要注意空格,也就是说注意各个字符串之间对应的描述。
结果如下
三、反序列化漏洞原理
当用户的请求在传给反序列化函数 unserialize()之前没有被正确的过滤时就会产生漏洞。因为 PHP允许对象序列化,攻击者就可以提交特定的序列化的字符串给一个具有该漏洞的 unserialize 函数,最终导致一个在该应用范围内的任意 PHP 对象注入。
前提:
1. unserialize 参数用户可控
2. 参数被传递到方法中被执行,并且方法中使用了危险函数。(php 代码执行函数、文件读取函数、文件写入函数等等)。
演示:这里一定注意理解好构造的序列化对象代码包括各种细节
![](https://img2022.cnblogs.com/blog/2727822/202203/2727822-20220322001456357-1553229934.png)
结果如下图 浅提一下这里没有加php代码的结尾分号注意自己去理解上面说的代码
四、经典ctf反序列化题目
解决如下图
我们这里的payload注意将变量的数量从1改成2就可以绕过__wakeup()函数,破坏了代码逻辑的执行顺序从而达到绕过的目的;我们也可以再加真实属性的个数,这样也可以绕过__wakeup函数;将小写的s改为大写的S后就可以解析二进制了我们这里的\00就是二进制的空格。(还是改为%00吧因为后来我又测试了一遍没成功。)