[toc] 刚打了护网杯,被打自闭了。其中一道laravel用到了PHP反序列化与POP CHAIN
和phar://
。然而PHP反序列化漏洞我也忘完了,这里复习加拓展一波。 复习了一波包包哥的PHP 面向对象与序列化学习
序列化与反序列化
所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。
- 序列化与反序列化与两个函数有关,分别是
serialize()
、unserialize()
这两个函数。 - 一般常用于传递 object ,object对象没法直接传值,所以需要先序列化为一段 字符串,接收方接收到后进行反序列化操作后即可得到原object对象。
- 当序列化对象时,PHP将试图在序列动作之前调用该对象的成员函数 __sleep() ,这就允许对象在被序列化之前 做任何清除操作。类似的,当使用 unserialize() 恢复对象之前,将调用 __wakeup() 成员函数
- 反序列化函数
unserialize()
接收一个string类型的变量,该值为已序列化后的字符串。 - 若被反序列化的变量是一个对象,在成功地重新构造对象之后,PHP会自动地试图去调用
__wakeup()
成员函数 (如果存在的话)。
对象的序列化格式
序列化对象时,不会保存常量的值。对于父类中的变量,则会保留。
1 |
|
输出
1 | O:1:"A":3:{s:11:"\0A\0_private";s:3:"123";s:11:"\0*\0_protect";d:12.34;s:7:"_public";i:456789;} |
漏洞成因
PHP魔法函数一般是以__
开头,通常会因为某些条件而触发不用我们手动调用:
1 | __construct()当一个对象创建时被调用,但在unserialize()时是不会自动调用的。 |
例子
1 |
|
构造test=O:1:"A":1:{s:4:"test";s:28:"<img src=1 onerror=alert(1)>";}
就可以xss。当然还可以结合实际环境造成更大的危害。
反序列化与POP CHAIN
POP CHAIN:把魔术方法作为最开始的小组件,然后在魔术方法中调用其他函数(小组件),通过寻找相同名字的函数,再与类中的敏感函数和属性相关联,就是POP CHAIN 。此时类中所有的敏感属性都属于可控的。当unserialize()传入的参数可控,便可以通过反序列化漏洞控制POP CHAIN达到利用特定漏洞的效果。 通俗点就是:反序列化中,如果关键代码不在魔术方法中,而是在一个类的普通方法中。这时候可以通过寻找相同的函数名将类的属性和敏感函数的属性联系起来。
1 |
|
构造POP链。 protected $ClassObj = new evil();
是不行的,还是要通过__construct
来实例化。 受保护成员变量含有\0
和*
需要URL编码一下。
1 |
|
payload:test=O%3A5%3A%22Smi1e%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00ClassObj%22%3BO%3A6%3A%22unsafe%22%3A1%3A%7Bs%3A12%3A%22%00unsafe%00data%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D
phpggc:收集了一些常见的PHP框架的通用反序列化的小工具链 https://github.com/ambionics/phpggc
phar://
转自Seebug:https://paper.seebug.org/680/#22-demo
概要
通常我们在利用反序列化漏洞的时候,只能将序列化后的字符串传入unserialize(),随着代码安全性越来越高,利用难度也越来越大。但在不久前的Black Hat上,安全研究员Sam Thomas分享了议题It’s a PHP unserialization vulnerability Jim, but not as we know it
利用phar文件会以序列化的形式存储用户自定义的meta-data这一特性,拓展了php反序列化漏洞的攻击面。 该方法在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作。
原理
phar文件结构
在了解攻击手法之前我们要先看一下phar的文件结构,通过查阅手册可知一个phar文件有四部分构成:
a stub
可以理解为一个标志,格式为xxx<?php xxx;__HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>
来结尾,否则phar扩展将无法识别这个文件为phar文件。
a manifest describing the contents
phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是上述攻击手法最核心的地方。
the file contents
被压缩文件的内容。
[optional] a signature for verifying Phar integrity (phar file format only)
签名,放在文件末尾,格式如下:
Demo测试
根据文件结构我们来自己构建一个phar文件,php内置了一个Phar类来处理相关操作。 注意:要将php.ini中的phar.readonly
选项设置为Off,否则无法生成phar文件。 phar_gen.php
1 |
|
可以明显的看到meta-data是以序列化的形式存储的: 有序列化数据必然会有反序列化操作,php一大部分的文件系统函数在通过
phar://
伪协议解析phar文件时,都会将meta-data进行反序列化,测试后受影响的函数如下: 通过一个小demo证明一下 phar_test1.php
1 |
|
其他函数当然也是可行的,当文件系统函数的参数可控时,我们可以在不调用unserialize()的情况下进行反序列化操作,一些之前看起来“人畜无害”的函数也变得“暗藏杀机”,极大的拓展了攻击面。
将phar伪造成其他格式的文件
在前面分析phar的文件结构时可能会注意到,php识别phar文件是通过其文件头的stub,更确切一点来说是__HALT_COMPILER();?>
这段代码,对前面的内容或者后缀名是没有要求的。那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件。
1 |
|
将后缀改为gif进行测试
1 |
|
采用这种方法可以绕过很大一部分上传检测。
利用条件
- phar文件要能够上传到服务器端。
- 如file_exists(),fopen(),file_get_contents(),file()等文件操作的函数要有可用的魔术方法作为”跳板”。
- 文件操作函数的参数可控,且
: / phar
等特殊字符没有被过滤。
漏洞验证
upload_file.php后端检测文件上传,文件类型是否为gif,文件后缀名是否为gif
1 |
|
upload_file.html
1 | <body> |
file_un.php存在file_exists()
,并且存在__destruct()
1 |
|
根据file_un.php写一个生成phar的php文件,在文件头加上GIF89a绕过gif,然后我们访问这个php文件后,生成了phar.phar,修改后缀为gif,上传到服务器,然后利用file_exists,使用phar://执行代码 构造eval.php
1 |
|
访问eval.php生成phar.phar,将后缀改为gif。 然后上传到目录下与
file_un.php
同目录,利用file_un.php
中的危险函数getshell payload:file_un.php?filename=phar://phar.gif/test
Referer
PHP反序列化
PHP CHAIN
phar
利用 phar 拓展 php 反序列化漏洞攻击面 初探phar:// [CTF] DefCamp CTF Qualification 2018: Vulture (Web) Write-up