
Prova de conceito e laboratório para um SSRF autenticado no endpoint de migração de repositório do Gitea, onde o git segue redirecionamentos HTTP para o git HTTP em loopback e vaza objetos internos.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-git-redir-ssrf
Gitea 1.27.3 - Gitea
IsMigrateURLAllowed é um net.LookupIP one-shot na URL que você digitou. Os bytes então passam por git clone --mirror. HandleGitCmdHTTPRedirection deveria impedir o git de seguir esse salto. Não faz nada. O comentário é a descoberta: eles deixaram http.followRedirects=false de fora porque o GitLab faz 301 sem .git. A busca do pull-mirror chama o mesmo no-op.
Um usuário autenticado pode migrar de um host isca na allow-list e o git segue o 302 para o git HTTP em loopback. Objetos internos acabam em um repositório que ele possui.
| ID | sem CVE ainda |
| CWE | CWE-918 |
| CVSS | Alto: 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N |
| Produto | Gitea |
| Afetado | até v1.27.3 (146cc3e) |
| Auth | autenticado (registro aberto por padrão) |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1 |
Registrar-se, migrar de uma URL com aparência externa que faz 302 para http://127.0.0.1:.../internal.git. Migração direta para loopback é negada. Clone que segue redirecionamento não é. O arquivo testemunha acaba no repositório dele.
Não é leitura de disco via file://. Não é X-Gitea-Internal-Auth em /api/internal (o git não define esse header). Não é RCE não autenticado. Não é RCE via metadata, a menos que você demonstre separadamente que o git armazenou o corpo.
Sobras da mesma tag: keys IDOR, hostmatcher 0.0.0.0/8.
A v1.27.3 fechou a onda de GHSAs de 2026: CVE-2026-60004 (RCE no hook diffpatch, CISA KEV), CVE-2026-59774 (#+INCLUDE do Org-mode sem auth), gates de Actions em fork-PR, padrão X-WEBAUTH-USER do reverse-proxy. Fique na 1.27.3+ por causa desses. Mesmo assim, eu fiquei nessa tag.
CVE-2026-59765 corrigiu o uri.Open do Go. CVE-2026-22874 corrigiu o reservedIPNets do Go. O redirecionador do git ainda é um FIXME. O changelog "disable HTTP redirects on pull mirror sync" não é essa função.
O lab coloca o Gitea em 8.8.4.2 para que um host isca em 8.8.4.10 pareça externo ao hostmatcher. O git HTTP interno escuta apenas em loopback no netns do Gitea (network_mode: service:gitea, porta 9418). Migração direta para 127.0.0.1 deve falhar. Clone que segue redirecionamento deve ter sucesso. redir.py faz 302 de /bait.git para http://127.0.0.1:9418/internal.git. O arquivo testemunha WITNESS contém GHSA-GITEA-GIT-REDIR-SSRF.
Caminhos errados já registrados: ALLOW_LOCALNETWORKS true (aí a allow-list some); file://; X-Gitea-Internal-Auth; um reverse shell. Teatro. Migração direta para loopback negada é o controle.
Bugs de cliente parecem bugs de produto. Um helper que faz shadow de http.client nunca envia um pacote. .recv() em um HTTPResponse não é .read(). Menciono isso uma vez porque custou tempo e pareceu, por um minuto, que a 1.27.3 tinha terminado o serviço.
cd lab
./run.sh
Alvo apenas 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
Não trate o DNS no momento do envio como a fronteira. Force http.followRedirects=false (e normalize as URLs de clone para .git para que o GitLab ainda funcione), ou faça proxy do git HTTP através de um dialer que reexecute o hostmatcher em cada salto.
HandleGitCmdHTTPRedirection · IsMigrateURLAllowedGNU Affero GPL v3.0. Veja LICENSE. Apenas lab em loopback. Sem garantia.