1,序列化(串行化):是将变量转换为可保存或传输的字符串的过程;
反序列化(反串行化):就是在适当的时候把这个字符串再转化成原来的变量使用。
这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性。
常见的php系列化和反系列化方式主要有:serialize,unserialize;json_encode,json_decode
在进行类的序列化时 私有属性 会加 空白字符类名空白字符 ; 保护属性会加 *(注意空白字符不是空格,空格的16进制为 20,空白字符的16进制为00)
O:4:"text":2:{s:10:"textfalg";s:4:"gggg";s:5:"*fg";s:4:"hhhh";}
2,
file_put_contents (生成的文件名,字符串) 将一个字符串写入文件
和依次调用 fopen(),fwrite() 以及 fclose() 功能一样
3,gzcompress($data, level)此函数使用ZLIB 数据格式压缩给定的字符串,这是不一样gzip压缩,它包括一些报头数据
data
要压缩的数据。
level
压缩程度。可以给出为0表示无压缩,最高压缩为9。
如果使用-1,则使用zlib库的默认压缩,即6。
4,
这两个是序列化和反序列化PHP中数据的常用函数。
使用JSON格式序列化和反序列化是一个不错的选择:
var_export 函数把变量作为一个字符串输出;eval把字符串当成PHP代码来执行,反序列化得到最初变量的内容。
wddx_serialize_value函数可以序列化数组变量,并以XML字符串形式输出
5 ,file_put_contents() 函数把一个字符串写入文件中
file_get_contents() 把整个文件读入一个字符串中。
该函数是用于把文件的内容读入到一个字符串中的首选方法
6,序列化生成字符串时:对象私有化成员会自动添加了类名,以区分他们是Private 变量;如果是Protected 变量则会添加* 号。并且前缀添加空字节
NULL
被序列化,并产生一个 E_NOTICE
级别的错误。__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。
__invoke() 当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。这个跟python 中的__call__函数类似
static __set_state ( array $properties
) : object
自 PHP 5.1.0 起当调用 var_export() 导出类时,此静态 方法会被调用。
本方法的唯一参数是一个数组,其中包含按 array('property' => value, ...) 格式排列的类属性
__debugInfo ( void ):数组
转储对象以获取应显示的属性时 ,var_dump()调用此方法。如果未在对象上定义该方法,则将显示所有公共属性,受保护属性和私有属性
__clone ( void ) : void
当复制完成时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改 属性的值(如果有必要的话)。
(1)echo (
$obj
) / print($obj
) 打印时会触发(2)反序列化对象与字符串连接时
(3)反序列化对象参与格式化字符串时
(4)反序列化对象与字符串进行==比较时(PHP进行==比较的时候会转换参数类型)
(5)反序列化对象参与格式化SQL语句,绑定参数时
(6)反序列化对象在经过php字符串函数,如 strlen()、addslashes()时
(7)在in_array()方法中,第一个参数是反序列化对象,第二个参数的数组中有toString返回的字符串的时候toString会被调用
(8)反序列化的对象作为 class_exists() 的参数的时候
8,
由前面可以看出,当传给 unserialize() 的参数可控时,我们可以通过传入一个精心构造的序列化字符串,从而控制对象内部的变量甚至是函数。
由前可以看到,unserialize()后会导致__wakeup() 或__destruct()的直接调用,中间无需其他过程。因此最理想的情况就是一些漏洞/危害代码在__wakeup() 或__destruct()中,从而当我们控制序列化字符串时可以去直接触发它们
9,序列化时:对象私有化成员会自动添加了类名,以区分他们是Private 变量;如果是Protected 变量则会添加 * 号。并且前后缀添加空字节
%00类名%00属性名
10,成也布尔,败也布尔‘,布尔类型的true跟任意字符串在‘==’下都成立
11,序列化只序列化数据,不序列化方法,因此在反序列化以后我们如果想正常使用这个对象的话我们必须要依托于这个类要在当前作用域存在的条件。(必须有相应的环境才能进行反序列化)
因为没有序列化方法嘛,我们能控制的只有类的属性,因此类属性就是我们唯一的攻击入口,在我们的攻击流程中,我们就是要寻找合适的能被我们控制的属性,然后利用它本身的存在的方法,在基于属性被控制的情况下发动我们的发序列化攻击(这是我们攻击的核心思想
12,
通过这个简单的例子总结一下寻找 PHP 反序列化漏洞的方法或者说流程
(1)寻找 unserialize() 函数的参数是否有我们的可控点
(2)寻找我们的反序列化的目标,重点寻找 存在 wakeup() 或 destruct() 魔法函数的类
(3)一层一层地研究该类在魔法方法中使用的属性和属性调用的方法,看看是否有可控的属性能实现在当前调用的过程中触发的
(4)找到我们要控制的属性了以后我们就将要用到的代码部分复制下来,然后构造序列化,发起攻击
玩过 pwn 的同学应该对 ROP 并不陌生,ROP 的全称是面向返回编程(Return-Oriented Programing),ROP 链构造中是寻找当前系统环境中或者内存环境里已经存在的、具有固定地址且带有返回操作的指令集,将这些本来无害的片段拼接起来,形成一个连续的层层递进的调用链,最终达到我们的执行 libc 中函数或者是 systemcall 的目的
POP 面向属性编程(Property-Oriented Programing) 常用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链,最终达到攻击者邪恶的目的
说的再具体一点就是 ROP 是通过栈溢出实现控制指令的执行流程,而我们的反序列化是通过控制对象的属性从而实现控制程序的执行流程,进而达成利用本身无害的代码进行有害操作的目的
在 2017 年的 hitcon Orange 的一道 0day 题的解法令人震惊,Orange 通过他对底层的深度理解,为 PHP 反序列化开启了新的篇章,在此之后的 black 2018 演讲者同样用这个话题讲述了 phar:// 协议在 PHP 反序列化中的神奇利用,那么接下来就让我们分析他为什么开启了 PHP 反序列化的新世界,以及剖析一下这个他的利用方法。
(1)首先我们必须有 unserailize() 函数
(2)unserailize() 函数的参数必须可控
这两个是原先存在 PHP 反序列化漏洞的必要条件,没有这两个条件你谈都不要谈,根本不可能,但是从2017 年开始 Orange 告诉我们是可以的
原来 phar 文件包在 生成时会以序列化的形式存储用户自定义的 meta-data ,配合 phar:// 我们就能在文件系统函数 file_exists() is_dir() 等参数可控的情况下实现自动的反序列化操作,于是我们就能通过构造精心设计的 phar 包在没有 unserailize() 的情况下实现反序列化攻击,从而将 PHP 反序列化漏洞的触发条件大大拓宽了,降低了我们 PHP 反序列化的攻击起点。
15,使用 Phar 反序列化的条件是什么
(1)文件上传点
(2)系统文件函数
(3) phar:// 伪协议
16,__sleep() 魔术方法的返回对象必须是一个数组