The bouncer was told to trust anyone wearing a name tag — and the name tags are self-serve
CVE-2026-20896 is a pre-auth authentication bypass in self-hosted Gitea instances deployed from the official Docker images. The image template shipped with REVERSE_PROXY_TRUSTED_PROXIES = *, so when reverse-proxy header auth is enabled, Gitea will accept an X-WEBAUTH-USER: admin header from *any* source IP and log the caller in as that user — no cookie, no password, no MFA. Affected versions are all Gitea Docker builds prior to 1.26.3; the fix pins trusted proxies to loopback and makes header auth opt-in.
There is no assigned vendor CVSS, but the community estimate of 9.8 is honest. This is a canonical supply-chain-tier compromise: attacker becomes admin on your source-code system, which means source theft, backdoored commits, stolen CI tokens, webhooks pointed anywhere, and SSH keys minted at will. Community severity matches reality — the only reason to soften it is if your Gitea isn't Docker-deployed or you never turned on reverse-proxy auth.
5 steps from start to impact.
Locate an exposed Gitea instance
/api/v1/version or the login page banner. Shodan/Censys queries for gitea return tens of thousands of instances. Tooling: httpx + nuclei template gitea-detect.- HTTP/HTTPS reachability to the Gitea web port (3000 or 443)
- Instances behind Cloudflare Access, Tailscale, or VPN-only exposure are not internet-reachable
- Some deployments front Gitea with an actual auth proxy (Authelia/oauth2-proxy) that strips
X-WEBAUTH-USER
product:Gitea and Censys services.software.product:gitea enumerate the population; GreyNoise is beginning to tag mass scans post-exploitarium dropConfirm reverse-proxy header auth is enabled
X-WEBAUTH-USER: nonexistent and watches the response. A 302 to a create-user flow or a session cookie confirms the config path is live. Weaponized in bikini's exploitarium PoC as a single curl one-liner.ENABLE_REVERSE_PROXY_AUTHENTICATION = trueinapp.ini- Instance built from the vulnerable official Docker image where
REVERSE_PROXY_TRUSTED_PROXIES = *
- Reverse-proxy auth is not on by default in bare-metal installs — this is largely a Docker-shipped-default problem
- Ops teams who overrode the trusted-proxy list to a specific CIDR are unaffected
X-WEBAUTH-USER from a non-proxy IP is the tell — trivial to alert on in NGINX/Traefik logsImpersonate the admin account
X-WEBAUTH-USER: <admin-username> (commonly gitea, admin, or the first-created user). Gitea trusts the header and returns an authenticated session cookie. The exploitarium PoC is one curl invocation.- Knowledge or guess of an admin username — enumerable via
/api/v1/users/search
- None once step 2 succeeds — this is the whole exploit
Establish persistence and pivot
/user/settings/applications), uploads an SSH key, or modifies runner registration tokens. From here they clone/steal every private repo, inject commits into build pipelines, and pull secrets from .env or CI config files. Tooling: standard Gitea API + gh-style scripts.- Successful admin session from step 3
- Signed-commit enforcement on protected branches limits commit injection, but does not prevent read/exfil
- External CI runners with hardened OIDC won't accept the forged token — but internal Actions runners will
Supply-chain payload
- Downstream consumers pull from the compromised repos on
main/latestwithout pinning
- Reproducible builds and artifact signing verification catch this — but almost no one enforces it
The supporting signals.
| In-the-wild status | Active exploitation reported as of 30 June 2026 per Daily Security Review; not yet CISA KEV as of 1 July 2026 |
|---|---|
| Proof-of-concept | Public one-header PoC in the exploitarium GitHub repo by anonymous researcher bikini (repo now removed but mirrored); trivial curl -H 'X-WEBAUTH-USER: admin' reproduction |
| EPSS | Not yet scored (published <14 days ago); expected to climb into the >90th percentile given trivial PoC + active exploitation |
| KEV status | Not currently listed; strong candidate for near-term addition given exploitation reports |
| CVSS | No vendor/CNA-assigned score; community estimate CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H → 9.8 |
| Affected versions | All Gitea Docker images prior to 1.26.3 where ENABLE_REVERSE_PROXY_AUTHENTICATION=true and REVERSE_PROXY_TRUSTED_PROXIES=* (the shipped default) |
| Fixed version | Gitea 1.26.3 (and 1.26.4 rollup); reverse-proxy auth is now opt-in and trusted-proxies defaults to loopback only |
| Exposure data | Shodan reports ~35–45k internet-reachable Gitea instances (product:Gitea); Docker-deployed share estimated majority given official image popularity |
| Disclosure | 21 June 2026, zero-day drop without vendor coordination as part of the exploitarium release covering ~130 issues |
| Reporter | Anonymous researcher pseudonym bikini via the exploitarium GitHub repository |
noisgate verdict.
The single decisive factor is role multiplier: Gitea is a source-of-truth code repository, and an unauthenticated one-header admin takeover on a source-control system is a canonical supply-chain pivot. Active exploitation of a trivial pre-auth chain against a widely-deployed configuration removes any argument for a lower bucket.
Why this verdict
- Pre-auth, one-request, network-reachable: no credentials, no user interaction, no chaining. The friction audit finds essentially zero attacker prerequisites beyond HTTP reachability.
- Role multiplier — source control is supply-chain-tier: Gitea holds source, signing config, CI secrets, and Actions workflows. Admin takeover = code theft + backdoored commits + runner RCE. Floor is CRITICAL by the noisgate role-multiplier rule (supply-chain pivot on a canonical high-value role component).
- Active exploitation + zero-day drop: the disclosure path was a public PoC dump, not coordinated disclosure. The window between patch availability and mass scanning is measured in days.
- Population is non-trivial: the vulnerable configuration was the *default* in official Docker images. Any team that stood up Gitea via
docker runand then enabled reverse-proxy SSO (a common pattern behind Traefik/NGINX + Authelia) is exposed. - Downward adjustment applied but bounded: bare-metal installs without reverse-proxy auth are not vulnerable. That trims population, not blast radius — so the verdict sits at CRITICAL rather than being pushed to a 9.8 ceiling.
Why not higher?
The bug is not universal to every Gitea install — reverse-proxy header auth must be enabled, and hardened deployments that pin REVERSE_PROXY_TRUSTED_PROXIES to a specific CIDR are unaffected. That population narrowing is the reason the score sits at 9.4 rather than a flat 9.8/10.
Why not lower?
Downgrading to HIGH would require either an authentication prerequisite or a role narrowing — neither exists. Gitea is source control; unauthenticated admin takeover on source control is definitionally a supply-chain-tier compromise, and the noisgate role-multiplier floor prevents dropping below CRITICAL when the canonical deployment role is high-value and exploitation is trivial.
What to do — in priority order.
- Pin
REVERSE_PROXY_TRUSTED_PROXIESimmediately — Editapp.ini(or the Docker env var) from*to your actual reverse-proxy IPs or127.0.0.1,::1. This is the single-line fix and it neutralizes the exploit even before you upgrade. Deploy within the noisgate mitigation SLA of 3 days. - Disable reverse-proxy header auth if you don't use it — Set
ENABLE_REVERSE_PROXY_AUTHENTICATION = falseinapp.ini. Most teams enabled this speculatively and never wired it up — turning it off costs nothing and kills the class. Do this within 3 days. - Strip
X-WEBAUTH-USERat the reverse proxy — Add an NGINXproxy_set_header X-WEBAUTH-USER "";or Traefikheaders.customRequestHeadersrule to unconditionally clear the header on inbound traffic — belt-and-braces defense that survives future config drift. Deploy within 3 days. - Upgrade to Gitea 1.26.3+ — This is the durable fix — trusted-proxy defaults are corrected and header auth is opt-in. Apply within the noisgate remediation SLA of 90 days, but given active exploitation aim for the current maintenance window (this week).
- Rotate all admin PATs and SSH keys — Assume compromise until proven otherwise. Force-expire personal access tokens (
/-/admin/applications), rotate SSH deploy keys, and rebuild any Actions runners that may have executed attacker workflows. Complete within 7 days. - Hunt in audit logs for admin logins with
X-WEBAUTH-USER— Grep NGINX/Traefik access logs for requests bearing the header from non-proxy source IPs; cross-reference against Gitea audit log admin sessions from unfamiliar IPs. Any hit is a confirmed breach.
- MFA on Gitea accounts — the exploit never touches the login flow, MFA is bypassed by design
- Strong admin passwords / rotating them — no password is validated in the header-auth path
- WAF rules blocking SQLi/XSS patterns — the request is a normal GET with a benign header; nothing looks malicious to a signature-based WAF unless you write a header-specific rule
- IP allow-listing on the Gitea container — the reverse proxy IS in the allow-list, and the header trust is what makes the source IP irrelevant
Crowdsourced verification payload.
Run this from an auditor workstation with network reach to the Gitea instance — no credentials required, this is a black-box check. Invoke as ./check-cve-2026-20896.sh https://gitea.internal.example.com. If you want a config-side check instead, SSH to the host and inspect app.ini directly (script prints the exact grep at the end).
#!/usr/bin/env bash
# noisgate verification for CVE-2026-20896 — Gitea reverse-proxy auth bypass
# Usage: ./check-cve-2026-20896.sh https://gitea.example.com
set -u
TARGET="${1:-}"
if [[ -z "$TARGET" ]]; then
echo "Usage: $0 <gitea-base-url>" >&2
exit 2
fi
# 1. Fingerprint Gitea version
VERSION_JSON=$(curl -sk --max-time 10 "$TARGET/api/v1/version" || true)
VERSION=$(echo "$VERSION_JSON" | grep -oE '"version":"[^"]+"' | cut -d'"' -f4)
if [[ -z "$VERSION" ]]; then
echo "UNKNOWN — could not reach $TARGET/api/v1/version"
exit 3
fi
echo "[i] Detected Gitea version: $VERSION"
# 2. Version gate — anything >= 1.26.3 is patched
major=$(echo "$VERSION" | cut -d. -f1)
minor=$(echo "$VERSION" | cut -d. -f2)
patch=$(echo "$VERSION" | cut -d. -f3 | grep -oE '^[0-9]+')
if (( major > 1 )) || (( major == 1 && minor > 26 )) || (( major == 1 && minor == 26 && patch >= 3 )); then
echo "PATCHED — Gitea $VERSION is at or above 1.26.3"
exit 0
fi
# 3. Behavioural probe — send X-WEBAUTH-USER and check for auth
PROBE=$(curl -sk --max-time 10 -o /dev/null -w '%{http_code}|%{header_json}' \
-H 'X-WEBAUTH-USER: __noisgate_probe_user__' \
"$TARGET/user/settings")
HTTP_CODE=$(echo "$PROBE" | cut -d'|' -f1)
# A vulnerable instance with header auth enabled will 302 to a create-user
# or return 200 for a session; a hardened instance returns 302 to /user/login.
if [[ "$HTTP_CODE" == "200" ]] || [[ "$HTTP_CODE" == "302" && "$PROBE" != *"/user/login"* ]]; then
echo "VULNERABLE — $VERSION < 1.26.3 AND header auth appears trusted (HTTP $HTTP_CODE)"
echo " Config-side check on the host:"
echo " grep -E 'ENABLE_REVERSE_PROXY_AUTHENTICATION|REVERSE_PROXY_TRUSTED_PROXIES' /etc/gitea/app.ini"
exit 1
fi
echo "VULNERABLE (version) — $VERSION < 1.26.3; header-auth probe was inconclusive (HTTP $HTTP_CODE), verify app.ini manually"
exit 1
If you remember one thing.
REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.1,::1 or disable reverse-proxy auth entirely) — that's the noisgate mitigation SLA of ≤ 3 days, tightened to *hours* because active exploitation is reported. Then schedule the upgrade to Gitea 1.26.3+ inside this week's maintenance window; the noisgate remediation SLA is ≤ 90 days for a CRITICAL, but with a public one-header PoC and no-notification zero-day drop, treating 90 days as the ceiling is malpractice — aim for 7. Rotate all admin PATs and SSH keys, and grep six months of reverse-proxy access logs for X-WEBAUTH-USER from non-proxy source IPs before you close the ticket.Sources
- Feedly CVE-2026-20896 entry
- IT-Connect — Exploitarium researcher exposes zero-day flaws in 15 open source projects
- The Register — Anonymous researcher drops 0-day 'exploitarium' repo
- SC Media — Anonymous researcher dumps zero-day exploits for multiple software products
- byteiota — Exploitarium: 130 0-Days Dropped—Two Are Critical Now
- Cybersec Brief — Daily Cybersecurity Briefing (30 June 2026)
- Gitea documentation — Reverse Proxy authentication
- CISA Vulnerability Summary Week of June 8, 2026
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.