
Proof-of-Concept und Lab für eine authentifizierte SSRF im Repo-Migrate-Endpunkt von Gitea, bei der git HTTP-Redirects in lokales git HTTP folgt und interne Objekte preisgibt.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-git-redir-ssrf
Write-up: https://abraxaslabs.tech/research/gitea-git-redir-ssrf
Gitea 1.27.3 - Gitea
IsMigrateURLAllowed ist ein einmaliger net.LookupIP auf die von dir eingegebene URL. Die Bytes laufen dann durch git clone --mirror. HandleGitCmdHTTPRedirection soll git davon abhalten, diesem Hop zu folgen. Es bewirkt nichts. Der Kommentar ist der Befund: Sie ließen http.followRedirects=false weg, weil GitLab ohne .git mit 301 antwortet. Der Fetch beim Pull-Mirror ruft dieselbe No-op auf.
Ein angemeldeter Benutzer kann von einem allow-gelisteten Köder-Host migrieren, und git folgt dem 302 in Loopback-git-HTTP. Interne Objekte landen in einem Repository, das ihm gehört.
| ID | noch keine CVE |
| CWE | CWE-918 |
| CVSS | Hoch: 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N |
| Produkt | Gitea |
| Betroffen | bis v1.27.3 (146cc3e) |
| Auth | authentifiziert (standardmäßig offene Registrierung) |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1 |
Registrieren, von einer extern wirkenden URL migrieren, die mit 302 auf http://127.0.0.1:.../internal.git umleitet. Eine direkte Migration zu Loopback wird verweigert. Ein Clone, der einer Weiterleitung folgt, nicht. Die Witness-Datei landet in seinem Repository.
Kein file://-Festplattenlesen. Kein X-Gitea-Internal-Auth auf /api/internal (git setzt diesen Header nicht). Keine unauthentifizierte RCE. Keine Metadata-RCE, es sei denn, du zeigst separat, dass git den Body gespeichert hat.
Übriggebliebene Funde aus demselben Tag: keys IDOR, hostmatcher 0.0.0.0/8.
v1.27.3 schloss die GHSA-Welle von 2026: CVE-2026-60004 (diffpatch-Hook-RCE, CISA KEV), CVE-2026-59774 (unauthentifiziertes Org-Mode #+INCLUDE), Fork-PR-Actions-Gates, Reverse-Proxy-Standard für X-WEBAUTH-USER. Bleib für diese bei 1.27.3+. Ich blieb trotzdem auf diesem Tag.
CVE-2026-59765 behob Go uri.Open. CVE-2026-22874 behob Go reservedIPNets. Gits Redirector ist immer noch ein FIXME. Der Changelog-Eintrag „disable HTTP redirects on pull mirror sync" betrifft nicht diese Funktion.
Das Lab platziert Gitea auf 8.8.4.2, damit ein Köder-Host auf 8.8.4.10 für den Hostmatcher extern wirkt. Internes git HTTP bindet nur an Loopback im Gitea-Netns (network_mode: service:gitea, Port 9418). Eine direkte Migration zu 127.0.0.1 muss fehlschlagen. Ein Clone, der einer Weiterleitung folgt, muss gelingen. redir.py leitet /bait.git mit 302 auf http://127.0.0.1:9418/internal.git um. Die Witness-Datei WITNESS enthält GHSA-GITEA-GIT-REDIR-SSRF.
Bereits dokumentierte Irrwege: ALLOW_LOCALNETWORKS true (dann ist die Allow-List weg); file://; X-Gitea-Internal-Auth; eine Reverse Shell. Theater. Dass die direkte Loopback-Migration verweigert wird, ist die Kontrolle.
Client-Bugs sehen wie Produkt-Bugs aus. Ein Helper, der http.client überschattet, sendet nie ein Paket. .recv() auf einer HTTPResponse ist nicht .read(). Ich erwähne das einmal, weil es Zeit gekostet hat und für eine Minute so aussah, als hätte 1.27.3 die Sache erledigt.
cd lab
./run.sh
Ziel nur http://127.0.0.1:18103. ALLOW_LOCALNETWORKS=false.
negative-loopback status=422 You can not import from disallowed hosts
migrate-bait status=201 clone_addr=http://8.8.4.10/bait.git
fetch raw/WITNESS status=200 GHSA-GITEA-GIT-REDIR-SSRF
SUCCESS Gitea #2 git HTTP redirect SSRF
Behandle DNS zum Zeitpunkt der Übermittlung nicht als Grenze. Erzwinge http.followRedirects=false (und normalisiere Clone-URLs auf .git, damit GitLab weiterhin funktioniert), oder leite git HTTP über einen Dialer weiter, der hostmatcher bei jedem Hop erneut ausführt.
HandleGitCmdHTTPRedirection · IsMigrateURLAllowedGNU Affero GPL v3.0. Siehe LICENSE. Nur Loopback-Lab. Keine Gewährleistung.