1. PHP(反)序列化
1.1 序列化
将变量转换为可保存或传输的字符串的过程;
1.2 反序列化
在适当的时候把这个字符串再转化成原来的变量使用。
这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性。常见的php系列化和反系列化方式主要有:serialize,unserialize;json_encode,json_decode。
string serialize ( mixed $value )返回字符串,此字符串包含了表示 value 的字节流,可以存储于任何地方。
mixed unserialize ( string $str )对单一的已序列化的变量进行操作,将其转换回 PHP 的值。
HTTP传输数据方式:
XML(XXE)
JSON
1.3 序列化后
1 |
|
1 | O:4:"test":6:{s:10:"public_var";s:7:"chessur";s:16:"0x00*0x00protected_var";i:126;s:17:"0x00test0x00private_var";d:1.26;s:11:"boolean_var";b:1;s:8:"null_var";N;s:5:"array";a:4:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:6;}} |
字母 | 含义 |
---|---|
s | 字符串(string) |
i | 整型(integer) |
b | 布尔(boolean) |
d | 双精度(double) |
N | 空(NULL) |
a | 数组(array) |
1.4 属性
public的属性在序列化时,直接显示属性名
protected的属性在序列化时,会在属性名前增加0x00*0x00
,其长度会增加3
private的属性在序列化时,会在属性名前增加0x00classname0x00
,其长度会增加类名长度+2
2. PHP反序列化漏洞
2.1 漏洞成因
1.用户可输入参数
2.对用户输入的参数没有进行过滤,或过滤规则不完善
3.参数被危险函数使用
2.1.1 魔术方法
1 | __construct(),类的构造函数 |
__construct()和__destruct()会在对象创建或销毁时自动调用;
在对象serialize()过程中,会检查是否具有__sleep()魔术方法。如果存在该方法,则在序列化前执行该方法。__slepp()方法会清理对象,并应该返回一个数组,数组中包含被序列化的对象的所有属性的名称。如果该方法不返回任何内容,则序列化后的字符串将变为N并提示Notice。__sleep()的预期用途是提交需要挂起的数据或执行类似的清理任务。如果有一个非常大的对象,不需要完全保存其所有属性,该功能将非常有用。
在unserialize()过程中,会检查是否具有__wakeup()魔术方法。如果存在该方法,则在反序列化时执行该方法。__wakeup()魔术方法可以重构对象可能具有的任何资源。__wakeup()预期用途是重新建立在序列化期间可能已丢失的任何数据库连接,并执行其他重新初始化任务。
1 |
|
1 | //OUTPUT |
__autoloading()
传统的PHP只能unserialize()定义过的类,意味着每个PHP文件都需要包含很多文件,在当前主流的PHP框架中,都采用了__autoloading()自动加载类来完成这项繁重的工作。
在简化了工作的同时,页为序列化漏洞造成了便捷。
2.2.2 魔术方法执行顺序
__wakeup()>__toString()>__destruct()
3. CTF示例
1 | //index.php |
1 | //hint.php |
在index.php中,通过右键查看源代码,可以看到提示
1 | <!-- |
当user的值为welcome to the bugkuctf
时,会进入下一步判断,这里使用php://input
file会在可以传入一个文件进行本地包含,提示了包含hint.php
,所以直接包含hint.php
,包含之后发现页面没有变化,因为里面的内容被解析了,使用php://filter/read=convert.base64-encode/resource=hint.php
读取文件,再将内容进行base64解码,可以获得上面的hint.php,再通过伪协议获得index.php的内容,可以看到会对password进行反序列化,再看hint.php中Flag类在被转化为字符串时,可以读取并输出文件内容,所以可以写个Flag类的序列化字符串,让它在echo的时候,输出flag.php中的内容。
由于需要Flag类,所以$file需要包含hint.php
页面。
构造序列化字符串
1 | O:4:"Flag":1:{s:4:"file";s:8:"flag.php";} |
4. PHP自身漏洞(CVE-2016-7124)
4.1 漏洞介绍
触发该漏洞的PHP版本为PHP5小于5.6.25或PHP7小于7.0.10。漏洞可以简要的概括为:当序列化字符串中表示对象个数的值大于真实的属性个数时会跳过__wakeup()的执行。
4.2 Demo
1 | //demo.php |
由于__wakeup()的执行顺序在__destruct()之前,所以__wakeup()会将对象内的所有属性设为NULL,在__destruct()执行时,没有内容会写到文件中。
但使用漏洞,可以跳过__wakeup(),直接执行__destruct(),这样可以将属性内容写入文件中。
payload
1 | test=test=O:4:"test":2:{s:4:"name";s:18:"<?php phpinfo();?>";} |
执行后会报错,但是数据会被写入到文件中
5. PHP反序列化漏洞防御
1.要严格控制用户输入的参数,坚持用户所输入的信息都是不可靠的原则
2.要对于unserialize后的变量内容进行检查,以确定内容没有被污染
3.对要使用变量的函数进行检查。
6. 参考
[1] PHP反序列化漏洞
[2] 浅谈php反序列化漏洞
[3] php反序列化总结