
Prueba de concepto y laboratorio para un SSRF autenticado en el endpoint repo migrate de Gitea, donde git sigue redirecciones HTTP hacia git HTTP en loopback y filtra objetos internos.
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 es un net.LookupIP de un solo uso sobre la URL que escribiste. Después los bytes pasan por git clone --mirror. Se supone que HandleGitCmdHTTPRedirection impide que git siga ese salto. No hace nada. El comentario es el hallazgo: dejaron fuera http.followRedirects=false porque GitLab responde 301 sin .git. La obtención del pull-mirror llama al mismo no-op.
Un usuario autenticado puede migrar desde un host señuelo incluido en la lista de permitidos y git sigue el 302 hacia el HTTP de git en loopback. Los objetos internos acaban en un repositorio que él controla.
| ID | aún sin CVE |
| 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 |
| Producto | Gitea |
| Afectados | hasta v1.27.3 (146cc3e) |
| Autenticación | autenticado (registro abierto por defecto) |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1 |
Registrarse, migrar desde una URL de apariencia externa que responde 302 hacia http://127.0.0.1:.../internal.git. La migración directa a loopback se deniega. El clon que sigue la redirección no. El archivo testigo acaba en su repositorio.
No es lectura de disco mediante file://. No es X-Gitea-Internal-Auth en /api/internal (git no establecerá esa cabecera). No es RCE sin autenticación. No es RCE vía metadata salvo que demuestres por separado que git almacenó el cuerpo.
Restos de la misma etiqueta: IDOR de claves, hostmatcher 0.0.0.0/8.
v1.27.3 cerró la oleada de GHSA de 2026: CVE-2026-60004 (RCE por hook diffpatch, CISA KEV), CVE-2026-59774 (#+INCLUDE de Org-mode sin autenticación), las barreras de Actions para PRs de fork, el valor por defecto de X-WEBAUTH-USER en el proxy inverso. Mantente en 1.27.3+ por todo eso. Aun así me quedé con esa etiqueta.
CVE-2026-59765 corrigió uri.Open de Go. CVE-2026-22874 corrigió reservedIPNets de Go. El redirector de git sigue siendo un FIXME. La entrada del changelog "disable HTTP redirects on pull mirror sync" no es esta función.
El laboratorio pone Gitea en 8.8.4.2 para que un host señuelo en 8.8.4.10 parezca externo al hostmatcher. El HTTP de git interno se enlaza solo a loopback en el netns de Gitea (network_mode: service:gitea, puerto 9418). La migración directa a 127.0.0.1 debe fallar. El clon que sigue la redirección debe tener éxito. redir.py responde 302 a /bait.git hacia http://127.0.0.1:9418/internal.git. El archivo testigo WITNESS contiene GHSA-GITEA-GIT-REDIR-SSRF.
Callejones sin salida ya registrados: ALLOW_LOCALNETWORKS true (entonces la lista de permitidos desaparece); file://; X-Gitea-Internal-Auth; una reverse shell. Teatro. La migración directa a loopback denegada es el control.
Los bugs del cliente parecen bugs del producto. Un helper que ensombrece http.client nunca envía un paquete. .recv() sobre un HTTPResponse no es .read(). Lo menciono una vez porque costó tiempo y pareció, durante un minuto, que 1.27.3 había terminado el trabajo.
cd lab
./run.sh
Dirígete solo a 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
No trates el DNS en el momento del envío como el límite. Fuerza http.followRedirects=false (y normaliza las URL de clonación a .git para que GitLab siga funcionando), o haz proxy del HTTP de git a través de un dialer que vuelva a ejecutar hostmatcher en cada salto.
HandleGitCmdHTTPRedirection · IsMigrateURLAllowedGNU Affero GPL v3.0. Consulta LICENSE. Solo para laboratorio en loopback. Sin garantía.