本文主要介绍1)序列化与反序列化基础 2)反序列化漏洞与魔法函数 3)CVE-2016-7124 4)Session反序列化漏洞 5)phar伪协议触发php反序列化 6)补充:phar伪协议绕过WAF
(一) 序列化与反序列化基础
serialize()将一个对象转换成一个字符串
unserialize()将字符串还原为一个对象
serialize()
在php中创建了一个对象后,可通过serialize()把这个对象转变成一个字符串,这有利于存储或传递PHP的值,同时不丢失其类型和结构。
序列化示例代码
1 | <?php |
序列化数据格式说明
1 | O:6:"lltest":1:{s:4:"test";s:8:"hello123";} |
unserialize()
unserialize()可以从序列化后的字符串中恢复为原来的对象(object)
反序列化示例代码
1 | <?php |
(二) 反序列化漏洞与魔法函数
漏洞原理
PHP调用unserialize()后会自动调用魔术方法__wakeup()和__destruct()。理想的情况就是当魔术方法__wakeup()或__destruct()中存在漏洞代码或者变量可控等,就可能导致远程命令执行(RCE)等漏洞。也可以间接调用或者利用普通成员方法-相同函数名称触发漏洞。
魔术方法
PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法。
http://php.net/manual/zh/language.oop5.magic.php
1 | __wakeup():当调用unserialize()函数时,unserialize() 会检查是否存在一个\__wakeup() 方法。如果存在,则会先调用\__wakeup 方法,预先准备对象需要的资源。 |
反序列化时魔术方法调用示例
PHP调用unserialize()后会自动调用魔术方法__wakeup() 和__destruct()
1 | <?php |
执行代码会输出如下,说明调用unserialize()后会自动调用魔术方法__wakeup() 和__destruct()1
2
3__wakeup
lltest Object ( [test] => hello123 )
__destruct
__wakeup() 或__destruct()
PHP调用unserialize()后会自动调用魔术方法__wakeup()和__destruct()
__wakeup()漏洞代码示例
PHP调用unserialize()后会自动调用魔术方法__wakeup(),执行__wakeup()中的assert,相当于执行assert(“phpinfo();”);
测试代码
1 | <?php |
生成POC
生成序列化POC,要求对象名/变量名(lltest/code) 与要触发代码的一样
1 | <?php |
间接调用
如果unserialize()中并不会自动调用的魔术函数,如__construct(),是不是就没有利用价值呢?不是。有时反序列化一个对象时,由它调用的__wakeup()中又去调用了其他的对象,由此可能层层间接调用触发漏洞。
比如__construct()方法中存在漏洞代码,正好在__wakeup()中new创建对象,所以unserialize()时 间接触发漏洞代码
测试代码
1 | <?php |
生成POC
1 | <?php |
利用普通成员方法-相同函数名称
当漏洞/危险代码存在类的普通方法中,就不能通过“自动调用”来触发漏洞。这时的利用方法:寻找相同的函数名,把敏感函数和类联系在一起
测试代码
1 | 直接访问u2.php 执行输出hello 执行class normal 中的 function action() |
1 | <?php |
生成POC
1 | <?php |
(三) CVE-2016-7124
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-7124
当序列化字符串中表示对象个数的值大于真实的属性个数时会跳过__wakeup()的执行。
存在该漏洞的PHP版本为PHP5小于5.6.25或PHP7小于7.0.10。
典型漏洞:SugarCRM v6.5.23 PHP反序列化对象注入
漏洞测试
测试版本:PHP Version 5.5.381
2
3
41)如果请求CVE-2016-7124.php?www=O:6:"lltest":1:{s:4:"test";s:16:"<?php phpinfo();";} 执行\__wakeup() 方法清除了对象属性,把$test值清空,在test.php写入内容为空
2)如果请求CVE-2016-7124.php?www=O:6:"lltest":99:{s:4:"test";s:16:"<?php phpinfo();";}
对象个数设为99 跳过\__wakeup() 在test.php写入<?php phpinfo();
CVE-2016-7124.php
1 | <?php |
(四) Session反序列化漏洞
漏洞原理
http://php.net/manual/zh/function.session-start.php
http://php.net/manual/zh/function.session-set-save-handler.php
http://php.net/manual/zh/session.configuration.php#ini.session.serialize-handler
1)Session反序列化漏洞,主要是由于: 当PHP在反序列化取出已存储的$_SESSION数据时所使用的处理器与之前序列化存储$_SESSION所使用的处理器不一样,会导致数据无法正确反序列化,可能导致存在漏洞。(存$_SESSION-序列化,取$_SESSION-反序列化)
2)当会话自动开始或者通过 session_start() 手动开始的时候, PHP 内部会调用会话管理器的 open 和 read 回调函数。 通过 read 回调函数返回的现有会话数据(使用特殊的序列化格式存储), PHP 会自动反序列化数据并且填充 $_SESSION 超级全局变量。
3)PHP 内置了多种处理器用于存取 $_SESSION 数据时会对数据进行序列化和反序列化(存$_SESSION-序列化,取$_SESSION-反序列化),常用的有以下三种,对应三种不同的处理格式:【session.serialize_handler 配置选项】1
2
3
4处理器 对应的存储格式
php (默认):键名 + 竖线 + 经过 serialize() 函数序列化处理的值。如:lltest|s:6:"qwe123";
php_serialize (php>=5.5.4):经过 serialize() 函数反序列处理的数组。如:a:1:{s:6:"lltest";s:6:"qwe123";}
php_binary:键名的长度对应的 ASCII 字符 + 键名 + 经过 serialize() 函数序列化处理的值。如:lltests:6:"qwe123";
4)通过竖线|构造POC。
在存储$_SESSION数据时如果用的处理器是php_serialize ,通过竖线|构造POC,写入含有恶意代码的序列化数据。
在读取$_SESSION数据时如果用的处理器是 php 的话,会将竖线|后面的数据进行反序列化,从而触发漏洞。
漏洞测试
1 | 1) 先请求sess1.php?www=|O:6:"lltest":1:{s:3:"www";s:16:"<?php phpinfo();";} |
代码示例1 sess1.php
1 | sess1.php |
代码示例2 sess2.php
1 | sess2.php |
(五) phar伪协议触发php反序列化
漏洞原理
phar文件本质上是一种压缩文件,能够以序列化的形式存储用户自定义的meta-data,php大部分的文件系统函数在通过phar://伪协议解析phar文件时,会将meta-data进行反序列化,从而导致触发漏洞。
受影响的函数如下:
fileatime、filectime、file_exists、file_get_contents、file_put_contents、file、filegroup、fopen、fileinode、filemtime、fileowner、fileperms、is_dir、is_executable、is_file、is_link、is_readable、is_writable、is_writeable、parse_ini_file、copy、unlink、stat、readfile、md5_file、filesize
phar文件结构
http://php.net/manual/en/phar.fileformat.phar.php
一个phar文件有四部分构成:
1)a stub
可以理解为一个标志,格式为xxx<?php xxx; __HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。
2)a manifest describing the contents
phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是上述攻击手法最核心的地方。
3) the file contents
被压缩文件的内容。
4) [optional] a signature for verifying Phar integrity (phar file format only)
签名,放在文件末尾
漏洞测试
说明:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件
1)先访问phar1.php 文件,生成lltest1.phar文件,并以反序列化形式写入payload <?php phpinfo();
2)请求phar2.php?www=phar://./lltest1.phar/test.txt
最终生成lltest6.php文件,内容为<?php phpinfo();
在1)生成lltest1.phar之后,也可以将lltest1.phar文件改成任意后缀如jpg,然后请求phar2.php?www=phar://./lltest1.jpg/test.txt
phar://路径/lltest1.phar/test.txt
代码示例1 phar1.php
1 | <?php |
代码示例2 phar2.php
1 | <?php |
(六) 补充:phar伪协议绕过WAF
使用phar://伪协议可Bypass一些waf,大多数情况下配合文件包含一起使用。
1)必须先把pharbypass1.php压缩。
或者压缩完之后,修改为其他任意后缀 如jpg 来绕过上传限制等
然后修改pharbypass2.php为include(‘phar://./pharbypass1.jpg/pharbypass1.php’)
2)请求pharbypass2.php,会include文件pharbypass1.zip中的pharbypass1.php 执行system(‘whoami’)
pharbypass1.php
1 | <?php system('whoami'); |
pharbypass2.php
1 | <?php |
参考
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/ 浅谈php反序列化漏洞
https://www.anquanke.com/post/id/159206 四个实例递进php反序列化漏洞理解
https://paper.seebug.org/39/ SugarCRM v6.5.23 PHP反序列化对象注入漏洞分析
http://www.vuln.cn/6413 PHP Session 序列化及反序列化处理器设置使用不当带来的安全隐患
https://paper.seebug.org/680/ 利用 phar 拓展 php 反序列化漏洞攻击面
https://xz.aliyun.com/t/3692 Phar的一些利用姿势