
Laboratorio de prueba de concepto que demuestra un SSRF autenticado en la lista de permitidos del hostmatcher de Gitea, donde un webhook a http://0.0.0.1 alcanza servicios de loopback a los que 127.0.0.1 no puede.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitea-hostmatcher-0000-ssrf
Gitea 1.27.3 - Gitea
CVE-2026-22874 añadió reservedIPNets. CGNAT, TEST-NET, NAT64, Teredo, Azure WireServer. Rangos reales. No el bloque 0.0.0.0/8 de RFC 6890, el antiguo "this network". IANA todavía lo publica. IsGlobalUnicast de Go es true para 0.0.0.1. No es IsLoopback (127.0.0.0/8). No es IsPrivate. La lista de permitidos externa por defecto lo trata como internet pública. Linux entregará 0.0.0.1 a un socket local si esa dirección está en lo. Mismo listener, dos nombres.
Un usuario autenticado puede apuntar un webhook a http://0.0.0.1 y leer el cuerpo HTTP del historial de hooks. 127.0.0.1 en el mismo puerto es denegado.
| ID | sin CVE todavía |
| 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 |
| Afectado | hasta v1.27.3 (146cc3e); remanente de CVE-2026-22874 |
| Auth | usuario autenticado que puede crear un webhook de repositorio |
| Licencia | GNU Affero GPL v3.0 |
| Lab | solo 127.0.0.1 |
Registrarse (registro abierto por defecto), crear un repositorio, apuntar un webhook a http://0.0.0.1:<local-port>/, probarlo y leer el cuerpo de la respuesta desde el historial de hooks. Eso es HTTP de loopback que 127.0.0.1 en el mismo puerto no puede alcanzar: cualquier cosa escuchando en el netns de Gitea que se haya enlazado más ampliamente que loopback, incluidos servicios que el operador creía que eran "solo localhost" porque bloqueaban 127.0.0.0/8.
No establece X-Gitea-Internal-Auth. No es RCE por sí mismo. Es un cuerpo HTTP completo desde una dirección que la lista de permitidos todavía llama externa.
v1.27.3 es la compilación que cerró la oleada de GHSA de 2026. Aun así me quedé en esa etiqueta. Los parches que añaden una lista de permitidos o una lista de denegados son un regalo: lees la lista y luego te preguntas qué sigue teniendo IANA que la lista olvidó. Dos remanentes ya tenían labs en este árbol (keys IDOR y git-redirect SSRF). La tabla de remanentes de ese post listaba un webhook a http://0.0.0.1/ como un mapa. Este es el SUCCESS.
La entrega de webhooks almacena el cuerpo HTTP en el historial de hooks. Eso es un oráculo mejor que git clone, que en su mayoría es "¿funcionó el fetch?". Puse un oráculo en el netns de Gitea en el puerto 8080, añadí 0.0.0.1/8 en lo con NET_ADMIN, y creé dos webhooks. Primero el control negativo.
Caminos equivocados ya registrados: tratar el HTTP 201 de creación de webhook como el oráculo (el historial es el oráculo); que la entrega a 127.0.0.1 tuviera éxito (eso sería un bug diferente - el lab requiere que sea denegada); golpear /api/internal con X-Gitea-Internal-Auth; una reverse shell. Teatro. El testigo es GITEA-0000-SSRF en el historial de hooks.
cd lab
./run.sh
Objetivo solo http://127.0.0.1:18131. El oráculo comparte el netns de Gitea en el puerto 8080. run.sh añade 0.0.0.1/8 en lo.
create-hook name=neg-loopback status=201
negative-control 127.0.0.1 blocked
create-hook name=hit-0000 status=201
poll id=2 witness=True
SUCCESS GITEA-HOSTMATCHER-0000
Añadir 0.0.0.0/8 a reservedIPNets, o dejar de tratar IsGlobalUnicast como "seguro para marcar". A 0.0.0.1 se le debe denegar de la misma manera que a 127.0.0.1.
hostmatcher.goGNU Affero GPL v3.0. Ver LICENSE. Solo lab de loopback. Sin garantía.