
CVE-2026-22874 writeup : liste d'autorisation SSRF incomplète dans le webhook/migration de Gitea (transition IPv6 et métadonnées cloud). Corrigé dans Gitea 1.26.3.
Analyse technique et références pour CVE-2026-22874 (GHSA-2r5c-gw76-rh3w) : une liste d'autorisation SSRF incomplète dans la livraison des webhooks et la migration de dépôts de Gitea, corrigée dans Gitea 1.26.3.
J'ai signalé indépendamment cette classe de SSRF aux mainteneurs de Gitea et suis crédité comme contributeur (other) dans l'avis publié ; le rapporteur principal de l'avis est J. Leitschuh. Ce dépôt est une analyse défensive et éducative de l'avis public — le problème est corrigé.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N)<= 1.26.2 — Corrigé : 1.26.3La liste d'autorisation SSRF par défaut de Gitea (, utilisée à la fois pour la livraison des webhooks et les migrations de dépôts) reposait sur la bibliothèque standard Go , qui ne reconnaît que les RFC 1918 (, , ) et RFC 4193 (). Plusieurs plages d'adresses couramment utilisées pour les services de métadonnées cloud, les réseaux internes et les mécanismes de transition IPv6 ne sont pas "privées" selon cette définition et ont donc passé le filtre "externe", permettant à un utilisateur authentifié de faire émettre par le serveur des requêtes HTTP vers ces destinations.
MatchBuiltinExternalnet.IP.IsPrivate()10/8172.16/12192.168/16fc00::/7Étant donné que la livraison du webhook enregistre le statut de la réponse, les en-têtes et jusqu'à ~1 Mio de corps et le rend dans l'interface d'historique des webhooks, la SSRF n'est pas aveugle : l'attaquant peut lire ce que le service interne ciblé renvoie.
net.IP.IsPrivate()| Plage | Signification |
|---|---|
100.64.0.0/10 | RFC 6598 NAT de niveau opérateur |
168.63.129.16/32 | Azure WireServer / point de terminaison des métadonnées d'instance |
64:ff9b::/96 | RFC 6052 NAT64 (peut encapsuler 169.254.169.254, ex. AWS IMDS) |
2001::/32 | RFC 4380 Teredo |
2002::/16 | RFC 3056 6to4 |
Les formes IPv6 mappées IPv4 des plages ci-dessus sont également affectées.
Un utilisateur authentifié qui peut créer ou modifier un webhook (ou lancer une migration) peut ainsi atteindre les points de terminaison de métadonnées cloud — par exemple AWS IMDS via un mappage NAT64, ou Azure WireServer à 168.63.129.16 — et les services internes, puis lire la réponse via la vue d'historique des webhooks.
La politique par défaut est MatchBuiltinExternal dans la vérification HostMatchList de Gitea, qui traitait toute adresse globale-unicast non IsPrivate() comme autorisée :
case MatchBuiltinExternal:
if ip.IsGlobalUnicast() && !ip.IsPrivate() {
return true
}
net.IP.IsPrivate() n'est pas une frontière de sécurité pour "externe" : elle ne tient pas compte du NAT de niveau opérateur, des points de terminaison de métadonnées cloud, ni des plages de transition IPv6 qui peuvent être routées vers des destinations internes ou de métadonnées.
Corrigé dans Gitea 1.26.3 en étendant le filtre intégré pour bloquer les plages supplémentaires ci-dessus. Les opérateurs doivent mettre à niveau vers la version 1.26.3 ou ultérieure. Comme défense en profondeur :