原文地址:PHP反序列化知识点总结 - 随风kali - 博客园
序列化和反序列化先容
serialize()将一个对象转换成一个字符串,unserialize()将字符串还原为一个对象,在PHP应用中,序列化和反序列化一般用做缓存,好比session缓存,cookie等。简单点讲序列化就是把一个对象变为可以传输的字符串,而反序列化就是把字符换换原为对象。
简单例子
这里我们再来看看反序列化后输出字符的含义
首先输出的内容为
O:4:"test":1:{s:7:"suifeng";s:5:"shuai";}
O->object4->object的长度test->object的名称1->object中变量个数s->变量名数据类型7->变量名长度suifeng->变量名S->变量值数据类型5->变量值长度shuai->变量的值
PHP其他数据类型
a - arrayb - booleand - doublei - integero - common objectr - references - stringC - custom objectO - classN - nullR - pointer referenceU - unicode string
PHP常见魔法函数
__construct() //一个对象创建时被调用
__destruct() //一个对象销毁前被调用
__call() //调用类不存在的方法时执行
__callStatic() //调用类不存在的静态方式方法时执行。
__wakeup() //将在反序列化之后立即被调用
__sleep() //在对象被序列化前被调用
__toString() //当一个对象被当做字符串使用时被调用
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问属性
__invoke() //调用函数的方式调用一个对象时的回应方法
__isset() //在不可访的属性上调用isset()或empty()触发
__unset() //在不可访的属性上使用unset()时触发
public、protected、private下序列化对象区别
php v7.x反序列化的时候对访问类别不敏感
public变量
直接变量名反序列化出来
protected变量
\x00 + * + \x00 + 变量名
可以用S:5:"\00*\00op"来代替s:5:"?*?op"
private变量
\x00 + 类名 + \x00 + 变量名
反序列化漏洞形成条件
1、unserialize函数的参数可控
2、后台使用了相应的PHP中的魔法函数
反序列化漏洞原理
我们先运行如下一串代码
PHP语言本身漏洞
还有一种PHP语言本身漏洞遇到某种特点情况导致的反序列化漏洞
如:__wakeup失效引发(CVE-2016-7124)
php版本< 5.6.25 | < 7.0.10
当序列化字符串中,如果表现对象属性个数的值大于真实的属性个数时就会跳过__wakeup()的执行
PHP_session序列化问题
当session_start()被调用或者php.ini中session.auto_start为1时,PHP内部调用会话管理器,访问用户session被序列化以后,存储到指定目录(默以为/tmp)。
PHP中有三种序列化处理器,如下:
处理器
| 对应的存储格式
| php
| 键名 + 竖线 + 颠末serialize()函数反序列化处理的值
| php_binary
| 键名的长度对应的ASCII字符 + 键名 + 颠末serialize()函数反序列化处理的值
| php_serialize(php>=5.5.4)
| 颠末serialize()函数反序列处理的数组
| 配置文件php.ini中含有这几个与session存储配置相关的配置项:
session.save_path="" --设置session的存储路径,默认在/tmp
session.auto_start --指定会话模块是否在请求开始时启动一个会话,默以为0不启动
session.serialize_handler --界说用来序列化/反序列化的处理器名字。默认使用php
session.save_handler="" --设定用户自界说存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式),好比files就是session默认以文件的方式进行存储
且在PHP中默认使用的是PHP引擎,如果想要修改成其他引擎,我们需要添加代码ini_set('session.serialize_handler', '需要设置的引擎'),例:
;
manifest:也就是meta-data,压缩文件的属性等信息,以序列化存储
contents:压缩文件的内容
signature:署名,放在文件末尾
这里有两个关键点,一是文件标识,必须以__HALT_COMPILER();?>结尾,但前面的内容没有限制,也就是说我们可以轻易伪造一个图片文件或者其它文件来绕过一些上传限制;二是反序列化,phar存储的meta-data信息以序列化方式存储,当文件操纵函数通过phar://伪协议解析phar文件时就会将数据反序列化,而这样的文件操纵函数有许多
前提条件
php.ini中设置为phar.readonly=Off
php version>=5.3.0
demo测试
根据文件结构我们来本身构建一个phar文件,php内置了一个Phar类来处理相关操纵
可以很明显看到manifest是以序列化形式存储的
如果现在通过phar://包装器对我们现有的Phar文件执行文件操纵,则其序列化元数据将被反序列化。这意味着我们在元数据中注入的对象被加载到应用步调的范围中。如果此应用步调具有已命名的类AnyClass并且具有魔术方法__destruct()或已__wakeup()界说,则会自动调用这些方法。这意味着我们可以在代码库中触发任何析构函数或唤醒方法。更糟糕的是,如果这些方法对我们注入的数据进行操纵,那么这可能会导致进一步的漏洞。
以下是受影响函数列表
这时使用phar://协议即可
使用条件
phar 文件能够上传
文件操纵函数参数可控, : ,/ phar 等特殊字符没有被过滤
有可用的魔术方法作为"跳板"
反序列化字符逃逸
PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的,同时反序列化的过程中必须严格按照序列化规则才能乐成实现反序列化 。
下面我们来分析一段代码
发现序列化为 O:4:"test":1:{s:7:"suifeng";s:5:"shuai";},也正常进行了反序列化。当我们把序列化结果修改为O:4:"test":1:{s:7:"suifeng";s:5:"shuai";}i:1;s:4:"test"; 后发现还是会正常解析。
但是我们修改其长度就会报错,如 O:4:"test":1:{s:7:"suifeng";s:4:"shuai";}
知道这个特性我们再来分析如下一段代码
|