应用程序与文件系统的交互始终是高度安全敏感的,因为较小的功能漏洞很容易成为可利用漏洞的来源。这种观察在web文件管理器的情况下尤其正确,其作用是复制完整文件系统的功能并以透明的方式将其公开给客户端的浏览器。
elFinder 是一种流行的 Web 文件管理器,常用于 CMS 和框架,例如 wordPress/ target=_blank class=infotextkey>WordPress 插件 (wp-file-manager) 或 Symfony 包,以允许对本地和远程文件进行简单操作。在过去,elFinder一直是针对不安全配置或实际代码漏洞的活跃攻击的一部分。因此,elFinder的发布带有一个安全的默认配置,以防止攻击者进行任何恶意使用。
作为对广泛部署的开源项目的定期评估的一部分,研究人员在elFinder中发现了多个新的代码漏洞。在以下关于web文件管理器常见代码漏洞的案例研究中,研究人员发现了五个不同的漏洞链,并演示了如何利用它们来获得对底层服务器及其数据的控制。研究人员还将讨论一些稍后由供应商实现的补丁,以展示如何在用户的代码中防止它们。
我们在开发分支上工作,提交 f9c906d。调查结果也在 2.1.57 版中得到证实;都影响默认配置(除非本文另有说明)并且不需要事先认证。正如研究人员所提到的,利用这些漏洞会让攻击者在安装elFinder的服务器上执行任意php代码,最终导致其受到攻击。
研究人员在这篇文章中讨论的发现均分配了 CVE-2021-32682,并成功地利用了这些发现来获得代码执行:
删除任意文件;
移动任意文件;
PHP文件上传;
参数注入;
竞争条件(Race Condition);
所有这些漏洞类在向用户公开文件系统的软件中都非常常见,并且可能影响到大量产品,而不仅仅是elFinder。
elFinder发布了2.1.59版本,以解决研究人员负责披露的所有bug。毫无疑问,这些漏洞也会被广泛利用,因为针对旧版本的漏洞已经被公开发布,而连接器文件名是试图破坏网站时要查找的路径汇编的一部分。因此,研究人员强烈建议所有用户立即将elFinder升级到最新版本。
elFinder带有一个用PHP编写的后端(也称为连接器)和一个用html和JAVAScript编写的前端。连接器是将前端代码的操作分派给右后端代码以实现文件系统特性的主要脚本。连接器可以配置为禁止危险操作,将上传限制为特定的 MIME 类型:默认安装中有两种不同的类型。研究人员在所谓的“最小”连接器中发现了漏洞。它只允许镜像和纯文本上传,FTP是唯一支持的远程虚拟文件系统:这可能是最安全的,也是最有可能部署的。
为了更好地理解研究人员将用来演示发现的代码片段,研究人员将首先描述elFinder的路由是如何工作的。与许多现代PHP应用程序一样,连接器(例如connector.minimal. PHP)是唯一的入口点。它声明配置指令和闭包,然后实例化elFinder(核心)和elFinderConnector (elFinder和传输通道之间的接口,这里是HTTP)。
属性elFinder::$commands包含了每个有效的操作和预期的参数:
php/elFinder.class.php
用户可以通过PATH_INFO、GET或POST向cmd参数提供所需的命令参数来调用这些命令,在每个命令处理程序中,使用$args访问参数。
为了允许远程文件系统(FTP、Dropbox等)与本地文件系统一起使用,elFinder实现了一个文件系统抽象层(elFinderVolumeDriver) ,所有驱动程序都在该层之上构建。然后文件通过它们的卷名(例如t1_是垃圾,l1_是默认的本地卷)和url安全的名称Base64来引用。
首先深入研究一个任意文件删除漏洞链,它由两个不同的漏洞组成。
PHP 核心不提供运行后台线程或执行同步和进程间通信的有效方法。 elFinder 试图通过大量使用临时文件和请求后挂钩来平衡这一点。例如,用户可以通过调用同名方法来终止正在进行的操作:
php/elFinder.class.php
可以看到,[1]和[2]存在一个代码漏洞:用户控制的参数在没有事先检查的情况下被连接到一个完整的路径中。对于[1],它最终会创建一个名称完全可控的空文件,而在[2]中,它可以用来删除任意文件。这两个漏洞的SonarCloud漏洞都是可用的:[1]和[2]。
由 [1] 生成的文件名将以 elfreq 为前缀,在路径遍历攻击中,如果路径中的任何足迹不存在或不是目录,POSIX系统将无法解析路径。例如,解析/tmp/i_do_not_exist/../或/ tmp / i_am_a_file / . ./将分别在ENOENT和ENOTDIR中运行失败。这个先决条件使得利用这两个漏洞不可能按原样进行,并且需要另一个漏洞,例如创建任意目录的能力。
然后攻击者可以查看命令mkdir并发现允许这种行为的原语,下面是它的顶层处理程序,出现在它通过文件系统抽象层之前:
php/elFinder.class.php
elFinderVolumeDriver 中存在一个通用实现来处理应该创建的卷和路径。它将使用文件系统上的卷绝对路径作为第一个参数和目标名称作为第二个参数调用 [1] 中特定于卷的实现:
php/elFinderVolumeDriver.class.php
它的定义如下:
php/elFinderVolumeLocalFileSystem.class.php
elFinderVolumeLocalFileSystem::_joinPath() 只是将两个值连接起来,导致路径遍历漏洞。这提供了在本地文件系统上创建任意空文件夹的原语。虽然本身不是漏洞,但它将允许利用上述漏洞。
同样值得注意的是,rm命令中出现了完整路径公开,公开给定文件在本地文件系统上的绝对路径:
php/elFinderVolumeDriver.class.php
这个漏洞的影响很大程度上取决于环境:它可能与其他的elFinder漏洞结合在一起,用于在其他应用程序中触发有趣的行为(例如删除WordPress的wp-config.php文件以获得代码执行)或用于影响现有的安全措施(例如删除.htaccess文件)。
通过改进
elFinderVolumeLocalFileSystem::_joinPath()的实现来断言最终路径不会在基路径之外,这个漏洞已经被修复了。作为加固措施,还在代码库中添加了几个对basename()的调用。
相同的
elFinderVolumeLocalFileSystem::_joinPath() 方法用于其他操作,例如重命名:它结合了一个卷基本目录和一个用户提供的目标名称。因此,它很容易受到研究人员刚才描述的漏洞的攻击。
在执行所有负责解码路径并确保允许目标扩展名的代码之后,
elFinderVolumeLocalFileSystem::rename()的实际实现如下面的代码片段所示:
php/elFinderVolumeLocalFileSystem.class.php
虽然目标扩展名仍然受到 MIME 检查的严格限制,但对于未经身份验证的攻击者来说,根据环境,通过覆盖authorized_keys、composer.json 等文件,该原语足以在服务器上执行命令。这个漏洞已经用研究人员之前讨论过的同一个补丁修复了。
对于大多数PHP应用程序来说,elFinder面临的最大威胁是攻击者可以将PHP脚本上传到服务器,因为没有任何东西(除了经过加固的web服务器配置)可以阻止他们直接访问它来执行它的内容。维护人员最初试图通过制作一个将危险的MIME类型与相关扩展关联起来的块列表来防御:
php/elFinderVolumeDriver.class.php
在Ubuntu 20.10上的Apache HTTP 2.4.46-1ubuntu1的测试环境中,默认配置声明 .phar 文件应该被视为 Application/x-httpd-php ([1]) 并被解释为:
在Debian的稳定版本中也可以看到这种配置,虽然对文件的内容执行另一遍MIME类型检测,因为PHP解释器允许在解释文件的任何地方使用语句(例如PHP可以放在一些虚拟数据之后),因此也可以很容易被绕过。
修复很简单:它声明 .phar 文件与默认情况下不允许的 MIME text/x-php 相关联。
在elFinder如此强大的默认功能中,用户可以选择多个文件,并使用zip、rar和7z等外部工具对它们进行压缩。该函数在名为archive的操作下被发布:
php/elFinder.class.php
请注意,即使上传文件被禁止,用户也可以通过对现有文件调用archive命令来创建压缩,该实现特定于正在使用的虚拟文件系统。研究人员只关注默认的命令行,因为它是由
elFinderVolumeLocalFileSystem继承的,elFinderVolumeLocalFileSystem创建了完整的命令行([1]),并使用默认shell([2])执行它:
php/elFinderVolumeLocalFileSystem.class.php
name的值来自用户控制的参数name的值来自用户控制的参数_GET['name'],虽然使用escapeshellarg()正确地转义以防止使用命令替换序列,但程序将尝试将此值解析为标志 (--foo=bar),然后解析为位置参数。还值得注意的是,在选择 ZIP压缩程序的情况下,用户的值以. ZIP作为后缀。
命令压缩包实现了一个完整性测试特性(-T),可以与-TT一起使用该特性来指定要运行的测试命令。在本例中,它为攻击者提供了一种使用此参数注入执行任意命令的方法。
为了能够利用此漏洞,攻击者需要创建一个虚拟文件(例如a.txt),将其压缩以创建a.zip,然后以原始文件和压缩为目标调用压缩操作,使用的名称如下: TmTT="$(id>out.txt)foooo"。
生成的命令行将是 zip -r9 -q '-TmTT="$(id>out.txt)foooo".zip' './a.zip' './a.txt',从而执行 id 并记录其标准输出到 out.txt,此文件将与 elFinder 界面中的其他文档一起使用。
当需要修复这个漏洞时,压缩操作不能很好的运行。通常基于POSIX的方法,故不能在此处应用,因为zip将退出,并出现以下漏洞:
然后维护人员决定在压缩名称前加上 ./ 以防止任何参数注入的风险,他们还决定在同一个补丁中加强对其他压缩程序(7z、rar等)的调用。
看看研究人员在这个案例研究中的最后发现,由于压缩文件无法上传,因此无法在默认配置中利用隔离特性中的此漏洞,由于设计特点,该函数可能会导致未来的安全问题。
隔离的基本原理是压缩文件可能包含不需要的文件(主要是PHP脚本) ,如果没有首先运行安全检查(例如,使用 MIME 验证),则不应在当前文件夹中提取这些文件。因此,elFinder选择将压缩文件提取到一个名为.quarantine的文件夹中,该文件夹位于files/文件夹下,
elFinderVolumeLocalFileSystem::_extract()为每个压缩文件提取生成一个随机的目录名(位于[1]):
php/elFinderVolumeLocalFileSystem.class.php
这可以通过strace或inotify套件动态确认,例如这里有一个包含PHP文件的压缩文件:
这个进程如下:
创建了一个名为
efbf975ccbac8727f434574610a0f1b6 的文件夹;
在
efbf975ccbac8727f434574610a0f1b6 中创建了一个名为 win.php 的文件;
数据被写入win.php;
win.php被删除;
efbf975ccbac8727f434574610a0f1b6被删除;
如果服务器被配置为列出目录,这个行为很容易被利用,因为可以在 MIME 验证步骤和删除它们之前访问危险文件(例如 .php)。然而,如果无法以这种方式找到随机目录名称,那么竞争条件窗口太小而无法考虑使用暴力破解的攻击。
攻击者可能会发现复制操作可用于内部文件夹,例如 .quarantine,并复制任何文件,而不管其内容如何。虽然它本身是一个无害的函数漏洞,但它可以与隔离函数链接在一起,以便在删除之前复制包含研究人员提取的压缩文件夹。复制的文件夹在界面中可见,并允许攻击者绕过随机名称访问恶意脚本,最终允许执行任意代码。
作为修复措施,维护人员决定将.quarantine文件夹移到files/之外。
elFinderVolumeLocalFileSystem抽象层不知道此文件夹之外的任何内容,从而防止对.quarantine执行任何意外操作。
在这个案例研究中,研究人员研究了在web文件管理器中常见的关键代码漏洞。本文的研究证明无害的漏洞通常可以组合起来形成有害漏洞链,比如获得任意代码执行。研究人员认为,记录和报告这些漏洞是很重要的,以缓解漏洞链,并减少类似漏洞的风险。