
PoC público e detector para CVE-2026-20896 ("Gitea Docker: Um Cabeçalho, Qualquer Utilizador")
A imagem Docker oficial do Gitea (até a versão 1.26.2 inclusive) vem com REVERSE_PROXY_TRUSTED_PROXIES = * na sua configuração padrão. Se você ativar o login por proxy reverso, esse curinga significa que todo IP de origem é tratado como um proxy confiável, então qualquer pessoa que consiga alcançar a porta pode enviar um cabeçalho X-WEBAUTH-USER e entrar como quem quiser. Sem senha, sem token.
Reportei isso ao Gitea e foi corrigido em 1.26.3 / 1.26.4. Este repositório é o meu próprio relatório, um PoC funcional e um verificador. É para um bug corrigido e público.
O Gitea suporta autenticação por proxy reverso: você o coloca atrás de um proxy que define X-WEBAUTH-USER, e o Gitea confia nesse cabeçalho para o nome de usuário. Isso é válido desde que apenas o seu proxy possa defini-lo. A configuração que deveria garantir isso é REVERSE_PROXY_TRUSTED_PROXIES, uma lista de permissões de IP. O Gitea só honra o cabeçalho quando o IP de origem da requisição está dentro dela.
O padrão seguro documentado, o do app.example.ini, é 127.0.0.0/8,::1/128: apenas loopback, então, de fábrica, apenas o proxy local é confiável. A imagem oficial do Docker não usa isso. O template do app.ini define * como valor fixo (docker/root/etc/templates/app.ini:55, e docker/rootless/etc/templates/app.ini:52 para a imagem rootless). * corresponde a qualquer IP de origem, então a verificação da lista de permissões não faz nada. Ative o login por proxy reverso e agora qualquer pessoa que alcance a porta pode enviar o cabeçalho, não apenas o seu proxy. Com o registro automático ativado, a conta é criada na hora. Envie o nome de usuário de um administrador e você é o administrador.
Portanto, o código não está errado; o padrão empacotado está, e isso é específico das imagens Docker. Uma instalação binária ou compilada por conta própria que segue o app.example.ini mantém o padrão de loopback e não é afetada.
Requer Docker e Python 3 (apenas biblioteca padrão, nada para 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
Veja como fica com a imagem incluída:
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)
O bypass funciona na sessão web, não na API de token em /api/v1/..., que ignora o cabeçalho.
detect.py envia uma sondagem inofensiva e a compara com uma requisição normal. Ele não toca em nada.
python3 detect.py https://gitea.example.com
Ele imprime VULNERABLE, looks-safe ou inconclusive. Execute-o apenas contra algo que você possua ou esteja autorizado a testar.
Atualize para 1.26.3 / 1.26.4 ou posterior. A autenticação por proxy reverso agora é opt-in e a imagem não inclui mais o curinga. Se você ainda não puder atualizar, defina REVERSE_PROXY_TRUSTED_PROXIES para o IP ou CIDR real do seu proxy (nunca *), ou desative ENABLE_REVERSE_PROXY_AUTHENTICATION se não o utiliza.
Encontrei este bug e o reportei ao Gitea em 2026-05-26. Sou o relator citado no aviso do Gitea, GHSA-f75j-4cw6-rmx4.
Algumas matérias creditaram o repositório Exploitarium por isso em vez de mim. Isso está errado, e é uma coisa separada do que aquele repositório publicou. Desde então, consegui que vários desses blogs fossem corrigidos; alguns ainda estão errados.
Licenciado sob MIT. Veja LICENSE.