0x01 前言

在本系列的第一章中,我们回顾了在Unitrends Enterprise Backup设备中远程代码执行的两个简单示例。这次,我们将详细介绍另一个含有RCE漏洞的功能,这可能需要更多的分析来找到这个含有漏洞的功能。随着应用程序与Unitrends的更新,接口不会总是显示出如何访问调用漏洞函数; 然而,使用find和grep的简单工具,我们可以跟踪每一次请求。

0x02 /API/INCLUDES/RESTORE.PHP的RCE(CVE-2017-7280)

在这篇文章中,我们不需要大量的跟踪代码。相比上一篇文章中的例子,导航到更改密码页面是比较直观的。通过find/grep,我们看到/api/includes/restore.php文件对execshell_exec进行了几次调用。通过搜索shell_exec寻找文件,我们来看看下载文件的功能:

restore_php_code1

功能控制流程大致如下:

  1. $data中查找’filenames‘参数。如果找不到返回500。
  2. $filenames设置为$data ['filenames'],并根据$filenames中的每个文件名从该数组创建一个字符串。
  3. 通过将filenames加载到另一个php文件中来获取文件的大小。

这里的问题是,如果想$data ['filenames']中的所有filenames都被感染,可以通过上面步骤3中的shell_exec来实现代码执行。现在我们已经找到了有问题的代码,但我们还需要找到从Web界面访问它的方法。为了做到这一点,我使用了grep命令行工具(在这个例子中将会介绍),但是无疑有更好的方法来实现和自动执行这样的任务。

~/scratch/html$ grep -r "downloadFiles(" . | tr -s [:space:]
./ui/app/recover/filerecovery/fileLevelRecoveryController.js: $scope.downloadFiles();
./api/includes/restore.php: return $this->downloadFiles($data, $sid);
./api/includes/restore.php: // which therefore invokes 'download-files' to take the downloadFiles() path. 
What a nested mess!
./api/includes/restore.php: function downloadFiles($data, $sid) {
./api/includes/restore.php: // get the source's max size limit and let the target verify it in
 downloadFiles() once it adds up all the file sizes.

我们看到从同一个文件调用的downloadFiles函数。重新打开restore.php文件,我们注意到:

public function post($which, $data, $sid)
{
    global $Log;

    $sid = $sid !== false ? $sid : $this->BP->get_local_system_id();
    $result = array();
    if (is_string($which[0])) {
        switch ($which[0]) {
        ... other cases ...

        case 'download-files':
            if (isset($data['id']) && $data['id'] !== false) {
                return $this->downloadFilesTargetDir($data, $sid);
            } else {
                return $this->downloadFiles($data, $sid);
            }
            break;

function postRestores类的一部分。我们继续使用grep来进行搜索,用“new”关键字搜索每个declaration,来找到Restores类的事例。

~/scratch/html$ grep -rH "new Restores" . | tr -s [:space:]
./api/includes/appliance.php: $restores = new Restores($this->BP);
./api/includes/appliance.php: $restores = new Restores($this->BP);

appliance.php里面,我们看到一个名为post_restore的短函数中的class declaration

public function post_restore($which, $data, $sid)
{
    require_once('restore.php');
    $restores = new Restores($this->BP);
    return $restores->post($which, $data, $sid);
}

./api/index.php中,我们看到post_restore,并由execute_post函数执行。此功能负责根据API端点命令发送POST请求,由以下switch/case代码段指定:

// Check access scheme
$method = $request[0];

... code dealing with request validation and var inits ...

switch ($controller->getMethod()) {
    case 'get':
        $status = 200;
        $Log->enterMethod("GET", $request[0], $data, $sid);
        $body = execute_get($request, $data, $sid, $systems);
        break;
    case 'post':
        // adding item returns Created (201)
        $status = 201;
        $Log->enterMethod("POST", $request[0], $data, $sid);
        $body = execute_post($request, $data, $sid, $systems);
        break;

...



function execute_post($request, $data, $sid, $systems)
{

    ... variable declarations ...

    $method = $request[0];
    switch($method) {
        case 'restore':
            $which = -1;
            if (isset($request[1])) {
                $which = array_splice($request, 1);
            }
            if (is_array($which) and $which[0] == "archive") {
                $which = array_splice($which, 1);
                $body = $archive->restore($which, $data, $sid);
            } else {
                $body = $appliance->post_restore($which, $data, $sid);
            }
            break;

在这里回顾一下,在restore.php中发现的问题函数,然后back-traced这个函数调用,查看它大部分的Restores class object,发现它被实例化并在appliance.php中调用。这是从通过execute_post调用POST请求的主要api/index.php文件调用的。如果我们访问/api/restore/download-files端点,那么我们将会成功地调用我们的函数。剩下的只是将数据(在本例中为“filenames”)作为filenames的JSON数组传递。

这个漏洞的一个注意事项是,我们在命令字符串中填充的数据被字符串文字(或单引号)封装,这些字符串用作我们输入的伪sanitization。一个例子是,命令 ‘`sleep 10`’会将`sleep 10`打印到终端,但“`sleep 10`”将会使终端休眠十秒钟。为了规避这种保护,我们使用换行符直接执行命令。我们的恶意文件名称环境“\ n”会导致终端将其解释为::

sudo rflr_manage.php –-get-size –-filenames ‘(new line returns this command)
     $FILENAME (new line submits our malicious filename)‘

以下是从Burp拦截器捕获的有效载荷的一个示例。

这导致文件/tmp/pwnd被创建。这个Web环境请求创建了一个python包装,并且这个漏洞利用如下所示。

2

0x03 UNITRENDS <9.1.1的LFI漏洞(CVE-2017-7282)

与上面的downloadFiles漏洞类似,函数downloadFile没有对文件名进行清理。它只是打开变量$filename,它在POST请求中传递,读取文件的内容,并将其返回给用户。我们以与上述相似的方式找到了这个LFI漏洞,除了“download-files”的情况,我们必须从restore.php中的switch语句中输入“download”。

3

我们在这里用python中创建了一个简单的包装器来自动执行此过程。

4

0x04 SUPPORT.UNITRENDS.COM上不受限制的TICKET访问

我通过发送电子邮件给Unitrends support 披露以上六个问题,并打电话确定他们应该修补的地方。但他们经过几天都没有回复,我决定在support.unitrends.com上注册一个帐号来管理我开的六个tickets。我注册了新帐户并导航到“My Profile and Cases”后,我发现我不用额外的花费就能够看到其他客户的support tickets,电子邮件,员工和附件的消息。

不同程度的support cases清单(这些不是我的cases,而是其他Unitrends客户的cases)。

5

在查看support ticket后,我可以假冒工作人员和客户聊天,甚至关闭cases。

6

ticket的附件甚至显示了客户正在使用的设备的root密码。

7

我适当的公开披露了一部分后,我立即通知了Unitrends他们存在漏洞。我被告知这是一个“我们已经知道的问题”,这需要一个“特定窗口”才能被利用,这是一个特殊的情况,因为我的社区帐户案例“比正常开放的时间长得多”。

我表示对这个说法不赞同,这是不正确的。无论他们是否已经打开了Unitrends ticket,当时任何用户帐户都可以访问这些数据。

0x05 披露和补救

我被告知这些漏洞在Unitrends 9.1.2中得到了解决,在我撰写这篇文章时,网站已经无法免费试用了。我要求发出9.1.2的副本(或至少是一个试用版)来验证补丁,但被拒绝。因此,我没有办法免费的在副本中检查问题是否得到解决。

经过一段披露时间和大量的内部会议后,我们决定向Rhino Security实验室网站发布研究报告。在报告后,Rhino Security实验室与Unitrends联系。 确定了因为他们的交流出现障碍,导致问题不能提交到处理问题的部门。 在这几天之内,Unitrends会修复这个问题,并让我们验证补丁。

这种互动,体现了对组织政策和技术控制需求的测试和验证。员工必须接受专业的安全培训并知道如何看待安全。Unitrends的安全团队迅速的与我们沟通,来修补这些问题,现在他们已经差不多修补了这些漏洞了。

 

*作者:Dwight Hohnstein

源链接

Hacking more

...