
Proof-of-concept e lab per un SSRF autenticato nell'endpoint repo migrate di Gitea, dove git segue i redirect HTTP verso git HTTP in loopback e fa trapelare oggetti interni.
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 è un net.LookupIP one-shot sull'URL digitato. I byte passano poi attraverso git clone --mirror. HandleGitCmdHTTPRedirection dovrebbe impedire a git di seguire quel salto. Non fa nulla. Il commento è il finding: hanno omesso http.followRedirects=false perché GitLab risponde con 301 senza .git. Il fetch del pull-mirror chiama lo stesso no-op.
Un utente autenticato può migrare da un host esca presente nell'allow-list e git segue il 302 verso il git HTTP in loopback. Gli oggetti interni finiscono in un repository di sua proprietà.
| ID | nessuna CVE al momento |
| CWE | CWE-918 |
| CVSS | High: 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N |
| Prodotto | Gitea |
| Affected | fino alla v1.27.3 (146cc3e) |
| Auth | autenticato (registrazione aperta di default) |
| Licenza | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Registrarsi, migrare da un URL dall'aspetto esterno che risponde con 302 verso http://127.0.0.1:.../internal.git. La migrazione diretta verso il loopback viene negata. Il clone che segue il redirect no. Il file testimone finisce nel loro repository.
Non è una lettura da disco tramite file://. Non è X-Gitea-Internal-Auth su /api/internal (git non imposta quell'header). Non è RCE non autenticato. Non è RCE tramite metadata, a meno che non si dimostri separatamente che git ha memorizzato il body.
Altri residui dello stesso tag: keys IDOR, hostmatcher 0.0.0.0/8.
La v1.27.3 ha chiuso l'ondata di GHSA del 2026: CVE-2026-60004 (RCE tramite hook diffpatch, CISA KEV), CVE-2026-59774 (#+INCLUDE di Org-mode non autenticato), i gate delle Actions per le PR da fork, il default X-WEBAUTH-USER del reverse-proxy. Per queste cose restate sulla 1.27.3+. Io comunque mi sono soffermato su quel tag.
CVE-2026-59765 ha corretto uri.Open di Go. CVE-2026-22874 ha corretto reservedIPNets di Go. Il redirector di git è ancora un FIXME. La voce del changelog "disable HTTP redirects on pull mirror sync" non riguarda questa funzione.
Il lab mette Gitea su 8.8.4.2 così che un host esca su 8.8.4.10 appaia esterno a hostmatcher. Il git HTTP interno si lega solo al loopback nel netns di Gitea (network_mode: service:gitea, porta 9418). La migrazione diretta verso 127.0.0.1 deve fallire. Il clone che segue il redirect deve riuscire. redir.py risponde con 302 a /bait.git verso http://127.0.0.1:9418/internal.git. Il file testimone WITNESS contiene GHSA-GITEA-GIT-REDIR-SSRF.
Vicoli ciechi già registrati: ALLOW_LOCALNETWORKS true (così però l'allow-list sparisce); file://; X-Gitea-Internal-Auth; una reverse shell. Teatro. La migrazione diretta verso il loopback negata è il controllo.
I bug del client sembrano bug del prodotto. Un helper che fa shadowing di http.client non invia mai un pacchetto. .recv() su un HTTPResponse non è .read(). Lo menziono una volta perché è costato tempo e per un minuto è sembrato che la 1.27.3 avesse completato il lavoro.
cd lab
./run.sh
Prendi di mira solo 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
Non trattare il DNS al momento dell'invio come il confine. Forza http.followRedirects=false (e normalizza gli URL di clone su .git in modo che GitLab continui a funzionare), oppure fai passare l'HTTP di git attraverso un dialer che riesegue hostmatcher a ogni salto.
HandleGitCmdHTTPRedirection · IsMigrateURLAllowedGNU Affero GPL v3.0. Vedi LICENSE. Solo laboratorio su loopback. Nessuna garanzia.