
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
Gitea 1.27.3 - Gitea
IsMigrateURLAllowed è una net.LookupIP una tantum sull'URL che hai digitato. I byte passano poi attraverso git clone --mirror. HandleGitCmdHTTPRedirection dovrebbe impedire a git di seguire quel salto. Non fa nulla. Il commento è la scoperta: hanno omesso http.followRedirects=false perché GitLab risponde con 301 senza .git. La fetch del pull-mirror chiama lo stesso no-op.
Un utente autenticato può migrare da un host esca in allow-list e git segue il 302 verso git HTTP in loopback. Gli oggetti interni finiscono in un repo 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 |
| Affetto | 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 302 verso http://127.0.0.1:.../internal.git. La migrazione diretta verso loopback è negata. Il clone che segue il redirect no. Il file testimone finisce nel repo loro.
Non è una lettura da disco file://. Non è X-Gitea-Internal-Auth su /api/internal (git non imposterà quell'header). Non è RCE non autenticato. Non è RCE da metadata a meno che tu non 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 GHSA del 2026: CVE-2026-60004 (RCE nell'hook diffpatch, CISA KEV), CVE-2026-59774 (#+INCLUDE di Org-mode non autenticato), gate delle Actions per fork-PR, default X-WEBAUTH-USER del reverse-proxy. Resta sulla 1.27.3+ per quelli. Io me ne sono comunque rimasto 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. Il changelog "disable HTTP redirects on pull mirror sync" non riguarda questa funzione.
Il lab mette Gitea su 8.8.4.2 così un host esca su 8.8.4.10 appare 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 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ì l'allow-list sparisce); file://; X-Gitea-Internal-Auth; una reverse shell. Teatro. La migrazione diretta verso 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 finito il lavoro.
cd lab
./run.sh
Target 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 a .git così GitLab continua a funzionare), oppure fai passare il git HTTP attraverso un dialer che riesegue hostmatcher a ogni salto.
HandleGitCmdHTTPRedirection · IsMigrateURLAllowedGNU Affero GPL v3.0. Vedi LICENSE. Solo lab in loopback. Nessuna garanzia.