One HTTP header, any Gitea user: inside CVE-2026-20896
The official Gitea Docker image trusted every proxy by default. Where reverse-proxy login was on, a single spoofed header logged you in as admin.

A Gitea box is not a web panel - it is your source code, the secrets developers committed by accident, and the deploy keys that reach production. CVE-2026-20896 hands all of that to an attacker with a single unauthenticated HTTP request. The official Gitea Docker image shipped REVERSE_PROXY_TRUSTED_PROXIES = **, meaning it trusted reverse-proxy authentication headers from any* source IP. On any instance where an admin had turned on reverse-proxy login, sending X-WEBAUTH-USER: admin was enough to be logged in as that user - no password, no token, no 2FA.
Scores as of 2026-07-09live record →
What actually broke
This is a packaging defect, not a code bug. Gitea gates reverse-proxy authentication on two settings. ENABLE_REVERSE_PROXY_AUTHENTICATION (default false) turns the feature on so Gitea will read a username from a trusted header. REVERSE_PROXY_TRUSTED_PROXIES decides which source IPs are allowed to assert that header - and its documented default is loopback only, 127.0.0.0/8,::1/128.
The official Docker image overrode that safe default. Its bundled app.ini template hard-coded REVERSE_PROXY_TRUSTED_PROXIES = * at docker/root/etc/templates/app.ini:55 (and the rootless template at line 52). The wildcard means "trust every source as a proxy," which disables the IP check that is supposed to ensure only your real front-end can vouch for an identity. Binary and source installs that follow app.example.ini keep the loopback default and were never affected.
The exploit: one header, web session, done
When the feature is on, Gitea reads the authenticated username from X-WEBAUTH-USER (the header name is configurable, so confirm yours). An attacker sets it to any valid or guessable login - admin and gitea_admin are the obvious picks. The public proof-of-concept confirms the bypass grants a web session only: the browser UI renders as the impersonated user, while the token API at /api/v1/... ignores the header. That is plenty - the attacker drives the UI to read private repos, add SSH or deploy keys, edit webhooks, and mint an API token, then pivots. If ENABLE_REVERSE_PROXY_AUTO_REGISTRATION is on, spoofing an unknown username even creates the account on the fly.
# Once the precondition holds, the whole exploit is one request:
curl -H "X-WEBAUTH-USER: admin" http://<target>:3000/user/settings
# No header -> 303 redirect to /user/login (not authenticated)
# With header -> 200, rendered as 'admin' (authenticated, no creds)The exploitation chain
From an exposed instance to a supply-chain pivot, the path is short. The one decision point - the three-part precondition - is where the entire chain lives or dies, and it is also the cheapest place to break it.
CVE-2026-20896: one header to supply-chain pivot
- Internet-facing Gitea — Attacker fingerprints an exposed Gitea (T1190).
- Recon: version + reachability — Read /api/v1/version; check if the container port answers directly.
- Header auth ON + trusted-proxies = * + port reachable? — The three-part precondition. Header auth OFF -> chain dead-ends.
- Spoof X-WEBAUTH-USER: admin — One unauthenticated request. No password, no token.
- Authenticated web session as admin — UI renders as admin. Leaves no failed-login trail.
- Loot + persist — Read private repos/secrets, add SSH keys, mint tokens, edit webhooks.
- Supply-chain pivot — Poison CI/CD, exfiltrate future pushes (T1195).
- PREVENT: patch 1.26.4 / pin CIDR / isolate port — Closes the precondition - the cheapest chokepoint.
- PREVENT: strip inbound header at the edge — Proxy clears client-supplied X-WEBAUTH-USER before setting its own.
- DETECT: header from non-proxy source — The only reliable signal - a Sigma rule at the proxy/edge.
Am I affected?
You are exposed only if all three conditions hold. Miss any one and the wildcard is inert.
| Question | Answer |
|---|---|
| Affected | Official Gitea Docker / rootless image, versions <= 1.26.2 |
| Fixed in | 1.26.4 (the fix first shipped in 1.26.3, but 1.26.3 has a separate regression - skip it) |
| Not affected | Binary / source installs; any instance with reverse-proxy auth disabled |
| Condition 1 | ENABLE_REVERSE_PROXY_AUTHENTICATION = true (off by default) |
| Condition 2 | REVERSE_PROXY_TRUSTED_PROXIES still = * (the image default) |
| Condition 3 | Gitea's HTTP port reachable by anything other than the real proxy |
| Severity | CVSS 9.8 (CWE-284). EPSS ~0.78%. Not on CISA KEV as of 2026-07-09 |
A safe self-test on a box you own: compare an anonymous request to one carrying the header. If curl -H 'X-WEBAUTH-USER: gitea_admin' https://<host>/user/settings returns 200 where the plain request redirects to /user/login, the header is being trusted and you are vulnerable.
Timeline
Disclosure to in-the-wild probing
- Releases 1.26.3 and 1.26.4Part of a security release fixing ~9 CVEs; dates vary slightly across sourcessource
- Public PoC + detectorIndependent researcher publishes a working exploit and checker
- In-the-wild probing reportedSysdig reports the first hit ~13 days after disclosure, from a VPN exit nodesource
Is it being exploited?
Yes, but state it carefully. Sysdig reported detecting the first in-the-wild hit roughly 13 days after disclosure, traced to a ProtonVPN exit node - characterized as early-stage reconnaissance and attempted bypass, with no confirmed successful breach. That signal is currently single-source; no GreyNoise or Shadowserver tag has surfaced for this CVE. The bug is not on CISA KEV, and EPSS sits low at ~0.78% - which reflects the narrow precondition, not the ease of hitting a matching instance. Do not let a low EPSS talk you out of patching a box that meets all three conditions.
How to detect it
Gitea's access log does not record inbound request headers, so the reliable detection point is the reverse proxy, WAF, or load balancer that terminates the connection. A proxy that legitimately sets X-WEBAUTH-USER should always strip any inbound copy first - so any inbound value from a client is anomalous. Log the header, then alert on it arriving from a source outside your trusted proxy set, and hunt downstream for privileged actions with no preceding login.
# 1) log the header at the edge (nginx)
log_format webauth '$remote_addr - $host [$time_local] "$request" '
'$status hdr_webauth="$http_x_webauth_user"';
access_log /var/log/nginx/gitea.log webauth;
# 2) hunt: header value = a privileged login, from a non-proxy source
# grep -iE 'hdr_webauth="(admin|gitea_admin|root)"' /var/log/nginx/gitea.log
# 3) post-compromise: new SSH/deploy keys, new tokens, or new admin users
# attributed to admin/gitea_admin with NO preceding password/2FA loginHow to fix it
- Upgrade the image to 1.26.4 (or later). The fix removes the wildcard from the Docker templates so reverse-proxy auth becomes genuinely opt-in.
- Fix the persisted config too: upgrading the image does not rewrite an app.ini you already mounted with ''. Set REVERSE_PROXY_TRUSTED_PROXIES to your real proxy CIDR (never '').
- If you do not use header auth, disable it: ENABLE_REVERSE_PROXY_AUTHENTICATION = false. That alone neutralizes the flaw.
- Network-isolate the port so only the proxy can reach Gitea - do not publish it to 0.0.0.0.
- Strip inbound X-WEBAUTH-USER (and its companions) at the proxy edge before your proxy sets its own.
- Then hunt - a spoofed session left no failed-login trail, so a clean auth log does not clear a previously-exposed box.
Is CVE-2026-20896 exploited in the wild?
Is every Gitea Docker instance vulnerable?
Which version fixes it?
How would I even see this in my logs?
Sources
- Gitea security advisory GHSA-f75j-4cw6-rmx4
- Gitea PR #38151 - the fix
- Gitea configuration cheat sheet
- Gitea blog - Release of 1.26.3 and 1.26.4
- NVD - CVE-2026-20896
- SecurityWeek - Critical Gitea flaw under active exploitation
- The Hacker News - Threat actors probe Gitea Docker flaw
- The Stack - Critical Gitea vulnerability exploited
- Rescana - Active exploitation alert (CVE-2026-20896)
- CyCognito - Gitea authentication bypasses
- Hive Security - Gitea/Forgejo 1.26.3 security release