Icinga Web 中的路径遍历漏洞 - 嘶吼 RoarTalk – 回归最本质的信息安全,互联网安全新媒体,4hou.com

Icinga Web 中的路径遍历漏洞

我们会有自己的猫 漏洞 2022-07-05 12:00:00
收藏

导语:我们最近在Icinga Web中发现了两个代码漏洞,这些漏洞允许攻击者通过运行任意PHP代码来破坏运行它的服务器。作为我们研究的一部分,我们在PHP引擎中发现了一个未修补的漏洞,它可以利用其中一个发现。本文介绍了这两个漏洞的技术细节,以及维护人员应该如何修复它们。

WechatIMG2969.jpegIcinga是一个具有Web界面的现代开源IT监控系统。由于它专门的脚本语言,它具有高度可配置性,几乎可以在任何IT设备上运行检查。它还提供有用的内置插件来查询在受监控主机上运行的服务的状态,例如正在运行的服务、网络流量或可用磁盘空间。

我们最近在Icinga Web中发现了两个代码漏洞,这些漏洞允许攻击者通过运行任意PHP代码来破坏运行它的服务器。作为我们研究的一部分,我们在PHP引擎中发现了一个未修补的漏洞,它可以利用其中一个发现。本文介绍了这两个漏洞的技术细节,以及维护人员应该如何修复它们。

在同一篇博文中同时讨论PHP和C代码并不常见。我们将尽最大努力保持它的趣味性。让我们深入了解它。

影响

部署Icinga最常见的方法是使用与Icinga监控服务器通信的管理界面Icinga Web。

我们发现了一个路径遍历漏洞(CVE-2022-24716),该漏洞可被滥用以泄露服务器上的任何文件。它可以在没有身份验证和事先不知道用户帐户的情况下被利用。我们还发现了CVE-2022-24715,它会导致从管理界面执行任意PHP代码。

如果攻击者可以通过首先披露配置文件并修改管理员密码来访问数据库,那么可以很容易地将它们从未经身份验证的位置连接到服务器。

我们强烈建议将您的icingaweb2实例更新到2.8.6、2.9.6或2.10,即使它没有直接暴露在互联网上。

虽然我们不会发布概念验证,但利用这些发现很简单。我们还建议假设Icinga Web配置中存在的任何秘密(例如数据库凭据)可能已被泄露。作为预防措施,应当轮流使用它们。

技术细节

我们假设Icinga Web 2是使用2.9.5-1.hirsute版本并遵循官方文档进行部署的。正如您稍后将在CVE-2022-24715-Remote Code Execution部分中看到的那样,此设置使攻击者的利用变得稍微复杂一些,对我们的安全研究人员来说就更有趣了。

任意文件泄露(CVE-2022-24716)

上下文

Apache HTTP服务器配置为使用其模块mod_rewrite将所有传入请求分派给index.php,这种设置对于仅提供一个入口点的现代PHP应用程序来说非常常见:

htaccess


RewriteEngine on    

RewriteBase /icingaweb2/    

RewriteCond %{REQUEST_FILENAME} -s [OR]    

RewriteCond %{REQUEST_FILENAME} -l [OR]    

RewriteCond %{REQUEST_FILENAME} -d    

RewriteRule ^.*$ - [NC,L]    

RewriteRule ^.*$ index.php [NC,L]


第一个脚本加载webrouter.php,然后尝试根据请求的路径将请求分派到正确的软件组件:

首先处理重要的静态资源(css/icinga.css、css/icinga.min.css等),支持ETag标头、缩小和服务器端缓存;

根据请求参数动态生成的图像(svg/chart.php,png/chart.php);

以lib/开头的路径的请求由StaticController处理;

其他一切都交给控制器。

动态路由器总是审查过程中的一个有趣的部分:它们必须根据用户控制的数据构建路径,因此很容易出现路径遍历漏洞。

识别代码漏洞

StaticController的重要代码如下所示:它首先遍历现有库以找到与请求URL匹配的库,然后将关联的资产路径连接到客户端提供的值:

Icinga/Web/Controller/StaticController.php

$assetPath = ltrim(substr($request->getRequestUri(), strlen($request->getBaseUrl()) + 4), '/');

$library = null;

foreach ($app->getLibraries() as $candidate) {

    if (substr($assetPath, 0, strlen($candidate->getName())) === $candidate->getName()) {

        $library = $candidate;

        $assetPath = ltrim(substr($assetPath, strlen($candidate->getName())), '/');

        break;

    }

}

// [...]

$assetRoot = $library->getStaticAssetPath();

$filePath = $assetRoot . DIRECTORY_SEPARATOR . $assetPath;

[...]    

$app->getResponse()

[...]

        ->setBody(file_get_contents($filePath));

}

StaticController的代码有两个安全问题:

库可以宣布一个空的资产路径,在这种情况下,文件的路径仅使用用户输入构建。例如,icinga/icinga-php-thirdparty。

用户输入可以包含目录遍历序列(../),从而产生预期目录之外的最终路径。例如,icinga/icinga-php-library。

影响

因此,攻击者可以泄露本地文件系统的任何文件。我们可以通过官方演示实例来确认这个漏洞,例如通过获取文件/etc/hosts的内容:

$ curl https://icinga.com/demo/lib/icinga/icinga-php-thirdparty/etc/hosts -v

[...]

127.0.0.1   localhost

::1 localhost ip6-localhost ip6-loopback

fe00::0 ip6-localnet

ff00::0 ip6-mcastprefix

ff02::1 ip6-allnodes

ff02::2 ip6-allrouters

172.17.0.1  demo-icinga2

172.17.0.3  2a2f396a3e13

攻击者还可以以incingaweb2配置文件为目标。其中,它们还包含Web界面使用的数据库凭据。

如果攻击者可以访问数据库服务,他们可以使用这些凭据更改现有帐户的密码并获得对实例的经过身份验证的访问权限。根据此访问权限,我们研究了这种情况,后来找到了一种在实例上执行任意代码的方法。

在非默认部署中,Icinga也可以被告知使用本地文件系统上的SSH私钥。可以通过使用这种技术读取它们,然后以监控代理的身份转移到其他系统。

远程代码执行(CVE-2022-24715)

初步调查结果

经过身份验证的用户可以编辑资源,以便以后从其他配置文件中引用它们。其中一种资源类型是SSH密钥,需要将其写入本地文件系统才能使用。

我们发现在[1]处没有对SshResourceForm的参数用户执行验证。它允许攻击者使用目录遍历序列(例如../ )在[2]的预期目录之外写入SSH密钥:

application/forms/Config/Resource/SshResourceForm.php

public static function beforeAdd(ResourceConfigForm $form)

{

    $configDir = Icinga::app()->getConfigDir();

    $user = $form->getElement('user')->getValue();

    $filePath = $configDir . '/ssh/' . $user; // [1]

    if (! file_exists($filePath)) {

        $file = File::create($filePath, 0600);

    // [...]

    $file->fwrite($form->getElement('private_key')->getValue()); // [2]

我们的第一个假设是认为这个漏洞是没有用的,因为SSH密钥是用openssl_pkey_get_private()验证的;编写一个同时也是有效PEM证书的PHP脚本听起来并不容易。

这个函数调用是唯一的障碍,值得花时间深入研究如何实现它。正如文档中提到的,这个函数是PHP的Cryptography Extensions的一部分,它的代码位于php-src/ext/openssl。

我们需要更深入

在PHP引擎源代码中查看这个实现时,可以注意到PHP中OpenSSL模块特有的一个属性。此类库通常提供一种加载数据的方法,要么基于它将打开和读取的文件名,要么基于数据本身(在这种情况下,由用户来处理任何I/O操作)。

在这里,两种方法都被自动支持:如果参数$private_key以file://为前缀,它会为用户读取文件。否则,该参数被视为是证书的值。

这导致在其实现中出现一些相当不常见的控制流:

php-src/ext/openssl/openssl.c

static EVP_PKEY *php_openssl_pkey_from_zval(zval *val, int public_key, char *passphrase, size_t passphrase_len)

{

   EVP_PKEY *key = NULL;

   X509 *cert = NULL;

   bool free_cert = 0;

   char * filename = NULL;

   // [...]

   } else {

       // [...]       

       if (Z_STRLEN_P(val) > 7 && memcmp(Z_STRVAL_P(val), "file://", sizeof("file://") - 1) == 0) {

           filename = Z_STRVAL_P(val) + (sizeof("file://") - 1);

           if (php_openssl_open_base_dir_chk(filename)) {

               TMP_CLEAN;

           }

       }

           // [...]

           if (filename) {

               in = BIO_new_file(filename, PHP_OPENSSL_BIO_MODE_R(PKCS7_BINARY));

           } else {

               in = BIO_new_mem_buf(Z_STRVAL_P(val), (int)Z_STRLEN_P(val));

           }

在上面的代码片段中,zval *val是通过表单提交的私钥的内部表示形式。val是二进制安全的,这意味着即使PHP引擎包含NULL字节,它也可以在数据旁边跟踪其长度(以字节为单位)来处理完整的字符串。但是,libssl API(BIO_*)仅适用于以NULL结尾的字符数组,这些数组本质上不是二进制安全的:处理活动将在第一个NULL字节处停止。

攻击者可以使用这个属性来绕过openssl_pkey_get_private()执行的验证,同时保留将任意数据放入资源文件的能力:PHP在磁盘上搜索证书时会在第一个NULL字节处停止,但完整数据将写入到目标文件!

然后,攻击者可以将有效载荷分为4个部分:

输入易受攻击代码路径的强制前缀file://;

服务器上有效PEM证书的路径,例如,我们的测试虚拟机中的/usr/lib/python3/dist-packages/twisted/test/server.pem;

一个NULL字节;

要编写的文件内容,这里是一个执行外部命令的小PHP脚本。

WechatIMG2970.jpeg最后一件事

使用官方Linux软件包安装时,Icinga Web 2的PHP脚本部署在/usr/share/icingaweb2下。它们归root用户所有,因此不能使用HTTP服务器运行时使用的www-data的身份进行修改。

虽然这可以防止基于在此目录下植入PHP文件并访问它们的直接利用,但我们发现了攻击者可以用来获取任意代码执行的另一种技术。

Icinga有一个模块的概念,即扩展接口功能的自包含第三方代码(例如,添加Grafana支持)。这些模块默认存储在/usr/share/icingaweb2/modules下,但管理员也可以直接从界面更改此路径。

设置global_module_path期望模块所在的路径以冒号分隔。将此值更改为先前演示的漏洞可以写入的路径,例如/dev/shm/,将global_module_path设置为/dev/,并启用名为shm的新模块,允许执行任意PHP代码。

补丁

这两个漏洞都与类似的易受攻击的代码模式相关,并通过在构建目标路径(067ec0f、b7c31eb)后引入新的验证步骤来解决:

构建路径;

调用realpath():目录遍历序列,解析符号链接,并确保目标文件存在;

它确保由realpath()调用产生的路径仍在预期的目录下。

在将SSH资源的值写入磁盘之前,还会对它们进行进一步的格式验证,以防止使用file://。

我们还联系了PHP维护人员,以解决OpenSSL核心扩展功能中的NULL字节注入问题。由于没有任何其他功能旨在验证证书的格式,其他软件很可能使用相同的易受攻击的功能。

我们提供了补丁和测试用例,以方便维护人员采用;在撰写本文时,bug ticket仍处于开放状态。尽管如此,我们还是选择公开记录这个错误,因为我们认为安全风险很低,而且Icinga Web 2已经提供了几周的额外修复。

时间线

日期

活动

2022-02-15

我们向Icinga报告了第一个路径遍历漏洞。

2022-02-21

我们向Icinga报告了第二个路径遍历漏洞。

2022-02-23

Icinga承认这些漏洞,并创建了GitHub咨询。

2022-03-10

在#81713的上游错误跟踪器上报告了PHP漏洞。

2022-03-14

Icinga发布了icingaweb2 2.8.6、2.9.6和2.10。

结语

在本篇文章中,我们介绍了IT监控解决方案Icinga Web 2中两个非常相似的漏洞背后的技术细节。这两个漏洞可以在一次攻击中结合使用,以完全破坏Icinga服务器。在研究这些漏洞的过程中,我们还发现了PHP解释器本身的一个bug。我们在这里提醒大家,在实现一种语言的内置函数时,可能会发现一些意想不到的属性,这些函数可以允许利用其他安全漏洞。

我们强烈建议不要将此类系统按原样暴露在Internet上:它们只能通过受信任的源IP地址(例如VPN端点)访问或置于集中式身份验证系统之后。

我们要感谢Icinga和PHP维护人员及时回复并帮助解决我们发现的问题。

本文翻译自:https://blog.sonarsource.com/path-traversal-vulnerabilities-in-icinga-web/如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论