深入理解PHP Phar反序列化漏洞原理及利用方法(一)

行业资讯 admin 发布时间:2024-04-12 浏览:39 次

概述

Phar反序列化漏洞是一种较新的攻击向量,用于针对面向对象的PHP应用程序执行代码重用攻击,该攻击方式在Black Hat 2018会议上由安全研究员Sam Thomas公开披露。类似于对编译二进制文件的ROP(Return-oriented Programming)攻击,这种类型的漏洞利用PHP对象注入(POI),这是面向对象的PHP代码上下文中的一种面向属性的编程(POP)。

由于其新颖性,这种攻击媒介在过去的几个月中越来越受到安全届的关注,导致在许多广泛部署的平台中,接连发现远程代码执行漏洞,例如:

· Wordpress < 5.0.1 (CVE-2018-20148)

· Drupal 8.6.x, 8.5.x, 7.x  (CVE-2019-6339)

· Prestashop 1.6.x, 1.7.x (CVE-2018-19126)

· TCPDF < 6.2.19 (CVE-2018-17057)

· PhpBB 3.2.3 (CVE-2018-19274)

在本系列文章中,我们的主要目的是讲解Phar反序列化漏洞的内部工作原理,并进行实战操作,尝试利用PhpBB 3.2.3版本平台中的远程代码执行漏洞。

关于Phar文件、DESERIALIATION和PHP包装器

为了更好地理解这个攻击向量是如何工作的,我们首先需要了解Phar文件是什么、反序列化攻击的原理、PHP包装器是什么,以及上述三个概念是如何关联的。

什么是Phar文件?

Phar(PHP Archive)文件是一种使用单一文件格式分发PHP应用程序和库的方法(类似于JAR文件在Java生态系统中的工作方式。这些Phar文件也可以直接包含在我们自己的PHP代码中。在结构上,它们只是用于压缩,具有可选gzip压缩或基于ZIP压缩文件的tar文件),PHP文档中描述的具体细节如下:

1. 存根(Stub):当Phar作为独立应用程序运行时,它是一个PHP代码序列,充当引导程序。其中,至少必须包含以下代码:

<?php __HALT_COMPILER();

2. 描述存档中包含的源文件的清单:该文件属于可选,保存序列化的元数据,这一序列化的块是漏洞利用链中的关键一环,后续我们将详细说明。

3. 源文件:实际的Phar功能。

4. 一种可选签名,用于完整性检查。

了解反序列化漏洞

序列化是指以二进制格式存储对象属性的过程,以允许其传递或存储在磁盘上,因此可以在以后对其进行反序列化和使用。

在PHP中,序列化过程仅会保存对象的属性、类名,但不保存其方法(因此其缩写词是POP)。从安全的角度来看,这被证明是一种明智的设计方案,除了有一个使反序列化过程变得危险的“魔术方法”。

这些函数特定于每个PHP类,具有Double-underscore的前缀名称,并在某些运行时事件上隐式调用。默认情况下,大多数时间什么都不做,开发人员的工作就是定义它们的行为。在我们的实际案例中,有以下两项值得特别关注,因为它们是只能被Phar反序列化触发的因素:

1. __wakeup():在对象的反序列化过程中调用实现;

2. __destruct():在代码中不再适用对象并被垃圾收集器销毁时隐式调用。

接下来,我们具体来看看如何使用此向量,在虚拟的示例中利用易受攻击的代码片段:

# file: dummy_class.php

<?php

/* Lets suppose some serialized data is written on the disk with

loose file permissions and gets read at a later time */

class Data {

  # Some default data

  public $data = array("theme"=>"light", "font"=>12);

  public $wake_func = "print_r";

  public $wake_args = "The data has been read!\n";

  # magic method that is called on deserialization

  public function __wakeup() {

    call_user_func($this->wake_func, $this->wake_args);

  }

}

# acting as main the conditional below gets executed only when file is called directly

if (basename($argv[0]) == basename(__FILE__)) {

  # Serialize the object and dump it to the disk; also free memory

  $data_obj = new Data();

  $fpath = "/tmp/777_file";

  file_put_contents($fpath, serialize($data_obj));

  echo "The data has been written.\n";

  unset($data_obj);

  # Wait for 60 seconds, then retrieve it

  echo "(sleeping for 60 seconds…)\n";

  sleep(60);

  $new_obj = unserialize(file_get_contents($fpath));

}

我们注意到,在反序列化时,__wake方法动态调用对象的$wake_func和$wake_args属性指向的print_r函数。经过简单的运行后,产生以下输出:

$ php dummy_class.php

The data has been written.

(sleeping for 60 seconds…)

The data has been read!

但是,如果在60秒的时间内,我们设法是用自己的序列化数据替换原序列化数据,从而控制反序列化中所要求的的函数呢?下面的代码描述了如何完成此操作:

# file: exploit.php

<?php

require(dummy_class.php);

# Using the existing class definition, we create a crafted object and overwrite the

# existing serialized data with our own

$bad_obj = new Data();

$bad_obj->wake_func = "passthru";

$bad_obj->wake_args = "id";

$fpath = "/tmp/777_file";

file_put_contents($fpath, serialize($bad_obj));

尽管dummy_class.php的源代码没有改变,但是当dummy_class.php正在等待的过程中,如果在60秒的时间内运行上面的代码片段,我们就能够得到一个完美的代码执行。该行为是由序列化对象的动态函数调用产生的,通过对象的属性更改为passthru("id")。

$ php dummy_class.php

The data has been written.

(sleeping for 60 seconds…)

uid=33(www-data) gid=33(www-data) groups=33(www-data),1001(nagios),1002(nagcmd)

在PHP对象注入(POI/反序列化)攻击的上下文中,这些易受攻击的代码序列中包含Gadget或POP链的名称。

PHP包装器:将它们包装在一起

根据PHP文档上的描述,流(Stream)是与文件、网络、数据压缩以及共享一组通用功能和用途的其他操作相关的方式。PHP包装器负责执行处理各种协议的复杂任务,并提供带有协议数据的流接口。这些流通常由文件系统函数使用,例如:fopen()、copy()和filesize()。

使用类似URL的语法方案访问流wrapper://source。PHP提供的最常见流接口是:

· file:// - 访问本地文件系统

· http:// - 访问HTTP(s) URL

· ftp:// - 访问FTP(s) URL

· php:// - 访问多种I/O流

我们需要重点关注的一种流类型是(*drum roll*) phar://包装器。其典型的格式类似于phar://full/or/relative/path,并且包含两个值得关注的属性:

1. 在声明流时,不会检查其文件扩展名,从而使phar文件成为名副其实的多类型候选者。

2. 如果使用phar流作为参数调用文件系统函数,那么Phar的序列化元数据会自动通过设计实现反序列化。

下面是触发Phar反序列化的文件系统函数列表:

copy                file_exists         file_get_contents   file_put_contents  file                fileatime           filectime           filegroup          fileinode           filemtime           fileowner           fileperms          filesize            filetype            fopen               is_dir             is_executable       is_file             is_link             is_readable        is_writable         lstat               mkdir               parse_ini_file     readfile            rename              rmdir               stat                touch               unlink

如何进行Phar反序列化攻击

至此,我们已经拥有了漏洞利用所需的所有组成部分。利用Phar反序列化漏洞所需的条件通常包括:

1. 应用程序源代码(包括第三方库)中存在小工具/弹出链,允许POI利用;大多数情况下,这都是通过检查源代码来发现的。

2. 能够包含本地或远程恶意Phar文件(最常见的一种是,通过文件上传和依赖于多类型(Polyglot))。

3. 在用户控制的Phar包装器上调用文件系统函数的入口点,也可以通过检查源代码发现。

举例来说,我们考虑一个未经过充分过滤的输入字段,用于通过URL设置个人资料图片。攻击者将输入值设置为先前上传的Phar/polyglot,而不是http://地址,例如:phar://../uploads/phar_polyglot.jpg;在服务器端,后端在提供的包装器上执行文件系统调用,例如通过调用file_exists(phar://../uploads/phar_polyglot.jpg)来验证磁盘上是否存在该文件。此时,上传的Phar的元数据被反序列化,并利用Gadget/POP链完成漏洞利用链。

在本博客文章的第二部分,我们将通过在PhpBB 3.2.3(CVE-2018-19274)中利用远程代码执行漏洞,来具体理解这些概念是如何应用的。

在线咨询

点击这里给我发消息售前咨询专员

点击这里给我发消息售后服务专员

在线咨询

免费通话

24h咨询:400-888-8888


如您有问题,可以咨询我们的24H咨询电话!

免费通话

微信扫一扫

微信联系
返回顶部