
PoC und Write-up für die HackTheBox-"Nexus"-Rechteausweitung: ein als root laufender Gitea-Template-Sync-Dienst, der über gefälschte Git-Objekte für Path Traversal anfällig ist. Nur für Bildungszwecke.
Gitea Template-Sync Path Traversal über gefälschte Git-Objekte → root
Ein Proof-of-Concept-Exploit zur Privilegienausweitung für die HackTheBox-Maschine Nexus.
Ein root-eigener systemd-Dienst synchronisiert Gitea-Template-Repositories auf die Festplatte,
ohne Eintragsnamen zu bereinigen, wodurch ein authentifizierter Benutzer mit geringen Rechten
beliebige Dateien als root durch Directory Traversal schreiben kann — hier genutzt, um einen
SSH-Schlüssel in /root/.ssh/authorized_keys einzuschleusen.
Dieser Code wird für Bildungszwecke und autorisierte Sicherheitstests bereitgestellt — d. h. für Laborumgebungen wie HackTheBox oder Systeme, die Ihnen gehören oder für die Sie eine ausdrückliche schriftliche Genehmigung zum Testen haben. Die unbefugte Nutzung gegen Systeme, die Sie nicht kontrollieren, ist illegal. Der Autor übernimmt keine Haftung für Missbrauch.
Wenn die Nexus-Maschine noch aktiv auf HackTheBox ist, veröffentlichen oder teilen Sie diesen Exploit nicht — die HTB-Regeln verbieten die Verbreitung von Lösungen für aktive Maschinen.
Das Synchronisierungsskript liest Dateien aus Repositories, die in Gitea als Templates markiert sind, und schreibt sie auf das lokale Dateisystem. Der Zielpfad wird ungefähr so aufgebaut:
dest = os.path.join(base_dir, name) # name comes straight from the Git tree
open(dest, "w").write(content)
os.path.join() neutralisiert ..-Sequenzen nicht. Ein Tree-Eintrag, dessen Name
ein Traversal-Pfad ist (z. B. ../../../../../root/.ssh/authorized_keys), bewirkt daher,
dass der privilegierte Prozess außerhalb des vorgesehenen Verzeichnisses schreibt.
Git speichert Inhalt und Namen getrennt:
name → object zu (hier lebt der Dateiname);Der Dateiname in einem Tree-Eintrag ist nur Text, kann also .. sein — aber der Standard-
Git-Client weigert sich, einen solchen Eintrag zu erstellen, genau um diesen Angriff zu verhindern.
Dieser PoC umgeht diese clientseitige Prüfung, indem er rohe Git-Objekte direkt
in .git/objects/ schreibt (die SHA-1-IDs werden manuell berechnet) und refs/heads/main
manuell erstellt. Das gepushte Repository enthält dann einen Tree-Eintrag namens .., den der
root-Synchronisierungsprozess auflöst und befolgt.
www-data..env, wiederverwendet als
Systempasswort → SSH als Benutzer mit geringen Rechten.root.Das wiederkehrende Thema in allen drei Phasen: ein privilegierter Prozess, der angreiferkontrollierten Daten ohne Validierung vertraut (ein gefälschter MIME-Typ, ein wiederverwendetes Passwort, ein unbereinigter Dateiname).
http://localhost:3000.git, python3, curl und ssh verfügbar (Standard auf der Box).chmod +x nexus_privesc.sh
./nexus_privesc.sh -t <TARGET_IP> -u <GITEA_USER> -p <GITEA_PASS> [-d DEPTH]
Beispiel:
./nexus_privesc.sh -t 10.129.21.192 -u jones -p 'REDACTED' -d 5
/tmp/.exploit_key)...-Traversal-Tree kodieren, der auf
/root/.ssh/authorized_keys abbildet, mit dem öffentlichen Schlüssel des Angreifers als Inhalt.root mit dem eingeschleusten Schlüssel an.Überwachen Sie die Synchronisierung auf dem Ziel mit:
tail -f /var/log/template-sync.log
Ein erfolgreicher Durchlauf protokolliert eine Zeile wie:
synced: ../../../../../root/.ssh/authorized_keys
DEPTHDEPTH ist die Anzahl der ..-Ebenen, die benötigt werden, um vom Synchronisierungs-/Staging-
Verzeichnis bis zum Dateisystem-Root (/) aufzusteigen. Wenn die Synchronisierung im Log
erfolgreich ist, aber das root-SSH fehlschlägt, ist das Traversal auf der falschen Ebene
gelandet — versuchen Sie es erneut mit -d 4 oder -d 6. Der genaue Wert kann durch Lesen des
Staging-Pfads in /etc/gitea/template-sync.py bestätigt werden.
Erstellen Sie niemals Dateisystempfade aus nicht vertrauenswürdigen Namen mit os.path.join() allein.
Normalisieren und begrenzen Sie Pfade, z. B.:
base = os.path.realpath(base_dir)
dest = os.path.realpath(os.path.join(base, name))
if not dest.startswith(base + os.sep):
raise ValueError("path traversal blocked")
Lehnen Sie Tree-Eintragsnamen ab, die .., absolute Pfade oder Pfadtrenner enthalten.
Führen Sie den Synchronisierungsdienst unter einem dedizierten Konto mit geringen Rechten aus, nicht als root.
Aktivieren Sie receive.fsckObjects = true in Gitea, um fehlerhafte gepushte Objekte abzulehnen.
<your name / handle> — für Bildungszwecke auf HackTheBox.
| Klasse | Path Traversal (CWE-22) → Lokale Privilegienausweitung |
| Komponente | gitea-template-sync.service (benutzerdefiniertes /etc/gitea/template-sync.py) |
| Läuft als | root, ausgelöst alle ~60s durch gitea-template-sync.timer |
| Grundursache | os.path.join() auf unbereinigten Namen aus Git-Tree-Objekten |
| Auswirkung | Authentifizierter Benutzer mit geringen Rechten → root |
| Flag | Beschreibung | Standard |
|---|
-t | Ziel-IP (für das abschließende root-SSH) | — (erforderlich) |
-u | Gitea-Benutzername | — (erforderlich) |
-p | Gitea-Passwort | — (erforderlich) |
-d | Anzahl der ..-Ebenen, um von dem Synchronisierungsverzeichnis nach / zu gelangen | 5 |