Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646

admin 2023年8月3日15:03:11评论39 views字数 2821阅读9分24秒阅读模式



Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646

前言
一开始,我按照平时在仓库中查找历史commit的方式进行查找,但是没有发现修复漏洞的地方。
我猜测代码仓库内没有对漏洞进行修复。
此外,官方在已修复漏洞的版本中删除了发布出的fatjar内的clj源码, 历史版本的源码是存在的。
Metabase这个项目使用了Lisp语言Clojure进行开发,因此编译出的二进制文件很难进行diff, 看来只能从历史源码找了。

漏洞分析
令牌泄露
审查了一遍源码中的公开路由和中间件后,并没有发现太大的问题。
然而,当我随手检查了一下浏览器发起的请求时,突然发现properties里的setup-token居然不为null。
我记得我去年之前审过这个项目,这个地方本应该是null的。
Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646
继续搜索之后我发现 官方在一个commit中 将 完成安装后移除setup-token 这个重要的的操作给移除了。
https://github.com/metabase/metabase/commit/0526d88f997d0f26304cdbb6313996df463ad13f#diff-bf3f2797f327779cbbd6bfab7e45261c7ee93b29db6828d2ee0ad17acf4d7825L33
这导致了完成安装后 properties仍然存在setup-token
现在 我们已经拿到了 setup-token 以用于调用setup api。

JDBC RCE
setup下 有一个/validate 路由 用于设置数据源时类验证是否连接成功

Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646
其中支持H2数据库 H2如果可以自定义连接参数的话就可以RCE
手动试了下 发现无法新建文件 只能读取现有数据库 (代码中通过添加IFEXISTS参数限制了创建)
Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646

使用metabase自带内置数据库 (metabase 默认使用H2作为系统数据库)
file:/metabase.db/metabase.db
尝试使用INIT 发现任何内容都可以连接成功
分析源码发现H2连接时会将INIT参数移除
(defn- connection-string-set-safe-options "Add Metabase Security Settings™ to this `connection-string` (i.e. try to keep shady users from writing nasty SQL)." [connection-string] {:pre [(string? connection-string)]} (let [ (connection-string->file+options connection-string)] (file+options->connection-string file (merge (->> options;; Remove INIT=... from options for security reasons (Metaboat #165);; http://h2database.com/html/features.html#execute_sql_on_connection (remove (fn [[k _]] (= (u/lower-case-en k) "init"))) (into {})) {"IFEXISTS" "TRUE"}))))
https://github.com/metabase/metabase/commit/252024431aa61c538c73ece21fa04ef99e4ed17b
那么有什么方法可以绕过这个限制呢?

H2限制绕过
方法1
connection-string-set-safe-options 使用了lower-case-en 将参数名转换为小写字母之后与init匹配进行校验.
H2则是将参数名转换为大写
private void readSettingsFromURL() { DbSettings var1 = DbSettings.getDefaultSettings(); int var2 = this.url.indexOf(59); if (var2 >= 0) { String var3 = this.url.substring(var2 + 1); this.url = this.url.substring(0, var2); String[] var4 = StringUtils.arraySplit(var3, ';', false); String[] var5 = var4; int var6 = var4.length;
for(int var7 = 0; var7 < var6; ++var7) { String var8 = var5[var7]; if (!var8.isEmpty()) { int var9 = var8.indexOf(61); if (var9 < 0) { throw this.getFormatException(); }
String var10 = var8.substring(var9 + 1); String var11 = var8.substring(0, var9); var11 = StringUtils.toUpperEnglish(var11); if (!isKnownSetting(var11) && !var1.containsKey(var11)) { throw DbException.get(90113, var11); }
String var12 = this.prop.getProperty(var11); if (var12 != null && !var12.equals(var10)) { throw DbException.get(90066, var11); }
this.prop.setProperty(var11, var10); } } }
}
两个方法不同 这里就出现了一个问题
在进行大小写转换时 其他语言的一些字母也能被转换成英文字母 然而这两个处理的大小写转换完全相反

参考 Fuzz中的javascript大小写特性
https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html

这里刚好可以使用 拉丁字母 ‘ı’ 替换INIT中的 I
ıNIT在转成大写时 为INIT
但是ıNIT转成小写后为 ınit ı没有被转换 绕过这个限制
这里修复的时候 可以将大小写转换和H2保持一致 就可以避免这个问题
之后构造SQL语句 创建触发器 执行javascript脚本
参考 Make_JDBC_Attack_Brilliant_Again
特殊符号问题可以使用 eval(decodeURIComponent(“”)) 解决 注意执行一次要修改一个名称
Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646
成功RCE
Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646

原文作者:橙子酱

原文地址:https://rce.moe/2023/07/28/Metabase-CVE-2023-38646/




感谢您抽出

Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646

.

Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646

.

Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646

来阅读本文

Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646

点它,分享点赞在看都在这里


原文始发于微信公众号(Ots安全):Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年8月3日15:03:11
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Metabase 远程代码执行漏洞分析 & 一种补丁绕过方法 CVE-2023-38646https://cn-sec.com/archives/1931479.html

发表评论

匿名网友 填写信息