在本系列的第一章中,我们回顾了在Unitrends Enterprise Backup设备中远程代码执行的两个简单示例。这次,我们将详细介绍另一个含有RCE漏洞的功能,这可能需要更多的分析来找到这个含有漏洞的功能。随着应用程序与Unitrends的更新,接口不会总是显示出如何访问调用漏洞函数; 然而,使用find和grep的简单工具,我们可以跟踪每一次请求。
在这篇文章中,我们不需要大量的跟踪代码。相比上一篇文章中的例子,导航到更改密码页面是比较直观的。通过find/grep
,我们看到/api/includes/restore.php
文件对exec
和shell_exec
进行了几次调用。通过搜索shell_exec
寻找文件,我们来看看下载文件的功能:
功能控制流程大致如下:
$data
中查找’filenames
‘参数。如果找不到返回500。$filenames
设置为$data ['filenames']
,并根据$filenames
中的每个文件名从该数组创建一个字符串。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
文件,我们注意到:
downloadfiles
函数在函数文件 ‘download-files’
的switch/case
语句中调用,内容如下: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 post
是Restores
类的一部分。我们继续使用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包装,并且这个漏洞利用如下所示。
与上面的downloadFiles
漏洞类似,函数downloadFile
没有对文件名进行清理。它只是打开变量$filename
,它在POST请求中传递,读取文件的内容,并将其返回给用户。我们以与上述相似的方式找到了这个LFI漏洞,除了“download-files”
的情况,我们必须从restore.php
中的switch
语句中输入“download
”。
我们在这里用python中创建了一个简单的包装器来自动执行此过程。
我通过发送电子邮件给Unitrends support 披露以上六个问题,并打电话确定他们应该修补的地方。但他们经过几天都没有回复,我决定在support.unitrends.com
上注册一个帐号来管理我开的六个tickets
。我注册了新帐户并导航到“My Profile and Cases”后,我发现我不用额外的花费就能够看到其他客户的support tickets
,电子邮件,员工和附件的消息。
不同程度的support cases
清单(这些不是我的cases,而是其他Unitrends客户的cases)。
在查看support ticket
后,我可以假冒工作人员和客户聊天,甚至关闭cases。
ticket的附件甚至显示了客户正在使用的设备的root
密码。
我适当的公开披露了一部分后,我立即通知了Unitrends他们存在漏洞。我被告知这是一个“我们已经知道的问题”,这需要一个“特定窗口”才能被利用,这是一个特殊的情况,因为我的社区帐户案例“比正常开放的时间长得多”。
我表示对这个说法不赞同,这是不正确的。无论他们是否已经打开了Unitrends ticket
,当时任何用户帐户都可以访问这些数据。
我被告知这些漏洞在Unitrends 9.1.2中得到了解决,在我撰写这篇文章时,网站已经无法免费试用了。我要求发出9.1.2的副本(或至少是一个试用版)来验证补丁,但被拒绝。因此,我没有办法免费的在副本中检查问题是否得到解决。
经过一段披露时间和大量的内部会议后,我们决定向Rhino Security实验室网站发布研究报告。在报告后,Rhino Security实验室与Unitrends联系。 确定了因为他们的交流出现障碍,导致问题不能提交到处理问题的部门。 在这几天之内,Unitrends会修复这个问题,并让我们验证补丁。
这种互动,体现了对组织政策和技术控制需求的测试和验证。员工必须接受专业的安全培训并知道如何看待安全。Unitrends的安全团队迅速的与我们沟通,来修补这些问题,现在他们已经差不多修补了这些漏洞了。
*作者:Dwight Hohnstein,