‧
阅读时间:17分钟
安全事件事后分析:2023年7月
Metabase 团队
‧ 17分钟阅读时间
分享这篇文章
在过去的两周里,Metabase 经历了项目历史上最严重的安全事件。我们知道这一事件对我们社区造成了极大的干扰,我们特别抱歉在周五连续发布两次升级的呼吁。
现在尘埃基本落定,我们想阐述一下发生了什么以及我们为什么要这样做。我们知道,没有解释的“立即升级”呼吁是模糊的,需要我们的社区对我们的判断有很大的信心。我们希望通过对所发生的事情(好事和坏事)保持透明,社区在未来的日子里会继续信任我们。
故事的核心是一系列相当严重的安全漏洞,为了更好地理解时间线中的事件,了解实际存在的漏洞是有用的。
提醒一下——这是一篇相当长且相当技术的帖子,针对那些管理和保护 Metabase 的人。
发生了什么
漏洞
这一系列漏洞的核心是我们支持的数据库——H2。H2 是一个基于 JVM 的嵌入式数据库。我们使用 H2 作为 Metabase 的“包含电池”数据库,用于存储用户账户、仪表板定义、设置等。当我们评估如何交付示例数据时,它在那里也成为了赢家。作为使用 H2 交付我们的示例数据库的一部分,我们决定也允许人们连接他们可能拥有的任何 H2 数据库。这从未被广泛使用,但它“免费提供”,所以我们基本上将 H2 留作一个受支持的选择。
因为从实际应用的角度来看,H2主要用于嵌入其他应用程序中,所以它拥有大量并非针对多客户端设计的特性。更具体地说,有方法可以在进程内运行H2的解释器,有连接字符串参数允许你运行SQL(或其他任何内容),并且通常缺乏对恶意客户端的加固。我们之前发现并修复了一种实现方式(通过连接字符串上的INIT
参数),但最近的一个漏洞(或者说是三个不同漏洞的集群)正是源于这一点。
涉及三个不同的问题
- 你可以使用Unicode字符(例如,用ï代替I)来绕过我们对
INIT
关键字的检查,因为H2会在底层将它们转换(感谢Reginaldo Silva)。 - 你可以欺骗Metabase将其数据库连接视为PostgreSQL连接,但通过在详情有效负载中偷偷地插入H2连接字符串,让JDBC
DriverManager
加载H2驱动程序(感谢Chaitin安全响应中心和bluE0)。 - H2中的
TRACE_LEVEL_SYSTEM_OUT
选项容易受到SQL注入攻击(感谢AssetNote和Maxwell Garrett)。
到目前为止,这些问题构成了一个非常令人烦恼的一组漏洞,允许添加或验证新数据库连接的人破坏运行Metabase的主机操作系统。通常,主机操作系统访问仅限于Metabase管理员,所以虽然这是一个问题,但它并不是一个世界停止的事件。
但还有两个关键问题。
首先,人们经常犯连接字符串错误,在确认某人输入的连接字符串之前,通常会进行测试连接。我们提供了一个API调用,以在保存数据库连接之前方便地进行这项检查。在添加数据库的常规管理面板流程中,这通过API检查管理员权限来保护。
最后一部分,也是最为尴尬的部分:我们有一个设置过程,它通过一个API调用创建用户账户并连接数据库。我们将这两个操作合并为单步操作,并暴露了一个未经身份验证的API端点/api/setup/validate
,该端点会尝试连接数据库连接字符串。此端点仅在使用setup_token
时才可访问。这个setup_token
从未打算公开曝光,通常Metabase会在第一次使用后立即删除该令牌。然而,去年年初,我们进行了一些更改,允许通过环境变量注入令牌,并意外地将令牌泄露到了在/api/session/properties
中暴露的设置中(此外,在第一次使用后没有正确清除令牌)。
这些组件与能够使H2在主机操作系统上执行命令的能力相结合,导致了未经身份验证的远程代码执行,并破坏了Metabase社区连续两个周五。
将这些全部放在一起,一个人可以
- 调用
/api/session/properties
以获取设置令牌。 - 使用设置令牌来调用
/api/setup/validate
。 - 利用缺失的检查来使H2在主机操作系统上执行命令。
- 打开反向shell,创建管理员账户等。
简而言之,这是一条极其严重的攻击链。
初始报告和修复(7月13日至21日)
在7月13日,我们收到外部研究人员关于应用程序中一个安全漏洞的报告。报告详细说明了上述(非常简单)的路径,在Metabase服务器中运行自定义代码而不需要任何身份验证(我们将此初始漏洞称为“AssetNote”漏洞,因为Assetnote团队报告了它)。
到7月14日,我们已经编写、测试并构建了这个漏洞的修复方案。我们将它推送到我们代表Metabase Cloud客户托管的2000多台服务器上。
然而,这时我们面临了一个问题,接下来应该做什么。我们有数百个自行托管的客户,我们对其有合同和道德责任,需要保护他们。我们还有一个由50,000多个组织组成的社区,这些组织在他们的服务器上运行Metabase。一旦你知道了这个漏洞,滥用它就非常简单,而且尽管利用该漏洞访问底层数据仓库需要一些特定技能,但用于DDoS、垃圾邮件和加密挖矿的滥用几乎不需要任何技能或努力。我们也没有办法强制任何人升级,甚至没有办法向运行Metabase服务器的人发送电子邮件。
标准选项是发布补丁,让全世界都知道补丁,然后让那些没有付钱给我们来管理他们的服务器的人自己照顾自己。如果这个漏洞是一个普通的漏洞,不允许世界上任何地方的任何人访问任何Metabase实例上的用户数据,我们就会这样做。
我们真的想做得更好。经过一番思考,我们制定了一个三阶段计划。
- 向我们的付费客户提供修补的二进制文件。我们会向运行自定义分支的客户提供实际的源代码补丁,但需要签署保密协议。
- 一周后,公开发布不带源代码的修复方案。通知我们的社区这是一次未授权的RCE(远程代码执行),并且必须立即升级。
- 再过一周,将修复方案合并到我们的主公共仓库中,并发布CVE,表彰所有帮助我们找到和修复这个漏洞的人。
在这个计划中,我们做出了一个非常具体的权衡。我们知道Clojure JAR可以被反编译,反编译后的JVM源代码可以被检查(Clojure通常会将其包含在jar中)。我们也从修补的二进制文件中移除了这部分源代码。在整个发布过程中,我们知道不可能提供补丁而不让一个高级研究人员或攻击者知道这个错误以及如何利用它。但我们希望能在利用代码进入通用流通之前,为我们的安装基础争取尽可能多的升级时间。
急迫地等待(7月18日至21日)
7月18日,我们已经将修补和去除源代码的二进制文件部署到位,并通知了我们的自行托管客户。
这个初始补丁
- 完全阻止了如果你已经初始化了实例(即完成了设置流程),则无法使用有漏洞的端点。
- 检测到一些传递给数据库连接字符串的字符串,这使得第一次攻击在
/api/setup/validate
端点(公开,你不需要身份验证)和/api/database
(私有,你需要身份验证)上成为可能。如果发现这些字符串,将返回错误,而不是将它们传递给H2。
我们向这些客户共享了所有受影响的版本的修补二进制文件,但保留了源代码。对于有自定义分支的客户,我们要求他们直接与我们联系,并在保密协议下提供补丁。大约有二十个客户在运行自定义分支,并联系我们。
到目前为止一切顺利。
接下来的三天里,发生了一些事件,改变了我们的计划。
等待和观察(7月21日至27日)
我们收到了来自客户的两个看似独立的报告,称他们已经知道这个漏洞。当时这非常令人惊讶,因为漏洞在被发现之前已经存在了一年多,并且利用这个漏洞需要针对我们产品中的多个漏洞进行相当具体的操作。我们深入调查后发现,这两位客户都已被发现该漏洞的原始研究团队告知(这两位客户都提供Metabase安全问题的赏金悬赏)。
加上定制的分支数量,这使得我们非常担心在没有提前通知或可行的修复二进制文件的情况下,让我们的开源社区处于危险之中。因此,我们决定加快我们的公告时间表,但仍然没有公开实际的利用方法。
7月21日,我们发布了推文,更新了博客,在领英上发布了帖子,并向我们整个更新邮件列表发送了电子邮件,这个列表有数万人注册。
我们开始监控社交媒体,看看是否有相关事件,但几天内没有任何相关信息,包括在Hacker News上。几天来,有一些关注,但没有关于任何人反编译并重新创建利用方法的消息。
7月26日,我们收到了另一份安全报告(从现在起称为“Qing”漏洞),详细说明了如何通过使用连接细节中的单独密钥(而不是同一连接字符串中的所有密钥)来绕过对/api/database
端点(仅限于认证管理员)的控制,以及使用连接到外部数据库来运行命令的AssetNote漏洞的变体。
虽然令人不安,但这些发现需要登录管理员,只要之前修补的二进制文件已经部署,就不会有未经身份验证的RCE。
7月27日,一位工程师(从现在起称为“Reginaldo”)成功逆向工程了我们的补丁,并发现了一种针对公共/api/setup/validate
端点的新攻击。这种攻击使用变音符号字符(添加到字母中的符号)来绕过我们实施的一些控制,但漏洞仅影响未初始化的实例(处于设置模式的实例),以及影响另一个影响/api/database
(私有)端点的漏洞,该端点将H2服务器作为外部数据库打开,您可以在其上运行命令。
这使情况变得更加严重,因为现在可以对修补过的实例进行未经身份验证的RCE攻击——尽管这种攻击仅在实例首次设置时持续几分钟。
紧急发布公开版本(7月28日至31日)
那天晚上(美国时区的夜间),另一组安全研究人员发布了博客文章,与我们事先没有联系。这篇文章包括了关于“AssetNote”漏洞的详细信息,这导致AssetNote发布了他们的发现(因为猫已经从袋子里出来了)。
7月28日醒来时,我们发现这篇文章还包括了作者“留给读者去发现”的另一个,“隐藏”攻击。我们与他们联系,他们告诉我们关于通过JDBC DriverManager
获取H2的另一种以前未知的方法。
当我们构建版本46.6.3(修复“Qing”和“Reginaldo”漏洞的补丁)时,我们正是了解到这种新攻击的精确时刻。
我们发布了那个补丁,而且为了以防万一,还决定发布另一个点版本(46.6.4),该版本移除了H2作为分析支持的数据库。由于很明显更广泛的网络安全社区已经了解漏洞的根本原因,我们还发布了一篇博客文章,并发送了电子邮件通知我们的客户和开源软件用户升级,或阻止所有相关端点,直到他们升级。
在所有这些事情发生的同时,我们为Metabase Cloud客户在自己的服务器上缓解了这个问题。一旦我们得知每个漏洞利用的情况,我们就在所有相关的API端点上设置了网络级别的阻止,关闭了我们的商店以防止新的注册,并开始审核我们所拥有的每一丝日志。到周六晚上(7月29日)晚些时候,我们已经单独审核了所有调用过漏洞端点的服务器,并且我们没有发现任何篡改的证据。我们仍在密切关注任何可疑活动,并就如何从这次事件中吸取教训进行了很多内部讨论。
7月31日,我们向社区发布了另一条推文和LinkedIn帖子。
总的来说,我们设法在我们发布通用补丁版本和漏洞进入公众认知之间有近一周的时间来自我混淆。这并不理想,但希望这给了更多的人时间来升级。
当前情况
TL;DR:如果你是自托管,并且在2023年7月28日之前最后进行了升级,请立即升级。
如果你是Metabase Cloud客户,你不受影响。
如果你是自托管并且正在运行最新的二进制文件(目前是46.6.4),你没问题。
如果你运行的是43-45版本的Metabase,并且你没有升级到最新的小版本43.7.3、44.7.3、45.4.3或更高版本,你是有漏洞的。
如果你运行的是42或以下版本,你不受这个漏洞的影响。但是你面临许多其他安全问题和错误,所以我们建议你尽快升级。
我们从这次事件中学到了什么?
好吧,我们了解到,如果你发布了一个没有任何信息的补丁二进制文件,字节码最多需要五天时间就可以反编译,并且可以识别和重现根本的漏洞。
我们了解到,客户端提供的连接字符串应该受到更严格的审查,并且不应该信任JDBC驱动程序。我们最初特别担心用于创建额外管理用户的setup_token
,并且了解到我们也应该对任何打开数据库连接的东西有同样的警惕。
我们真正运行了我们的日志记录、事件响应和入侵检测流程,并确定了改进的领域。
我们仍在进行事后分析,并在接下来的几天内无疑会学到更多。
附录
我如何知道我是否受到影响?
如果你是Metabase Cloud客户,根据我们所能找到的信息,你以任何方式都没有受到影响。我们一收到报告就修补并阻止了所有漏洞。我们Kubernetes容器中的所有实例都已回收并从已知的受保护镜像中重新创建。我们回顾了过去几周所有客户的日志,没有发现任何可疑的利用迹象。我们将继续监控我们的基础设施,确保您受到保护。
如果你是Metabase的自托管用户
这些漏洞自2022年5月存在以来,因此,如果您保存了您的Metabase实例的日志,或者您在Metabase实例前面有负载均衡器/反向代理,您应该检查以下模式
如果您在日志中看到在实例的初始设置后任何时间收到一个对/api/setup/validate
的POST
API调用,并且返回了状态2xx
或400
,并且您不在我们于2023年7月18日发布的版本上,那么您的实例可能已被入侵(即:您的实例可能受到攻击者的控制)。
如何知道攻击者入侵的深度,以及是否有信息被窃取
有两种类型的攻击
- 如果您看到返回状态
2xx
的调用,那么您将无法看到攻击者对您的实例运行了哪些代码,因此您应该考虑最坏的情况:攻击者进入了服务器,获取了应用程序数据库的凭据,并能够连接到它,并且他们窃取了您连接的数据仓库的凭据。 - 如果您看到返回状态
400
的调用,那么Metabase日志将打印出攻击者在您的实例上运行的代码。例如,
从这一点开始,攻击者将能够获取到Metabase应用程序数据库的连接字符串,而该连接字符串反过来又包含了您数据仓库的连接字符串。
如何知道攻击者是否打开了反向shell?
如果您在运行Metabase的服务器或容器中运行top
并看到带有参数(如echo
、编码参数等)运行的bash进程,则实例有一个反向shell开启(例如,下面的屏幕截图中的PID 3763
)。
如果您在运行Metabase的服务器或容器中运行netstat -antp
并看到非Metabase使用的端口的活跃连接,并且连接状态为ESTABLISHED
(例如,下面的屏幕截图中的来自外部地址172.23.0.2:9001
的连接)。
如果shell已经关闭了一段时间,您也可能看到处于FIN_WAIT2
状态的连接,如下例所示
我应该对此做些什么?
如果您怀疑您可能受到影响,我们建议您
- 使用全新的服务器实例将您的实例升级到最新版本。
- 旋转您的数据仓库凭据。
- 限制未知主机对您的数据仓库的IP访问。
- 检查您的Metabase实例上是否有陌生的用户账户。
- 旋转Metabase中使用的Slack和SMTP凭据令牌。
这个漏洞正在被积极利用
我们知道一些恶意软件开发者/僵尸网络已经将漏洞利用代码包含在他们的攻击中。我们也知道至少有一个攻击Metabase服务器以挖掘加密货币并将服务器用作DDoS攻击节点的例子。
我们没有在其他提到的端点上发现其他漏洞。