News

One HTTP header could impersonate any user in Gitea's Docker image (CVE-2026-20896)

A high-severity flaw in Gitea's official Docker image — CVE-2026-20896, CVSS 9.8 — let one HTTP header impersonate any user, including an admin. It only bites instances that enabled reverse-proxy login and kept the wildcard default. Upgrade to 1.26.3 or later, stop trusting every IP, and ask whether your Git server should face the internet at all.

By Paul Rudenko, Security ResearcherJul 14, 20266 min read

What happened with the Gitea Docker image (CVE-2026-20896)?

The flaw is an authentication bypass rated CVSS 9.8. Gitea’s official Docker images shipped an app.ini template that hard-coded REVERSE_PROXY_TRUSTED_PROXIES = * — a wildcard telling Gitea to trust the identity header from any source IP (Gitea 1.26.3/1.26.4 release notes).

With reverse-proxy login enabled, that wildcard means anyone who can reach the container’s HTTP port directly — not through the intended authenticating proxy — can send an X-WEBAUTH-USER: <username> header and be treated as that user, including an admin whose login name is known or guessable (as reported by The Hacker News).

The payoff for an attacker is unusually large. A Git server holds source code, deploy keys, and CI secrets, so admin takeover there is close to a full compromise of everything that server can reach.

Why is this a config problem, not a code bug?

The vulnerable line wasn’t something a user wrote. It was a default baked into the official image. You could deploy Gitea exactly as documented and inherit a wildcard you never chose.

That’s the pattern worth internalizing: insecure-by-default container images. A sensible-looking image ships a permissive setting so first-run “just works,” and that convenience quietly becomes everyone’s exposure. Gitea’s fix was the right one — remove the wildcard and make reverse-proxy authentication opt-in and admin-configured.

“Official image” is not the same as “hardened image.” A published default is tuned for a clean first boot, not for sitting on the open internet — that part is on whoever deploys it.

Who is actually affected?

Here’s the part most headlines blur. The bypass only works if an administrator explicitly enabled reverse-proxy authentication (ENABLE_REVERSE_PROXY_AUTHENTICATION = true) and left the wildcard trusted-proxies default in place, on a Docker image at version 1.26.2 or earlier. Deployments that never turned on reverse-proxy auth are not exploitable through this path.

So when you see “~6,200 internet-facing Gitea instances,” read it as the exposed population, not the vulnerable count. That figure comes from Sysdig’s threat researchers and counts instances reachable from the internet — not instances actually configured the vulnerable way. Exposed and vulnerable are not the same thing.

Sysdig also reported the first in-the-wild exploitation attempt roughly 13 days after public disclosure, with scanning coming from VPN exit nodes — early reconnaissance rather than mass compromise. The honest summary: if you run Gitea from the Docker image at 1.26.2 or earlier with reverse-proxy auth on and the default trusted-proxies, treat it as urgent; otherwise, patch on your normal cadence and move on.

For the site or server owner (plain English)

Strip the config detail away and the owner’s questions are simple: do we run a self-hosted Git server, does it need to be reachable from the internet, and who patches it?

  • Find out if you have one. “A Gitea box someone set up” is exactly the kind of system that gets forgotten. If a contractor or a past team stood up self-hosted Git, it may still be running — and still exposed.
  • Ask whether it should be public. A Git server holds your source code, deploy keys, and CI secrets — one of the highest-value things you can expose. For most teams it belongs behind a VPN or on an internal network, not on the open internet.
  • Make sure someone owns updates. If nobody’s name is against “keep Gitea patched,” that’s the finding — independent of this particular flaw.

You can’t lock down or retire a server you’ve forgotten is exposed. That’s what an external attack-surface scan is for: it looks at your domain from the outside, the way an attacker’s tooling does, and shows you what’s reachable — admin panels, login pages, self-hosted tools you didn’t remember were public. It won’t tell you “you have CVE-2026-20896” — no honest external scan can name a specific flaw from the outside — but it shows you what’s exposed so you can decide what to close.

How do developers and IT respond?

  • Upgrade now. Move to Gitea 1.26.3 or 1.26.4 (or later), where the wildcard default is removed and reverse-proxy authentication is opt-in.
  • Fix the trusted-proxies setting. Set REVERSE_PROXY_TRUSTED_PROXIES to the actual IP address(es) of your proxy — never *. If you don’t use reverse-proxy login, set ENABLE_REVERSE_PROXY_AUTHENTICATION = false.
  • Keep Gitea off the open internet. Put it behind the intended authenticating proxy or a VPN, not exposed on its container port directly.
  • Assume-breach check if you were exposed and configured the vulnerable way. Grep access logs for X-WEBAUTH-USER, audit for admin accounts you didn’t create, and rotate deploy keys, CI tokens, and other secrets the instance held. Patching afterward doesn’t undo access an attacker already used.
  • Reference: the Gitea 1.26.3/1.26.4 release notes and the CVE-2026-20896 record.

What’s the bigger lesson here?

Same shape as always: an internet-exposed app, a permissive default nobody chose, and automated scanners that find it fast. Gitea is this week’s example; last week it was a mass CMS-webshell campaign. The constant is never the specific CVE — it’s the forgotten thing facing the internet.

Patch cadence matters, but knowing your external footprint and keeping it small is what actually breaks the pattern. For the public web properties you do run, a free Website Security Score check is a fast outside-in read; for everything else, the discipline is the same — if it doesn’t need to be public, don’t leave it that way.

Frequently asked questions

Am I affected if I run Gitea?

Only under a specific combination. The bypass needs two things true at once: reverse-proxy authentication turned on (ENABLE_REVERSE_PROXY_AUTHENTICATION = true) and the Docker image's wildcard trusted-proxies default (REVERSE_PROXY_TRUSTED_PROXIES = *) still in place, on an image at version 1.26.2 or earlier. If you never enabled reverse-proxy login, this particular path doesn't apply to you. If you did — and especially if the instance is reachable from the internet — treat it as urgent: upgrade to 1.26.3 or later and set the trusted-proxies list to your real proxy IPs. The ~6,200 figure you may have seen is the count of internet-facing Gitea instances reported by Sysdig, not the number actually configured the vulnerable way. Exposed and vulnerable are not the same thing.

Should my Git server be reachable from the internet at all?

For most teams, no. A self-hosted Git server is one of the highest-value targets you can run: it holds your source code, deploy keys, and the secrets your CI uses to ship. If an attacker gets admin on it, the blast radius is everything that server can reach. Convenience is the usual reason it ends up public — remote contributors, a CI runner in another network — but there are safer ways to get that: a VPN, an SSH bastion, or an identity-aware proxy in front. The default posture should be internal-only, with public access as a deliberate, documented exception rather than something that happened because the container came up listening on a port. This isn't specific to Gitea or to this flaw; it's true of any tool that holds your code.

What should I do right now?

Three steps, in order. First, upgrade: move to Gitea 1.26.3 or 1.26.4 (or later), where the wildcard default is removed and reverse-proxy authentication is opt-in. Second, fix the configuration: set REVERSE_PROXY_TRUSTED_PROXIES to the actual IP addresses of your proxy — never the * wildcard — and disable reverse-proxy authentication entirely if you don't use it. Third, if your instance was internet-facing and configured the vulnerable way, don't just patch — check whether it was already used. Search your access logs for the X-WEBAUTH-USER header, look for admin accounts you didn't create, and rotate any deploy keys, CI tokens, and secrets the server held. Updating the software closes the door, but it doesn't remove access an attacker may have set up while the door was open.

Does MySecScan tell me if I have CVE-2026-20896?

No — and that limit is deliberate. MySecScan is an external attack-surface scanner: it looks at your domain from the outside, the way an attacker's tooling does, and reports what's reachable and how it's configured. It does not log into your systems or fingerprint the exact version and settings of a self-hosted Git server to declare "you have CVE-2026-20896," because that can't be done reliably from the outside, and guessing would be worse than useless. What it can do is show you the exposure that makes a flaw like this reachable in the first place — which servers, admin panels, and self-hosted tools are facing the internet — so you know where to look. In a story like this, that visibility is the useful part: the fix is an upgrade, but only once you know the server is there and public.

Sources