
PoC e write-up per l'escalation di privilegi di HackTheBox "Nexus": servizio di sincronizzazione template Gitea eseguito come root, vulnerabile a path traversal tramite oggetti Git contraffatti. Solo per uso didattico.
Path traversal nel template-sync di Gitea tramite oggetti Git contraffatti → root
Un exploit proof-of-concept per l'escalation di privilegi sulla macchina HackTheBox Nexus.
Un servizio systemd di proprietà di root sincronizza i repository template di Gitea su disco
senza sanificare i nomi delle voci, consentendo a un utente autenticato con privilegi bassi di
scrivere file arbitrari come root tramite directory traversal — usato qui per impiantare
una chiave SSH in /root/.ssh/authorized_keys.
Questo codice è fornito per scopi educativi e test di sicurezza autorizzati soltanto — ovvero ambienti di laboratorio come HackTheBox, o sistemi di tua proprietà o per cui hai esplicita autorizzazione scritta a eseguire test. L'uso non autorizzato contro sistemi che non controlli è illegale. L'autore non si assume alcuna responsabilità per usi impropri.
Se la macchina Nexus è ancora attiva su HackTheBox, non pubblicare o condividere questo exploit — le regole di HTB proibiscono la distribuzione di soluzioni per macchine attive.
Lo script di sincronizzazione legge i file dai repository contrassegnati come template in Gitea e li scrive sul filesystem locale. Il percorso di destinazione è costruito all'incirca così:
dest = os.path.join(base_dir, name) # name comes straight from the Git tree
open(dest, "w").write(content)
os.path.join() non neutralizza le sequenze ... Una voce di tree il cui nome
è un percorso di traversal (ad es. ../../../../../root/.ssh/authorized_keys) fa quindi
scrivere il processo privilegiato al di fuori della directory prevista.
Git memorizza contenuto e nomi separatamente:
nome → oggetto (è qui che risiede il nome del file);Il nome del file in una voce di tree è solo testo, quindi può essere .. — ma il client
Git standard rifiuta di creare una tale voce, proprio per prevenire questo attacco.
Questo PoC aggira quel controllo lato client scrivendo oggetti Git grezzi direttamente
in .git/objects/ (calcolando gli id SHA-1 a mano) e costruendo refs/heads/main
manualmente. Il repository pushato contiene quindi una voce di tree chiamata .., che il
processo di sincronizzazione root risolve e segue.
www-data..env di produzione, riutilizzate come
password di sistema → SSH come utente con privilegi bassi.root.Il tema ricorrente in tutte e tre le fasi: un processo privilegiato che si fida di dati controllati dall'attaccante senza validazione (un MIME type falsificato, una password riutilizzata, un nome di file non sanificato).
http://localhost:3000.git, python3, curl e ssh disponibili (predefiniti sulla macchina).chmod +x nexus_privesc.sh
./nexus_privesc.sh -t <TARGET_IP> -u <GITEA_USER> -p <GITEA_PASS> [-d DEPTH]
Esempio:
./nexus_privesc.sh -t 10.129.21.192 -u jones -p 'REDACTED' -d 5
/tmp/.exploit_key)... che mappa a
/root/.ssh/authorized_keys, con la chiave pubblica dell'attaccante come contenuto.root con la chiave impiantata.Monitora la sincronizzazione sul target con:
tail -f /var/log/template-sync.log
Un'esecuzione riuscita registra una riga come:
synced: ../../../../../root/.ssh/authorized_keys
DEPTHDEPTH è il numero di livelli .. necessari per risalire dalla directory di
sync/staging fino alla radice del filesystem (/). Se la sincronizzazione riesce nel log ma l'SSH
root fallisce, il traversal è atterrato al livello sbagliato — riprova con -d 4 o
-d 6. Il valore esatto può essere confermato leggendo il percorso di staging in
/etc/gitea/template-sync.py.
Non costruire mai percorsi filesystem da nomi non attendibili con il solo os.path.join().
Normalizza e confina i percorsi, ad es.:
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")
Rifiuta i nomi delle voci di tree che contengono .., percorsi assoluti o separatori di percorso.
Esegui il servizio di sync sotto un account dedicato con privilegi bassi, non root.
Abilita receive.fsckObjects = true in Gitea per rifiutare oggetti pushati malformati.
<your name / handle> — per uso educativo su HackTheBox.
| Classe | Path Traversal (CWE-22) → Escalation di Privilegi Locali |
| Componente | gitea-template-sync.service (custom /etc/gitea/template-sync.py) |
| Eseguito come | root, attivato ogni ~60s da gitea-template-sync.timer |
| Causa principale | os.path.join() su nomi non sanificati presi dagli oggetti tree di Git |
| Impatto | Utente autenticato con privilegi bassi → root |
| Flag | Descrizione | Predefinito |
|---|
-t | IP target (per l'SSH root finale) | — (obbligatorio) |
-u | Username Gitea | — (obbligatorio) |
-p | Password Gitea | — (obbligatorio) |
-d | Numero di livelli .. per raggiungere / dalla directory di sync | 5 |