一、简介
为了有效地存储或传递数据,同时不丢失其类型和结构,持久化对象,经常需要利用序列化和反序列化函数对数据进行处理。重点在于解决php对象传递和存储的问题。
1、序列化和反序列化
- 序列化函数将各种类型的数据压缩为字符串,该字符串可以存储于任何地方。
- 反序列化函数将可传输的包含字节流的字符串转换回原来的值。
2、php中常使用的序列化和反序列化函数
- serialize()
- unserialize()
- json_encode()
- json_decode()
二、序列化
当序列化对象时,PHP将试图在序列动作之前调用该对象的成员函数 __sleep() ,这就允许对象在被序列化之前做任何清除操作。类似的,当使用 unserialize() 恢复对象之前,将调用 __wakeup() 成员函数。
1、php序列化格式
2、例子
class test
{
private $flag = 'Inactive';
protected $test = "test";
public $test1 = "test1";
public function set_flag($flag)
{
$this->flag = $flag;
}
public function get_flag()
{
return $this->flag;
}
}
$object = new test();
$object->set_flag('Active');
$data = serialize($object);
echo $data;
- 输出:O:4:"test":3:{s:10:"testflag";s:6:"Active";s:7:"*test";s:4:"test";s:5:"test1";s:5:"test1";}
- 说明:
(1)O代表这是一个对象,对象名占4个字符,对象名是“test”,对象有3个属性。
(2)s:10:"testflag";s:6:"Active";前面代表属性名,后面表示属性值。
(3)PHP 的属性的访问权限问题:属性权限不同,对应的序列化表示也有区别。
- public权限:序列化常规思路,例s:5:"test1";s:5:"test1";。
- private权限:该权限是私有权限,也就是说只能 test类使用,在序列化的时候一定要在 private 属性前面加上自己的名字。在私有属性序列化的时候格式是%00类名%00属性名,例s:10:"testflag";s:6:"Active";。
- protected权限:格式是%00%00属性名,例s:7:"test";s:4:"test";。
- 在传入序列化字符串进行反序列化时需要注意补齐两个空字节。O:4:"test":1:{s:10:"%00test%00flag";s:6:"Active";}
- 注意:序列化只序列化属性,不序列化方法。
三、反序列化
unserialize是反序列化函数。若被序列化的变量是一个对象,在重新构造对象之后,会自动调用__wakeup成员函数。
例子:
四、反序列化漏洞利用
- 在反序列化的时候一定要保证在当前的作用域环境下有该类存在。因为我们没有序列化方法,因此在反序列化以后我们如果想正常使用这个对象的话我们必须要依托于这个类要在当前作用域存在的条件。
- 在反序列化攻击的时候也就是依托类属性进行攻击。因为没有序列化方法嘛,我们能控制的只有类的属性,因此类属性就是我们唯一的攻击入口,在我们的攻击流程中,我们就是要寻找合适的能被我们控制的属性,然后利用它本身的存在的方法,在基于属性被控制的情况下发动我们的发序列化攻击。
核心思想:寻找合适的能被控制的属性,利用它本身存在的方法发动攻击。
1、简介
反序列化漏洞的产生主要有以下两个原因:
(1)unserialize函数的参数可控。
(2)存在魔法函数。
2、魔法函数
__construct() //对象创建时触发
__wakeup() //使用unserialize时触发
__sleep() //使用serialize时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当脚本尝试将对象调用为函数时触发
__set_state()
__clone()
__debugInfo()
(1)__sleep()函数和__wakeup()函数
serialize()函数会检查类中是否存在__sleep函数。如果存在,该函数会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该函数未返回任何内容,则NULL被序列化,并产生一个E_NOTICE级别的错误。
__sleep()函数不能返回父类的私有成员的名字。这样做会产生一个E_NOTICE级别的错误。该函数可以用serializable接口来代替。
__sleep函数常用于提交未提交的数据或进行类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,则使用此功能比较好。
unserialize函数会检查是否存在__wakeup函数。如果存在,则会先调用__wakeup函数,预先准备对象需要的资源。
-
__wakeup函数经常用在反序列化操作中,例如,重新建立数据库连接或执行其他初始化操作。
绕过wakeup()方法:CVE-2016-7124漏洞,当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行。漏洞影响版本:PHP5 < 5.6.25 PHP7 < 7.0.10。
(2)__construct()函数和__destruct()函数
construct是构造函数(PHP5),具有构造函数的类会在每次创建新对象时先调用此方法,所以construct函数非常适合在使用对象之前做一些初始化工作。
destruct是析构函数(PHP5),析构函数会在对某个对象的所有引用都被删除或者对象被显式销毁时执行。
(3)__toString()函数
- __toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。
__toString()函数的触发条件:
- (1)echo (obj) 打印时会触发
- (2)反序列化对象与字符串连接时
- (3)反序列化对象参与格式化字符串时
- (4)反序列化对象与字符串进行==比较时(PHP进行==比较的时候会转换参数类型)
- (5)反序列化对象参与格式化SQL语句,绑定参数时
- (6)反序列化对象在经过php字符串函数,如 strlen()、addslashes()时
- (7)在in_array()方法中,第一个参数是反序列化对象,第二个参数的数组中有toString返回的字符串的时候toString会被调用
- (8)反序列化的对象作为 class_exists() 的参数的时候
五、session反序列化漏洞
- PHP在session存储和读取时,都会有一个序列化和反序列化的过程,PHP内置了多种处理器用于存取 $_SESSION 数据,都会对数据进行序列化和反序列化。
六、POP链
POP 面向属性编程(Property-Oriented Programing) 常用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链,最终达到攻击者邪恶的目的。类似于PWN中的ROP,有时候反序列化一个对象时,由它调用的__wakeup()中又去调用了其他的对象,由此可以溯源而上,利用一次次的“gadget”找到漏洞点。
- POP CHAIN:把魔术方法作为最开始的小组件,然后在魔术方法中调用其他函数(小组件),通过寻找相同名字的函数,再与类中的敏感函数和属性相关联,就是POP CHAIN 。此时类中所有的敏感属性都属于可控的。当unserialize()传入的参数可控,便可以通过反序列化漏洞控制POP CHAIN达到利用特定漏洞的效果。
一些有用的POP链中出现的方法:
- 命令执行:exec()、passthru()、popen()、system()
- 文件操作:file_put_contents()、file_get_contents()、unlink()
七、利用 phar:// 拓展 PHP 反序列化的攻击面
1、利用phar文件会以序列化的形式存储用户自定义的meta-data这一特性,拓展了php反序列化漏洞的攻击面。该方法在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。
2、利用条件
- phar文件要能够上传到服务器端。
- 如file_exists(),fopen(),file_get_contents(),file()等文件操作的函数要有可用的魔术方法作为"跳板"。
- 文件操作函数的参数可控,且: / phar等特殊字符没有被过滤。
八、相关题目
1、http://web.jarvisoj.com:32768/index.php
2、ciscn2019 web1- JustSoso
3、https://code-breaking.com/puzzle/7/
4、工具
phpggc:收集了一些常见的PHP框架的通用反序列化的小工具https://github.com/ambionics/phpggc
参考:
1、https://xz.aliyun.com/t/3674
2、https://www.k0rz3n.com/2018/11/19/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E5%B8%A6%E4%BD%A0%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3PHP%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/
3、https://chybeta.github.io/2017/06/17/%E6%B5%85%E8%B0%88php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/4、https://www.anquanke.com/post/id/864525、https://www.smi1e.top/php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%94%BB%E5%87%BB%E6%8B%93%E5%B1%95/