
PoC público y detector para CVE-2026-20896 ("Gitea Docker: One Header, Any User")
La imagen oficial de Docker de Gitea (hasta la 1.26.2 inclusive) incluye
REVERSE_PROXY_TRUSTED_PROXIES = * en su configuración por defecto. Si activas el
inicio de sesión mediante proxy inverso, ese comodín significa que cada IP de origen se trata como un proxy
de confianza, por lo que cualquiera que pueda alcanzar el puerto puede enviar una cabecera X-WEBAUTH-USER e
iniciar sesión como quien quiera. Sin contraseña, sin token.
Reporté esto a Gitea y está corregido en 1.26.3 / 1.26.4. Este repositorio es mi propio informe, un PoC funcional y un comprobador. Es para un fallo público ya parcheado.
Gitea admite la autenticación mediante proxy inverso: lo colocas detrás de un proxy que establece
X-WEBAUTH-USER, y Gitea confía en esa cabecera para el nombre de usuario. Eso está bien siempre
y cuando solo tu proxy pueda establecerla. El ajuste que se supone que debe garantizar eso
es REVERSE_PROXY_TRUSTED_PROXIES, una lista de permitidos de IP. Gitea solo honra la cabecera
cuando la IP de origen de la solicitud está dentro de esa lista.
El valor por defecto documentado como seguro, el de app.example.ini, es
127.0.0.0/8,::1/128: solo loopback, así que de serie solo se confía en el proxy local.
La imagen oficial de Docker no usa eso. Su plantilla app.ini
fija * (docker/root/etc/templates/app.ini:55, y
docker/rootless/etc/templates/app.ini:52 para la imagen rootless). * coincide con
cada IP de origen, por lo que la comprobación de la lista de permitidos no hace nada. Activa el inicio
de sesión mediante proxy inverso y ahora cualquiera que pueda alcanzar el puerto puede enviar la cabecera, no solo tu proxy.
Con el auto-registro activado, la cuenta se crea en el acto. Envía el nombre de usuario de un
admin y eres el admin.
Así que el código no está mal, el valor por defecto empaquetado sí lo está, y es específico de las imágenes
de Docker. Una instalación binaria o compilada manualmente que siga app.example.ini mantiene el
valor por defecto de loopback y no se ve afectada.
Necesita Docker y Python 3 (solo biblioteca estándar, nada que instalar).
docker compose up -d # boots vulnerable gitea/gitea:1.26.2
# give it ~30-60s to finish first-run setup, then:
python3 poc.py # random new victim, shows auto-registration
python3 poc.py http://localhost:3000 admin # impersonate a chosen username
docker compose down -v # clean up
Así se ve contra la imagen incluida:
1) /user/settings with no header -> HTTP 303 (redirect to login = not authed)
2) /user/settings with X-WEBAUTH-USER -> HTTP 200
logged in as 'pocadmin' - no password, no token, any source IP
3) /pocadmin profile page -> HTTP 200 (account created on the fly)
El bypass funciona en la sesión web, no en la API de tokens en /api/v1/..., que
ignora la cabecera.
detect.py envía una única sonda inofensiva y la compara con una solicitud normal. No
toca nada.
python3 detect.py https://gitea.example.com
Imprime VULNERABLE, looks-safe o inconclusive. Ejecútalo solo contra algo que sea tuyo o que tengas permitido probar.
Actualiza a 1.26.3 / 1.26.4 o posterior. La autenticación mediante proxy inverso ahora es opcional y la
imagen ya no incluye el comodín. Si aún no puedes actualizar, establece
REVERSE_PROXY_TRUSTED_PROXIES con la IP o el CIDR reales de tu proxy (nunca *), o
desactiva ENABLE_REVERSE_PROXY_AUTHENTICATION si no lo usas.
Encontré este fallo y lo reporté a Gitea el 26 de mayo de 2026. Soy la persona que reportó el fallo mencionada en el aviso de Gitea, GHSA-f75j-4cw6-rmx4.
Alguna cobertura acreditó el repositorio Exploitarium por esto en lugar de a mí. Eso es incorrecto, y es algo distinto de lo que ese repositorio publicó. Desde entonces he conseguido que varios de esos blogs se corrijan; unos pocos siguen estando mal.
Con licencia MIT. Consulta LICENSE.