## https://sploitus.com/exploit?id=0FC89D13-9196-52D2-AF79-402D089DB0F4
# CVE-2026-20896
Gitea's official Docker image (up to and including 1.26.2) ships
`REVERSE_PROXY_TRUSTED_PROXIES = *` in its default config. If you turn on
reverse-proxy login, that wildcard means every source IP is treated as a trusted
proxy, so anyone who can reach the port can send an `X-WEBAUTH-USER` header and
get logged in as whoever they want. No password, no token.
I reported this to Gitea and it's fixed in 1.26.3 / 1.26.4. This repo is my own
writeup, a working PoC, and a checker. It's for a patched, public bug.
- CVE record: https://vulners.com/cve/CVE-2026-20896 (reporter: rz1027)
- Gitea advisory: https://github.com/go-gitea/gitea/security/advisories/GHSA-f75j-4cw6-rmx4
- Fix / release notes: https://blog.gitea.com/release-of-1.26.3-and-1.26.4/
## The bug
Gitea supports reverse-proxy authentication: you put it behind a proxy that sets
`X-WEBAUTH-USER`, and Gitea trusts that header for the username. That's fine as
long as only your proxy can set it. The setting that's supposed to enforce that
is `REVERSE_PROXY_TRUSTED_PROXIES`, an IP allowlist. Gitea only honors the header
when the request's source IP falls inside it.
The documented-safe default, the one in `app.example.ini`, is
`127.0.0.0/8,::1/128`: loopback only, so out of the box just the local proxy is
trusted. The official Docker image doesn't use that. Its `app.ini` template
hard-codes `*` (`docker/root/etc/templates/app.ini:55`, and
`docker/rootless/etc/templates/app.ini:52` for the rootless image). `*` matches
every source IP, so the allowlist check does nothing. Turn on reverse-proxy login
and now anyone who can reach the port can send the header, not just your proxy.
With auto-registration on, the account is created on the spot. Send an admin's
username and you're the admin.
So the code isn't wrong, the packaged default is, and it's specific to the Docker
images. A binary or self-built install that follows `app.example.ini` keeps the
loopback default and isn't affected.
## Try it
Needs Docker and Python 3 (standard library only, nothing to install).
```bash
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
```
What it looks like against the bundled image:
```
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)
```
Without the header the page bounces you to the login screen. With one spoofed
header the same request comes back authenticated as that user.
(Note: this is a web-session bypass. The token-based REST API at `/api/v1/...`
does not honor `X-WEBAUTH-USER`.)
## Check your own instance
`detect.py` sends one harmless probe and compares it to a normal request. It
doesn't touch anything.
```bash
python3 detect.py https://gitea.example.com
```
It'll say VULNERABLE if the instance logged in the spoofed header, or tell you
it looks safe. One caveat it prints: if reverse-proxy auth is on but
auto-registration is off, a random probe name can't log in, but an existing
username could still be impersonated, so check your config in that case.
Only run it against something you own or are allowed to test.
## Fix
Upgrade to 1.26.3 / 1.26.4 or later. Reverse-proxy auth is opt-in now and the
image no longer ships the wildcard. If you can't upgrade yet, set
`REVERSE_PROXY_TRUSTED_PROXIES` to your proxy's actual IP or CIDR (never `*`), or
turn off `ENABLE_REVERSE_PROXY_AUTHENTICATION` if you don't use it.
## Timeline
- 2026-05-26: Reported privately to Gitea.
- 2026-07-03: CVE-2026-20896 published, credited to rz1027.
- Fixed in Gitea 1.26.3 / 1.26.4.
## Credit
Found and reported by rz1027 (Ali Mustafa). Credited as the reporter on the
CVE record and in the Gitea 1.26.3/1.26.4 release notes.
MIT licensed. See [LICENSE](./LICENSE).