← Back to Feed CACHED · 2026-07-01 01:39:09 · CACHE_KEY CVE-2026-20896
CVE-2026-20896 · CWE-290 · Disclosed 2026-06-21

Gitea Docker reverse-proxy authentication bypass via trusted-proxy wildcard default

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

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.

"One HTTP header equals admin on any Docker-deployed Gitea with reverse-proxy auth. Active exploitation. Patch this week."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Locate an exposed Gitea instance

Attacker fingerprints Gitea via /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.
Conditions required:
  • HTTP/HTTPS reachability to the Gitea web port (3000 or 443)
Where this breaks in practice:
  • 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
Detection/coverage: Shodan product:Gitea and Censys services.software.product:gitea enumerate the population; GreyNoise is beginning to tag mass scans post-exploitarium drop
STEP 02

Confirm reverse-proxy header auth is enabled

Attacker sends a benign request with 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.
Conditions required:
  • ENABLE_REVERSE_PROXY_AUTHENTICATION = true in app.ini
  • Instance built from the vulnerable official Docker image where REVERSE_PROXY_TRUSTED_PROXIES = *
Where this breaks in practice:
  • 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
Detection/coverage: Any 200/302 to a request bearing X-WEBAUTH-USER from a non-proxy IP is the tell — trivial to alert on in NGINX/Traefik logs
STEP 03

Impersonate the admin account

Attacker re-issues the request with 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.
Conditions required:
  • Knowledge or guess of an admin username — enumerable via /api/v1/users/search
Where this breaks in practice:
  • None once step 2 succeeds — this is the whole exploit
Detection/coverage: Post-exploit, admin login from a source IP that has never previously authenticated; correlate with audit log timestamps
STEP 04

Establish persistence and pivot

Attacker mints a personal access token (/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.
Conditions required:
  • Successful admin session from step 3
Where this breaks in practice:
  • 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
Detection/coverage: New admin PAT creation, new SSH key on admin account, and outbound clones to unfamiliar IPs are all first-class audit events
STEP 05

Supply-chain payload

Attacker pushes a malicious tag to a widely-consumed internal library, poisons a release artifact, or modifies a webhook to redirect build notifications to attacker infrastructure. If Gitea Actions runners exist, arbitrary code execution on runners is trivial via a crafted workflow.
Conditions required:
  • Downstream consumers pull from the compromised repos on main/latest without pinning
Where this breaks in practice:
  • Reproducible builds and artifact signing verification catch this — but almost no one enforces it
Detection/coverage: Delta scanning of tags/releases; SLSA provenance mismatches; unexpected workflow modifications
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusActive exploitation reported as of 30 June 2026 per Daily Security Review; not yet CISA KEV as of 1 July 2026
Proof-of-conceptPublic 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
EPSSNot yet scored (published <14 days ago); expected to climb into the >90th percentile given trivial PoC + active exploitation
KEV statusNot currently listed; strong candidate for near-term addition given exploitation reports
CVSSNo vendor/CNA-assigned score; community estimate CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H9.8
Affected versionsAll Gitea Docker images prior to 1.26.3 where ENABLE_REVERSE_PROXY_AUTHENTICATION=true and REVERSE_PROXY_TRUSTED_PROXIES=* (the shipped default)
Fixed versionGitea 1.26.3 (and 1.26.4 rollup); reverse-proxy auth is now opt-in and trusted-proxies defaults to loopback only
Exposure dataShodan reports ~35–45k internet-reachable Gitea instances (product:Gitea); Docker-deployed share estimated majority given official image popularity
Disclosure21 June 2026, zero-day drop without vendor coordination as part of the exploitarium release covering ~130 issues
ReporterAnonymous researcher pseudonym bikini via the exploitarium GitHub repository
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to CRITICAL (9.4/10)

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.

HIGH Root cause and exploit mechanics (public PoC, vendor patch analysis)
HIGH Patched version and fix commit
MEDIUM Precise exposure population — depends on how many Docker deployers enabled reverse-proxy auth
MEDIUM Scale of active exploitation — reported but not yet quantified by GreyNoise/Shodan telemetry

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 run and 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.

05 · Compensating Control

What to do — in priority order.

  1. Pin REVERSE_PROXY_TRUSTED_PROXIES immediately — Edit app.ini (or the Docker env var) from * to your actual reverse-proxy IPs or 127.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.
  2. Disable reverse-proxy header auth if you don't use it — Set ENABLE_REVERSE_PROXY_AUTHENTICATION = false in app.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.
  3. Strip X-WEBAUTH-USER at the reverse proxy — Add an NGINX proxy_set_header X-WEBAUTH-USER ""; or Traefik headers.customRequestHeaders rule to unconditionally clear the header on inbound traffic — belt-and-braces defense that survives future config drift. Deploy within 3 days.
  4. 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).
  5. 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.
  6. 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.
What doesn't work
  • 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
06 · Verification

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).

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/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
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: assume every Docker-deployed Gitea in your fleet is exploitable until proven otherwise. Push a config change today (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

  1. Feedly CVE-2026-20896 entry
  2. IT-Connect — Exploitarium researcher exposes zero-day flaws in 15 open source projects
  3. The Register — Anonymous researcher drops 0-day 'exploitarium' repo
  4. SC Media — Anonymous researcher dumps zero-day exploits for multiple software products
  5. byteiota — Exploitarium: 130 0-Days Dropped—Two Are Critical Now
  6. Cybersec Brief — Daily Cybersecurity Briefing (30 June 2026)
  7. Gitea documentation — Reverse Proxy authentication
  8. CISA Vulnerability Summary Week of June 8, 2026
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.