Proof-of-Concept für eine Gitea-Restore-Repo-file://-Local-File-Inclusion (CWE-73), die Symlink-Ziele außerhalb eines nicht vertrauenswürdigen Dumps in Release-Anhänge kopiert.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-restore-file-open
Gitea 1.27.3 - Gitea
CVE-2026-59765 setzte einen hostmatcher-HTTP-Client auf OpenWithClient. Der file://-Zweig ignoriert diesen Client und ruft os.Open auf. Die Wiederherstellung eines Dumps hat kein DownloadFunc. gitea_uploader.go öffnet dann die umgeschriebene file://-URL. os.Open folgt einem dump-seitigen Symlink. Der Join bleibt unter dem Dump. Der Symlink nicht.
Die Wiederherstellung eines nicht vertrauenswürdigen Dumps durch einen Operator kopiert das Symlink-Ziel in einen Release-Anhang. Als Gitea-uid ist das jede Datei, die das Dienstkonto lesen kann.
| ID | noch keine CVE |
| CWE | CWE-73 |
| CVSS | Medium: 4.2 CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:N |
| Produkt | Gitea |
| Betroffen | bis v1.27.3 (146cc3e); lokaler Operator gitea restore-repo |
| Auth | lokale Operator-Wiederherstellung eines nicht vertrauenswürdigen Dumps |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1 |
Einem Admin einen nicht vertrauenswürdigen Dump aushändigen (ein „Backup"-Verzeichnis, eine Wiederherstellung von einem nicht vertrauenswürdigen Remote), der einen Symlink unter dem joined-under-dump-Pfad enthält. Die Wiederherstellung kopiert die Zieldatei in einen Release-Anhang des wiederhergestellten Repos. Wer dieses Repo lesen kann, lädt sie herunter.
Wenn der Dump-Autor app.ini benennt, ist das Credential-Diebstahl (INTERNAL_TOKEN, SECRET_KEY, JWT, DB-Passwort). Das Lab-Orakel war eine platzierte Witness-Datei, nicht app.ini. Es ist kein OS-LPE. Es ist nicht unauthentifiziert. Es ist kein Dump-Root-Join-Escape. Live-Migration von GitHub/GitLab/Gitea setzt DownloadFunc und ruft nicht os.Open auf einer Angreifer-file:// auf.
Zwei Restore-Maps lagen auf dem übrig gebliebenen Tisch. Ich habe die offensichtliche zuerst im Lab getestet.
Finding 8 war Dump-Root-Join-Escape: eine absolute DownloadURL in release.yml platzieren und hoffen, dass FilePathJoinAbs das Dump-Präfix so fallen lässt, wie es ein naives filepath.Join(base, "/etc/passwd") tut. Auf 1.27.3 tut es das nicht. Jedes sub ist filepath.Clean("/"+s) und dann filepath.Join(base, cleaned). Der Replica-Join von /tmp/dump + /tmp/GITEA-JOIN-OUTSIDE blieb /tmp/dump/tmp/GITEA-JOIN-OUTSIDE. Die Wiederherstellung schrieb file:// unter dem Dump um und schlug fehl mit open ...: no such file or directory. Das ist kein Join-Escape auf diesem Tag.
Finding 9 ist das, was gelandet ist. Ich platzierte eine Witness-Datei außerhalb des Dumps, legte einen Symlink am joined-under-dump-Pfad an, führte gitea restore-repo aus. Restore rc=0. Der wiederhergestellte Release-Anhang enthielt GITEA-FILE-OPEN-WITNESS.
Bereits dokumentierte Irrwege: Dump-Root-Join-Escape über eine absolute DownloadURL; Live-Migrate-file://-LFI; X-Gitea-Internal-Auth (Restore ist CLI als Gitea-uid, nicht /api/internal); Restore-HTTP als Remote-Unauth behandeln (CVSS ist lokal, hohe Privilegien, Benutzerinteraktion); eine Reverse Shell. Theater.
cd lab
./run.sh
Ziel nur http://127.0.0.1:18137. Compose mountet lab/dump read-only. run.sh platziert den Witness im Container, baut den Dump, legt den Symlink an und führt dann gitea restore-repo aus.
restore-rc=0 Restore repo labadmin/restored successfully
download status=200 snippet=GITEA-FILE-OPEN-WITNESS
SUCCESS GITEA-RESTORE-FILE-OPEN
file:// aus OpenWithClient entfernen oder das Folgen von Dump-Symlinks verweigern und keine nicht vertrauenswürdigen Dumps wiederherstellen. Der wiederhergestellte Anhang darf den Out-of-Dump-Witness nicht enthalten.
uri.go · gitea_uploader.go · FilePathJoinAbsGNU Affero GPL v3.0. Siehe LICENSE. Nur Loopback-Lab. Keine Gewährleistung.