开卷有益 · 不求甚解
Zabbix 是一个非常流行的开源监控平台,用于收集、集中和跟踪整个基础设施中的 CPU 负载和网络流量等指标。它与 Pandora FMS 和 Nagios 等解决方案非常相似。由于其受欢迎程度、功能和在大多数公司网络中的特权地位,Zabbix 是威胁参与者的高调目标。一家公共漏洞经纪人,一家专门从事安全漏洞获取的公司,也公开宣布了他们对该软件的兴趣。
我们在 Zabbix 的客户端会话实现中发现了一个严重漏洞,该漏洞可能导致整个网络遭到破坏。在本文中,我们介绍了不同类型的会话存储,并讨论了实现安全的原因。然后,我们描述了我们在 Zabbix 中发现的漏洞的技术细节、其影响以及如何预防。让我们深入了解它!
会话都是关于跨多个 HTTP 请求存储状态的,设计上是无状态的。为此,应用程序通常为每个客户端提供一个唯一标识符;他们必须将其与未来的请求一起传输。然后,服务器可以加载相关信息,无论它是存储在内存中、数据库中还是本地文件系统上等等。这就是我们通常所说的服务器端会话存储。
这种历史方法效果很好,但在开发和部署现代 Web 应用程序的方式上存在缺陷。例如,它不能很好地扩展:如果后端服务分散在多个服务器上,如何确保一个会话在服务甚至整个服务器群中可用?
因此,开发人员在客户端引入了会话存储。他们现在不必为客户端分配会话标识符,而是必须在每个请求中发送状态的副本。像 ASP 和 Java 这样的技术堆栈将这个概念包装在一种称为“视图状态”的东西中,但现在很常见的是依赖于 JSON Web 令牌 (JWT) 标准。
这两种方法的目标都是在客户端安全地存储数据,但后端服务仍然可以确保其真实性和完整性:它需要使用密码学来提供这些保证。尽管存在配置错误的风险(弱机密、支持破坏的加密算法)以及撤销 JWT 的固有困难,但这主要是一种安全的方法。
在此类用例中,不得混淆加密和身份验证提供的安全保证。虽然加密数据在未受过教育的人看来可能看起来“安全”,但后端服务无法检测到会话数据是否被客户端更改。使用 ECB 等加密模式甚至可以让攻击者在不知道密钥的情况下制作有效的任意密文!
为了演示由于客户端会话代码的不安全设计和实现而可能出现的风险,让我们看看我们在 Zabbix 中发现的两个漏洞的技术细节。
监控平台 Zabbix 通常部署在具有四个不同组件的基础设施上:
在 2021 年 12 月,我们分析了Zabbix Web 前端的外部攻击面,以更好地了解与该软件暴露于不受信任的网络相关的风险。这项工作导致发现了两个关键漏洞,CVE-2022-23131 和 CVE-2022-23134。
这些发现都与 Zabbix 在客户端存储会话数据的方式有关。我们将指导您完成其易受攻击的实施,讨论其影响以及如何在早期开发阶段发现它。
发现的漏洞影响我们研究时所有受支持的Zabbix Web 前端版本,包括 5.4.8、5.0.18 和 4.0.36。它们不需要事先了解目标,并且可以被攻击者毫不费力地自动化。
我们强烈建议您将运行*Zabbix Web 前端*的实例升级到 6.0.0beta2、5.4.9、5.0.19 或 4.0.37 以保护您的基础设施。
在启用 SAML SSO 身份验证的实例上,它允许绕过身份验证并获得管理员权限。攻击者可以使用此访问权限在链接的Zabbix Server和Zabbix Agent实例上执行任意命令,CVE-2021-46088,其利用代码已经公开。与Zabbix Agent不同,不能将Zabbix 服务器配置为禁止执行命令。
服务器端会话是 PHP 的内置功能。客户端在 cookie 中被分配了一个唯一的会话标识符,PHPSESSID是最常见的一个,并且必须在每个请求中传输它。在服务器端,PHP 采用此值并在文件系统(/var/lib/php/sessions,有时是/tmp/)上查找关联的会话值以填充超全局变量$_SESSION。客户端不能随意修改会话值,因为它们只控制会话的标识符。
Zabbix Web 前端基于 PHP 的强大功能自定义会话处理程序推出了自己的客户端存储实现。通过使用实现SessionHandlerInterface的类调用session_set_save_handler() ,对$_SESSION的所有后续访问都将由此类的方法处理。
在他们的例子中,目标是将任何对_SESSION会导致调用CCookieSession::read();CCookieHelper::get()只是$_COOKIE的包装器:
ui/include/classes/core/CCookieSession.php
<?php
class CCookieSession implements SessionHandlerInterface {
// [...]
public const COOKIE_NAME = ZBX_SESSION_NAME;
// [...]
public function read($session_id) {
$session_data = json_decode($this->parseData(), true);
// [...]
foreach ($session_data as $key => $value) {
CSessionHelper::set($key, $value);
}
// [...]
protected function parseData(): string {
if (CCookieHelper::has(self::COOKIE_NAME)) {
return base64_decode(CCookieHelper::get(self::COOKIE_NAME));
}
return '';
}
Zabbix 开发人员引入了一种方法来验证存储在 cookie 中的数据并确保它们不被篡改。此功能在CEncryptedCookieSession中实现:
ui/include/classes/core/CEncryptedCookieSession.php
class CEncryptedCookieSession extends CCookieSession {
// [...]
public function extractSessionId(): ?string {
// [...]
if (!$this->checkSign($session_data)) {
return null;
}
// [...]
return $session_data['sessionid'];
}
// [...]
protected function checkSign(string $data): bool {
$data = json_decode($data, true);
if (!is_array($data) || !array_key_exists('sign', $data)) {
return false;
}
$session_sign = $data['sign'];
unset($data['sign']);
$sign = CEncryptHelper::sign(json_encode($data));
return $session_sign && $sign && CEncryptHelper::checkSign($session_sign, $sign);
}
}
作为高级读者的旁注,这里有一个很大的危险信号:术语“签名[ature]”和“加密”可以互换使用。CEncryptHelper::sign()内部使用 AES ECB,易于延展,无法提供有关数据真实性的安全保证。使用此构造还导致了另一个安全建议,但本文不会详细介绍。
CEncryptedCookieSession::checkSign()方法仅在CEncryptedCookieSession::extractSessionId()中调用,而在CCookieSession方法中从不调用(例如在CCookieSession)。当访问sessionid以外的字段时,永远不会验证会话的真实性。
由于 cookie 完全由客户端控制,因此它们基本上可以控制会话。这是非常罕见的,并且打破了大多数关于存储在其中的值的可信度的假设。它可能导致使用会话的应用程序部分存在漏洞。
安全断言标记语言 (SAML) 是最常见的单点登录 (SSO) 标准之一。围绕 XML 实现,它允许身份提供者(IdP,一个能够对用户进行身份验证的实体)告诉服务提供者(SP,这里是 Zabbix)你是谁。您可以将Zabbix Web 前端配置为允许通过 SAML 进行用户身份验证,但默认情况下不启用它,因为它需要了解身份提供者的详细信息。这是企业部署最常见的设置。
与 SAML 身份验证机制相关的代码可以在index_sso.php中找到。简而言之,它的目标是:
如上一节所述,CEncryptedCookieSession::checkSign()永远不会在此文件中调用,因此会话条目saml_data[username_attribute]的值可以完全由客户端控制:
ui/index_sso.php
if (CSessionHelper::has('saml_data')) {
$saml_data = CSessionHelper::get('saml_data');
CWebUser::$data = API::getApiService('user')->loginByUsername($saml_data['username_attribute'],
(CAuthenticationHelper::get(CAuthenticationHelper::SAML_CASE_SENSITIVE) == ZBX_AUTH_CASE_SENSITIVE),
CAuthenticationHelper::get(CAuthenticationHelper::AUTHENTICATION_TYPE)
);
漏洞利用很简单,特别是因为Zabbix Web 前端自动配置了一个名为Admin的高权限用户。
一旦在仪表板上被认证为管理员,攻击者可以在任何附加的Zabbix Server上执行任意命令,如果在配置中明确允许AllowKey=system.run[](非默认) ,则可以在Zabbix Agents上执行。*
在setup.php中发现了另一个不安全使用会话的情况。此脚本通常由系统管理员在首次部署Zabbix Web 前端时运行,以后仅允许经过身份验证的高权限用户访问。
此页面通常使用会话来跟踪设置步骤的进度;再一次,这里永远不会调用CEncryptedCookieSession::checkSign()。使用设置为 6 的入口步骤制作会话允许重新运行安装过程的最新步骤。
这一步对攻击者来说非常有趣,因为它的目标是创建Zabbix Web 前端配置文件conf/zabbix.conf.php:
ui/include/classes/setup/CSetupWizard.php
private function stage6(): array {
// [...] [1]
$config = new CConfigFile($config_file_name);
$config->config = [
'DB' => [
'TYPE' => $this->getConfig('DB_TYPE'),
'SERVER' => $this->getConfig('DB_SERVER'),
'PORT' => $this->getConfig('DB_PORT'),
'DATABASE' => $this->getConfig('DB_DATABASE'),
// [...]
] + $db_creds_config + $vault_config,
// [...]
];
$error = false;
// [...] [2]
$db_connect = $this->dbConnect($db_user, $db_pass);
$is_superadmin = (CWebUser::$data && CWebUser::getType() == USER_TYPE_SUPER_ADMIN);
$session_key_update_failed = ($db_connect && !$is_superadmin)
? !CEncryptHelper::updateKey(CEncryptHelper::generateKey())
: false;
if (!$db_connect || $session_key_update_failed) {
// [...]
return $this->stage2();
}
// [...]
if (!$config->save()) {
// [...]
在[1]处,创建了一个新的CConfigFile对象来存储和验证新的配置值。CSetupWizard::getConfig()方法只是当前会话的包装,因此这些值完全由攻击者控制。
在[2]处,代码尝试通过尝试连接来识别新的数据库配置是否有效。由于此代码仅应在初始设置过程中调用,当尚未配置用户帐户和数据库设置(如加密密钥)时,控制会话的攻击者将能够通过各种检查。
因此,即使Zabbix Web Frontend实例已经处于工作状态,攻击者也可以覆盖现有的配置文件。通过指向他们控制的数据库,攻击者可以使用高权限帐户访问仪表板:
重要的是要理解,这种访问不能用于访问网络上部署的Zabbix Agent : Zabbix Web Frontend和Zabbix Server必须使用相同的数据库才能进行通信。仍然有可能将其与 Web 仪表板上的代码执行漏洞链接起来,以获得对数据库的控制权并在网络上进行支点。
在非强化或旧环境中可能存在其他利用场景。例如,PHP 的 MySQL 客户端实现了LOAD DATA LOCAL语句,但现在默认禁用 3 年。另一个线索可能是在验证数据库配置时使用完全受控的参数调用file_exists(),这是已知的安全风险,因为phar://等潜在危险的方案包装器。
Zabbix 维护人员分别解决了这两个漏洞:
他们还决定不强制执行 cookie 签名检查:这种方法的主要缺点是,如果忘记了对CEncryptedCookieSession::checkSign()的调用,依赖于会话的新功能可能会引入类似的漏洞。也没有办法检测潜在的安全回归。
日期 | 行动 |
---|---|
2021-11-18 | 向 Zabbix 维护人员发送安全公告。 |
2021-11-22 | 供应商证实了我们的发现。 |
2021-12-14 | 发布了第一个候选版本 5.4.9rc1。 |
2021-12-14 | 我们通知供应商该补丁可以被绕过。 |
2021-12-22 | 发布了第二个候选版本 5.4.9rc2。 |
2021-12-23 | Zabbix 5.4.9、5.0.9 和 4.0.37 发布。 |
2021-12-29 | 在 https://support.zabbix.com/browse/ZBX-20350 发布公告。 |
2022-01-11 | Zabbix 6.0.0beta2 发布。 |
在本文中,我们介绍了实现客户端会话存储时的常见安全问题。作为案例研究,我们描述了在流行的开源监控平台 Zabbix 中发现的高严重性漏洞。漏洞 CVE-2022-23131 和 CVE-2022-23134 的根本原因相同,可导致绕过身份验证并使远程攻击者能够在目标服务器实例上执行任意代码。
在编写和审查与重要安全功能相关的代码时,很容易做出与引入漏洞的原始开发人员相同的假设。在这里,没有与客户端会话存储相关的集成测试可以发现这种行为。
始终通过 VPN 或一组受限的 IP 地址提供对具有扩展内部访问(例如编排、监控)的明智服务的访问,强化文件系统权限以防止意外更改,删除设置脚本等。
我们要感谢 Zabbix 维护者的响应能力和强大的披露流程。
近期阅读文章
,质量尚可的,大部分较新,但也可能有老文章。开卷有益,不求甚解
,不需面面俱到,能学到一个小技巧就赚了。译文仅供参考
,具体内容表达以及含义, 以原文为准
(译文来自自动翻译)尽量阅读原文
。(点击原文跳转)每日早读
基本自动化发布(不定期删除),这是一项测试
最新动态: Follow Me
微信/微博:
red4blue
公众号/知乎:
blueteams