
PoC public et détecteur pour CVE-2026-20896 ("Gitea Docker: One Header, Any User")
L'image Docker officielle de Gitea (jusqu'à la version 1.26.2 incluse) embarque
REVERSE_PROXY_TRUSTED_PROXIES = * dans sa configuration par défaut. Si vous activez
la connexion par reverse-proxy, ce joker signifie que chaque IP source est
traitée comme un proxy de confiance, donc toute personne pouvant atteindre le port peut
envoyer un en-tête X-WEBAUTH-USER et se connecter comme n'importe qui. Pas de mot de passe, pas de jeton.
J'ai signalé ce problème à Gitea et c'est corrigé dans les versions 1.26.3 / 1.26.4. Ce dépôt est ma propre analyse, un PoC fonctionnel et un outil de vérification. Il concerne un bug public et corrigé.
Gitea prend en charge l'authentification par reverse-proxy : vous le placez derrière un proxy qui définit
X-WEBAUTH-USER, et Gitea fait confiance à cet en-tête pour le nom d'utilisateur. C'est acceptable tant
que seul votre proxy peut le définir. Le paramètre censé garantir cela
est REVERSE_PROXY_TRUSTED_PROXIES, une liste blanche d'adresses IP. Gitea ne respecte l'en-tête que
lorsque l'IP source de la requête se trouve dans cette liste.
Le défaut documenté comme sûr, celui de app.example.ini, est
127.0.0.0/8,::1/128 : boucle locale uniquement, donc par défaut seul le proxy local est
digne de confiance. L'image Docker officielle ne l'utilise pas. Son modèle app.ini
code en dur * (docker/root/etc/templates/app.ini:55, et
docker/rootless/etc/templates/app.ini:52 pour l'image rootless). * correspond
à chaque IP source, donc la vérification de la liste blanche ne fait rien. Activez la connexion
par reverse-proxy et désormais toute personne pouvant atteindre le port peut envoyer l'en-tête,
pas seulement votre proxy. Avec l'auto-enregistrement activé, le compte est créé sur-le-champ. Envoyez
le nom d'utilisateur d'un administrateur et vous êtes l'administrateur.
Donc le code n'est pas en cause, c'est la valeur par défaut embarquée, et c'est spécifique aux images
Docker. Une installation binaire ou auto-compilée qui suit app.example.ini conserve la
boucle locale par défaut et n'est pas affectée.
Nécessite Docker et Python 3 (bibliothèque standard uniquement, rien à installer).
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
Voici ce que ça donne avec l'image incluse :
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)
Le contournement fonctionne sur la session web, pas sur l'API de jeton /api/v1/..., qui
ignore l'en-tête.
detect.py envoie une sonde inoffensive et la compare à une requête normale. Il
ne touche à rien.
python3 detect.py https://gitea.example.com
Il affiche VULNERABLE, looks-safe ou inconclusive. Ne l'exécutez que sur quelque chose que vous possédez ou que vous êtes autorisé à tester.
Passez à la version 1.26.3 / 1.26.4 ou ultérieure. L'authentification par reverse-proxy est désormais opt-in et l'image
n'embarque plus le joker. Si vous ne pouvez pas encore mettre à jour, définissez
REVERSE_PROXY_TRUSTED_PROXIES sur l'IP réelle ou le CIDR de votre proxy (jamais *), ou
désactivez ENABLE_REVERSE_PROXY_AUTHENTICATION si vous ne l'utilisez pas.
J'ai trouvé ce bug et l'ai signalé à Gitea le 2026-05-26. Je suis le rapporteur nommé dans l'avis de sécurité de Gitea, GHSA-f75j-4cw6-rmx4.
Certains articles ont crédité le dépôt Exploitarium pour cela au lieu de moi. C'est inexact, et c'est une chose distincte de ce que ce dépôt a publié. J'ai depuis fait corriger plusieurs de ces blogs ; quelques-uns sont encore erronés.
Sous licence MIT. Voir LICENSE.