// HackTheBox
Analytics
Metabase Pre-Auth-RCE (CVE-2023-38646) über H2-JDBC-Injection → aus Umgebungsvariablen geleakte SSH-Zugangsdaten → GameOver(lay) Kernel-Rechteausweitung
Aufklärung
Zwei Ports, und die ganze Box hängt daran, was der Webserver wirklich ausliefert:
| Port | Service | Version |
|---|---|---|
| 22 | SSH | OpenSSH 8.9p1 Ubuntu 3ubuntu0.4 |
| 80 | HTTP | nginx 1.18.0; redirects → analytical.htb (static landing) |
Port 80 liefert für die reine IP keinen Inhalt aus — er leitet auf den vhost
analytical.htb um, also wandert dieser Name in /etc/hosts. analytical.htb selbst
ist nur eine statische Marketing-Landingpage, aber das Durchsuchen ihrer Links fördert
einen zweiten vhost zutage, data.analytical.htb, der neben dem ersten in
/etc/hosts eingetragen wird. Diese Subdomain antwortet mit Metabase, einem
quelloffenen Business-Intelligence-Dashboard. Eine selbst gehostete Analytics-App auf
einer eigenen Subdomain ist genau die Art von fettem, versioniertem Ziel, das man
zuerst fingerprinten sollte — das ist also der Faden, an dem man zieht.
Erstzugang — CVE-2023-38646
1. Metabase fingerprinten und das Setup-Token leaken
Metabase stellt einen nicht authentifizierten Settings-Endpoint bereit,
/api/session/properties, der seine öffentliche Konfiguration als JSON ausgibt —
einschließlich der laufenden Version und, auf einer nie vollständig initialisierten
Box, des einmaligen Setup-Tokens, das den Erstkonfigurations-Assistenten absichert:
$ curl -s http://data.analytical.htb/api/session/properties
Zwei Fakten fallen aus dieser Antwort:
- Version
v0.46.6(veröffentlicht 2023-06-29). setup-token249fa03d-fd94-4d5b-b94f-b4ebf3df681f— das Token ist nicht null, was bedeutet, dass der Erstkonfigurations-Flow nie abgeschlossen wurde und noch offen ist.
Beides ist entscheidend für CVE-2023-38646, eine Pre-Authentication-RCE in Metabase. Sie wurde in 0.46.6.1 behoben; 0.46.6 liegt genau im verwundbaren Bereich, und ein aktives Setup-Token ist exakt die Voraussetzung, die der Exploit benötigt.
2. Pre-Auth-RCE über H2-JDBC-Injection
Metabase bringt für die eigenen Beispieldaten eine eingebettete H2-Datenbank mit
(eine reine Java-SQL-Engine). Der Setup-Assistent erlaubt es, Metabase über einen
JDBC-Verbindungsstring auf eine Datenbank zeigen zu lassen, und
POST /api/setup/validate „testet” diese Verbindung, bevor ein Konto existiert —
wobei das geleakte Setup-Token die einzige Berechtigung ist. Der JDBC-Treiber von H2
erlaubt es einem Verbindungsstring, einen SQL-Trigger in JavaScript zu definieren,
und dieser Trigger-Code läuft serverseitig, sobald die Verbindung geöffnet wird. Ein
präparierter Verbindungsstring verwandelt somit „validiere meine Datenbankeinstellungen”
in „führe meinen Code aus”: CREATE TRIGGER ... AS '//javascript ...' →
Kommandoausführung als der Metabase-Prozess.
Der öffentliche Exploit metabase_rce.py automatisiert dies gegen
POST /api/setup/validate. Über zwei Implementierungsdetails stolpert man leicht:
- Das vollständige
details-Objekt ist erforderlich. Das äußeredetails-Feld des Requests muss jeden vom Endpoint erwarteten Geschwister-Schlüssel tragen —is_on_demand,is_full_sync,is_sample,cache_ttl,refingerprint,auto_run_queries,schedules. Lässt man einen weg, weist der Server den gesamten Request mit400 {"message":"Vector arg to map conj must be a pair"}ab, bevor er überhaupt den verwundbaren Code erreicht. - Der Payload ist Base64, mit Leerzeichen aufgefüllt. Das Reverse-Shell-Kommando
wird Base64-kodiert und mit Leerzeichen aufgefüllt, damit es nie auf ein
=-Zeichen endet — ein=würde den H2-Verbindungsstring vorzeitig beenden und die Injection brechen.
Der Trigger ist so verdrahtet, dass er feuert, wenn Metabase während der Validierung
sein eigenes SELECT gegen INFORMATION_SCHEMA.TABLES ausführt. Der HTTP-Request
kommt als 400-SQL-Syntaxfehler zurück, aber das ist kosmetisch — das JavaScript des
Triggers wurde zu diesem Zeitpunkt bereits ausgeführt. Das Ergebnis ist eine
Reverse-Shell als uid=2000(metabase) innerhalb des Containers 71c5ea8a011e.
Lateral — aus Umgebungsvariablen geleakte SSH-Zugangsdaten
Die Shell landet in einem Container, nicht auf dem Host, also lautet die nächste Frage, was der Container weiß, das der Host wiederverwendet. Metabase wird über Umgebungsvariablen konfiguriert, und seine eigene Prozessumgebung gibt einen Satz Zugangsdaten preis:
META_USER=metalytics
META_PASS=An4lytics_ds20223#
Die Variablennamen sehen Metabase-spezifisch aus, aber die Werte werden als
Host-SSH-Zugangsdaten wiederverwendet — der klassische Password-Reuse-Pivot aus
einem Container heraus auf die Box, auf der er läuft. Sie funktionieren direkt über
SSH und liefern eine echte Session als uid=1000(metalytics) samt user.txt:
$ ssh metalytics@10.129.229.224
Rechteausweitung — GameOver(lay)
Auf dem Host meldet uname -a Ubuntu 22.04.3 auf Kernel 6.2.0-25-generic.
Dieser Kernel ist anfällig für GameOver(lay) (CVE-2023-2640 / CVE-2023-3262), einen
Fehler in Ubuntus OverlayFS, bei dem Datei-Capabilities, die auf einer Datei in einem
unprivilegierten User-Namespace gesetzt werden, auf das echte Dateisystem übernommen
werden — wodurch ein unprivilegierter Benutzer privilegierte Capabilities auf eine
ausführbare Datei stempeln kann.
Der Standard-Einzeiler unshare -rm nutzt dies aus: einen neuen User- und
Mount-Namespace erzeugen, einen OverlayFS-Mount aufbauen und die Capability
cap_setuid auf eine Kopie des ruby-Binaries setzen. Da die Capability den Copy-up
auf das Host-Dateisystem übersteht, kann dieses ruby anschließend
Process::Sys.setuid(0) aufrufen, um root zu werden:
$ ruby -e 'Process::Sys.setuid(0); exec "/bin/bash"'
Das liefert eine root-Shell zurück — /root/root.txt lesen, um die Box abzuschließen.