
Preuve de concept et laboratoire pour une SSRF authentifiée dans le point de terminaison de migration de dépôt de Gitea, où git suit les redirections HTTP vers le git HTTP en loopback et divulgue des objets internes.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-git-redir-ssrf
Gitea 1.27.3 - Gitea
IsMigrateURLAllowed est un net.LookupIP one-shot sur l'URL que vous avez saisie. Les octets passent ensuite par git clone --mirror. HandleGitCmdHTTPRedirection est censé empêcher git de suivre ce saut. Il ne fait rien. Le commentaire est la découverte : ils ont omis http.followRedirects=false parce que GitLab renvoie un 301 sans .git. L'appel de récupération pull-mirror appelle le même no-op.
Un utilisateur connecté peut migrer depuis un hôte appât autorisé et git suit le 302 vers le git HTTP loopback. Les objets internes atterrissent dans un dépôt qu'il possède.
| ID | pas encore de CVE |
| CWE | CWE-918 |
| CVSS | Élevé : 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N |
| Produit | Gitea |
| Affecté | jusqu'à v1.27.3 (146cc3e) |
| Auth | authentifié (inscription ouverte par défaut) |
| Licence | GNU Affero GPL v3.0 |
| Lab | 127.0.0.1 uniquement |
S'inscrire, migrer depuis une URL d'apparence externe qui renvoie un 302 vers http://127.0.0.1:.../internal.git. La migration directe vers loopback est refusée. Le clone suivant la redirection ne l'est pas. Le fichier témoin atterrit dans son dépôt.
Pas de lecture disque file://. Pas de X-Gitea-Internal-Auth sur /api/internal (git ne définira pas cet en-tête). Pas de RCE non authentifié. Pas de RCE via métadonnées à moins de démontrer séparément que git a stocké le corps.
Autres restes du même tag : keys IDOR, hostmatcher 0.0.0.0/8.
La v1.27.3 a fermé la vague GHSA 2026 : CVE-2026-60004 (RCE via hook diffpatch, CISA KEV), CVE-2026-59774 (#+INCLUDE Org-mode non authentifié), les barrières Actions fork-PR, le défaut reverse-proxy X-WEBAUTH-USER. Restez sur 1.27.3+ pour ceux-là. Je suis resté sur ce tag malgré tout.
CVE-2026-59765 a corrigé le uri.Open de Go. CVE-2026-22874 a corrigé les reservedIPNets de Go. Le redirecteur de Git est toujours un FIXME. Le changelog « disable HTTP redirects on pull mirror sync » ne concerne pas cette fonction.
Le lab place Gitea sur 8.8.4.2 afin qu'un hôte appât sur 8.8.4.10 paraisse externe au hostmatcher. Le git HTTP interne écoute uniquement sur loopback dans le netns de Gitea (network_mode: service:gitea, port 9418). La migration directe vers 127.0.0.1 doit échouer. Le clone suivant la redirection doit réussir. redir.py renvoie un 302 de /bait.git vers http://127.0.0.1:9418/internal.git. Le fichier témoin WITNESS contient GHSA-GITEA-GIT-REDIR-SSRF.
Fausses pistes déjà consignées : ALLOW_LOCALNETWORKS true (alors la liste d'autorisation disparaît) ; file:// ; X-Gitea-Internal-Auth ; un reverse shell. Du théâtre. La migration directe vers loopback refusée est le contrôle.
Les bugs clients ressemblent à des bugs produit. Un helper qui masque http.client n'envoie jamais de paquet. .recv() sur un HTTPResponse n'est pas .read(). Je le mentionne une fois parce que cela a coûté du temps et a ressemblé, pendant une minute, à ce que la 1.27.3 ait terminé le travail.
cd lab
./run.sh
Cible uniquement 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
Ne traitez pas le DNS au moment de la soumission comme la frontière. Forcez http.followRedirects=false (et normalisez les URL de clone en .git pour que GitLab fonctionne toujours), ou proxifiez le git HTTP via un dialer qui réexécute hostmatcher à chaque saut.
HandleGitCmdHTTPRedirection · IsMigrateURLAllowedGNU Affero GPL v3.0. Voir LICENSE. Lab loopback uniquement. Aucune garantie.