基本概念
把对象转换为字节序列的过程称为对象的序列化;把字节序列恢复为对象的过程称为对象的反序列化。
对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。
序列化和反序列化本身并不存在问题。但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。
常见的php系列化和反系列化方式主要有:serialize,unserialize;json_encode,json_decode
漏洞触发条件
unserialize函数,json_decode函数变量可控,php文件中存在可利用的类,类中有魔术方法
php将所有以 __ (两个下划线) 开头的类方法保留为魔术方法
常用的魔术方法
1 | __construct() 当一个对象创建时自动调用 |
不同属性序列化
Public属性序列化后格式:成员名
Private属性序列化后格式:%00类名%00成员名
Protected属性序列化后的格式:%00*%00成员名
new类名():调用这个类的构造函数初始化对象,类名()这个是构造函数,用来初始化。
在进行类的序列化时 私有属性 会加空白字符类名空白字符;保护属性会加 * (注意空白字符不是空格,空格的16进制为20,空白字符的16进制为00)
在类外定义类中的值
重新定义类中属性的值
通过new 类名 (”对类中的变量重新定义“)
在类的外面定义类中的函数并调用方法
wakeup绕过
1 | __wakeup 使用unserialse()函数时会自动调用 |
OC绕过
OC绕过是通过修改O:数字(C:数字)来绕过正则匹配
例:
1 | if (preg_match('/[oc]:\d+:/',$var)){ |
私有属性绕过
私有属性产生的一些不可见字符如果被过滤掉,可以用字符编码来替换这些字符进行绕过。
例:当反序列化中的属性是private时,在url输入payload时空字符不能被识别,会导致反序列化失败,我们可以在把空字符转换为16进制绕过。
1 | \00就是16进制的chr(0) |
可以看出,当属性为private时,例:private $address = ‘shanxi’,反序列化的时候应为
1 | \00test\00address |
当属性为protected时,例:protected $age = ‘21’,反序列化的时候应该为
1 | \00*\00age |
当代码过滤一些字符的时候例如:flag
,我们可以通过16进制编码把flag编写为16进制,这时我们可以把表示字符串的字符s改为大写,这样它在反序列化的时候就能识别16进制,既可以绕过过滤,又可以成功执行反序列化。
可以看到,当s为大写时,可以成功识别16进制。
unserialize4
cURL会话
1 | 初始化一个新的cURL会话并获取一个网页 |
1 |
|
1 | payload |
字符逃逸
反序列化字符串都是以一";}
结束的,所以如果我们把";}
带入需要反序列化的字符串中(除了结尾处),就能让反序列化提前闭合结束,后面的内容就丢弃了。
在反序列化的时候php会根据s所指定的字符长度去读取后边的字符,我们可以根据需求指定s的长度。
例:字符逃逸将age从13变成35。
PHar反序列化
Phar反序列化可以在不使用php函数unserialize()的前提下,进行php反序列化漏洞。
大多数PHP文件操作允许使用各种URL协议去访问文件路径:如data://
,zlib://
或php://
。
例如常见的
1 | include('php://filter/read=convert.base64-encode/resource=index.php'); |
phar://
也是流包装的一种
phar原理
phar的本质是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是上述攻击手法最核心的地方。
phar文件类似zip和jar,它将PHP文件打包成一个文件然后PHP可以在不解压的情况下去访问这个包里面的php,并执行。
phar文件标识
a stub
可以理解为一个标志,格式为xxx<?php xxx;__HALT_COMPILER();?>
,前面内容不限,但必须以__HALT_COMPILER();?>
来结尾,否则phar扩展将无法识别这个文件为phar文件。
manifest
phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是上述攻击手法最核心的地方。
contents
被压缩文件的内容。
signature
签名,发在文件末尾。
构建phar文件
将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。不同的php.ini文件对应着phpstudy的不同版本。
执行下面的文本生成phar文件
1 |
|
修改php-5.5.38文件,可以看到成功执行,生成phar文件。
打开phar文件
可以看到该phar文件由四部分组成,文件标识(__HALT_COMPILER())
manifest(反序列化内容) ,压缩文件内容,签名。
利用phar文件
1 | 当用phar://访问phar文件时,会触发反序列化,会触发魔法函数。 |
当在setStub添加图片文件头,可以绕过图片头检测。
漏洞利用
只要phar://协议解析文件的时候,就会造成序列化的问题,所以文件操作的函数都可以触发这种序列化:
1 | fileatime / filectime / filemtime |
绕过图片头检测
1 |
|
session反序列化
sseion的简单介绍
PHP session 变量用于存储有关用户会话的信息,或更改用户会话的设置。Session 变量保存的信息是单一用户的,并且可供应用程序中的所有页面使用。
Session 的工作机制是:为每个访问者创建一个唯一的 id (UID),并基于这个 UID 来存储变量。UID 存储在 cookie 中,亦或通过 URL 进行传导。
开启session
在您把用户信息存储到 PHP session 中之前,首先必须启动会话。
注释:session_start() 函数必须位于 标签之前:
终结session
开启session并执行文件
删除文件
1 |
|
session存储方式
PHP中的session中的文件内容默认是以文件的方式来存储的,存储方式就是由配置项session.save_handler来进行确定的。
1 | //以如下代码为例,查看不同存储引擎存储的结果 |
有$_SESSION赋值的反序列化
当网站序列化存储 session
与反序列化读取 session
的方式不同时,就可能导致 session
反序列化漏洞的产生。 一般都是以 php_serialize
序列化存储 session
, 以 PHP
反序列化读取 session
,造成反序列化攻击。
参考文章
1 | PHP session 常见利用点 - 先知社区 https://xz.aliyun.com/t/8221#toc-5 |