Send one HTTP header. Get full admin access to a self-hosted Git server with all its repositories, SSH keys, CI/CD pipelines, and secrets. No password. No session token. Just X-WEBAUTH-USER: admin.

That’s CVE-2026-20896, the 9.8 CRITICAL vulnerability at the center of Gitea’s June 2026 security release window. But it wasn’t alone — versions 1.26.3 and 1.26.4 addressed a cluster of security issues across authentication, SSRF, access control, privilege escalation, and information disclosure.

TL;DR

  • Gitea Docker images shipped with REVERSE_PROXY_TRUSTED_PROXIES = * — any IP could impersonate any user via a single header
  • Gitea published multiple advisories around 1.26.3 and 1.26.4, ranging from CRITICAL (9.8) to medium severity
  • Gitea 1.26.3 carried an upstream regression, so the practical target is 1.26.4 or later
  • Fix: update to 1.26.4+, restrict REVERSE_PROXY_TRUSTED_PROXIES to your actual proxy IPs
  • Prioritize internet-facing instances first, then verify whether reverse proxy authentication is enabled

Why This Matters

Self-hosted Git platforms are not just code storage — they’re the nervous system of software development. They hold source code, deployment keys, CI/CD credentials, webhook secrets, and container images. A compromised Gitea instance typically means access to the systems a development team builds and deploys.

Gitea is used by everyone from solo developers to organizations that prefer self-hosted source control over cloud providers like GitHub or GitLab.


The Vulnerability Landscape

IdentifierTypeCVSSSummary
CVE-2026-20896Auth bypass9.8Any IP can impersonate any user via X-WEBAUTH-USER
CVE-2026-22874SSRF9.6Webhooks and migration filters miss reserved/internal ranges
CVE-2026-20779TOTP replay7.1TOCTOU race enables OTP reuse across multiple surfaces
CVE-2026-28740Access control7.1Non-Code access can authorize private LFS object reuse
CVE-2026-27775Privilege escalationHighBranch maintainers escalate to full repo write
CVE-2026-24451Info disclosureHighFork sync continues after parent goes private
CVE-2026-27761Token scope bypass4.3Tokens without repo scope read private commit data via RSS
CVE-2026-25038Info disclosureMediumPrivate org labels visible to non-members
GHSA-rjvx-x5h2-6px5API authorization bypassHighOrg members bypass repository creation restrictions through the fork API

We’ll dig into the five most impactful ones in detail.


CVE-2026-20896: The Auth Bypass That Needed One Header

CVSS 9.8 — Critical

This is the one that should wake you up at night if you’re running a Gitea Docker deployment.

Gitea supports reverse proxy authentication — a feature that lets a trusted upstream proxy (like nginx or Traefik) pass the authenticated username via an HTTP header, so users don’t have to log in twice. This is a legitimate and common pattern.

The problem was in the default configuration. Gitea’s Docker images shipped with:

[security]
REVERSE_PROXY_TRUSTED_PROXIES = *

The wildcard * means Gitea trusts every IP address as a legitimate proxy. Combined with any configuration where reverse proxy auth is active, this meant anyone on the internet could add one header to their request:

GET /user/settings HTTP/1.1
Host: git.example.com
X-WEBAUTH-USER: admin

Gitea would look at that header, check if the source IP is a trusted proxy (yes — everything is trusted), and log the request in as admin. No password. No 2FA. No session cookie.

Attack chain

Attacker → sends HTTP request with X-WEBAUTH-USER: admin
→ Gitea checks REVERSE_PROXY_TRUSTED_PROXIES
→ * matches any IP → trust the header
→ Gitea authenticates request as admin
→ Attacker has full admin access

From there an attacker can: read all private repositories, inject SSH keys for persistent access, modify webhooks to exfiltrate future code pushes, steal CI/CD secrets, or pivot through the server via webhook SSRF (see next section).

Scale of exposure

Not every internet-facing Gitea instance is exploitable: reverse proxy authentication has to be enabled for the header impersonation path to work. But exposed instances should still be prioritized first, because the dangerous default made a single configuration change enough to turn a reachable service into an authentication bypass.

Fix

The fix in 1.26.3 makes reverse proxy authentication explicitly opt-in. It no longer activates based on the trusted proxy list alone — an administrator must enable it deliberately.

If you’re running Gitea today, verify your configuration:

Terminal window
grep -i "REVERSE_PROXY" /path/to/gitea/conf/app.ini

If you see REVERSE_PROXY_TRUSTED_PROXIES = *, restrict it to your actual proxy IP or subnet:

[security]
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.1,172.18.0.1

Replace 172.18.0.1 with your Docker bridge gateway or reverse proxy IP. Then restart Gitea.


CVE-2026-22874: SSRF via Webhooks

CVSS 9.6 — Critical

Gitea’s webhook feature lets repositories automatically call external URLs on events like push, pull request, or release. It’s designed for integrations — trigger a CI build, post to Slack, update a status page.

The SSRF protection is supposed to block webhooks from targeting internal addresses. The problem: Gitea’s filter missed several address classes that should not be reachable from webhook or migration requests, including RFC 6598 Carrier-Grade NAT addresses (100.64.0.0/10) and reserved ranges that can matter in cloud environments. One practical target class is instance metadata services:

http://169.254.169.254/latest/meta-data/iam/security-credentials/

Gitea would execute that request from the server’s local context. Because webhook delivery logs show the full HTTP response body, the attacker could read the response — including IAM role credentials — through the Gitea UI.

What an attacker can steal

On AWS instances where IMDSv1 is still allowed, this can return something like:

{
"Code": "Success",
"Type": "AWS-HMAC",
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"Token": "...",
"Expiration": "2026-06-27T12:00:00Z"
}

Those credentials can then be used to enumerate and access S3 buckets, EC2 instances, RDS databases — anything the instance role has access to.

Fix

Update to 1.26.4 or later for the patched SSRF filter and the 1.26.3 regression fix. At the infrastructure level:

  • AWS: Enforce IMDSv2 with HttpTokens=required and HttpPutResponseHopLimit=1
  • Egress filtering: Block 169.254.169.254 at the network level for all Gitea containers
  • Webhook allowlisting: If possible, maintain an explicit allowlist of permitted webhook destinations

For a deeper dive on SSRF mechanics, see our SSRF Complete Guide.


CVE-2026-20779: TOTP Replay Attack

High severity

Two-factor authentication via TOTP (Time-based One-Time Password) is designed around the assumption that each code is valid only once. Gitea’s implementation had a TOCTOU (Time-of-Check to Time-of-Use) race condition that broke this guarantee.

The flaw affected three surfaces simultaneously:

  • Web login
  • Password reset flow
  • Basic-Auth with X-Gitea-OTP header

A valid TOTP code could be used multiple times within its validity window across these different endpoints. In practice this means: if an attacker intercepts a valid OTP (via phishing or man-in-the-middle), they have a wider window to replay it than intended — and the same code may work across different authentication surfaces.

Fix

1.26.3 enforces single-use TOTP codes across all three surfaces. Once a code is consumed on any endpoint, it cannot be reused.


CVE-2026-27775: Branch Permission Escalation

High severity

Gitea’s branch protection allows per-branch maintainers — users with write access to a specific branch but not the whole repository. This is a useful way to delegate branch management without granting full repository access.

The vulnerability: when a push contained multiple refs (branches or tags), Gitea cached the write permission check from the first ref and applied it to all subsequent refs. A per-branch maintainer could craft a push operation with their permitted branch first, then append refs they had no access to — and Gitea would grant write access to all of them.

This is a privilege escalation within a repository, but in organizations where branch protection enforces deployment gates or compliance controls, the impact is significant.


GHSA-rjvx-x5h2-6px5: API Fork Authorization Bypass

High severity

This one is less flashy than a one-header admin bypass, but it is the kind of bug that turns read-only organizational access into a supply-chain problem.

Gitea’s web UI enforced the CanCreateOrgRepo permission before allowing an organization member to fork a repository into the organization. The API fork endpoint did not apply the same authorization check.

In the advisory’s attack chain, an organization member without repository creation rights could fork an existing organization repository back into the same organization through the API. Because the attacker becomes administrator of the newly created fork, they can enable Actions and push a workflow under the organization’s namespace.

That matters because organization-level CI/CD secrets are commonly available to workflows running under organization-owned repositories. In a realistic environment, those secrets can include deploy keys, cloud credentials, registry tokens, or broad personal access tokens.

The operational takeaway is simple: do not treat read-only organization membership as harmless on a vulnerable instance. Review unexpected forks, Actions workflow creation, and workflow runs from organization-owned repositories created by low-privilege users.


Forgejo Impact

Forgejo shares ancestry with Gitea, but it is maintained as an independent hard fork. That means Gitea advisories should be treated as a reason to check Forgejo, not as proof that the same issue affects the same Forgejo versions.

If you run Forgejo, check its current release notes and security advisories separately, and still audit reverse proxy authentication settings. A wildcard trusted-proxy setting is dangerous wherever reverse proxy authentication can consume identity headers from untrusted clients.


What You Can Do Today

Immediate (do these now)

1. Update Gitea to 1.26.4 or later. Gitea 1.26.3 included the security fixes but also shipped with a regression affecting repository code pages, so do not stop there.

2. Audit your reverse proxy config:

Terminal window
grep -i "REVERSE_PROXY" /path/to/app.ini

Change * to your actual proxy IP.

3. Check if reverse proxy auth is enabled:

Terminal window
grep -i "ENABLE_REVERSE_PROXY_AUTHENTICATION" /path/to/app.ini

If this key doesn’t exist, the feature is off — but fix the trusted proxies setting anyway before it’s accidentally enabled.

4. Enforce IMDSv2 if running on AWS:

Terminal window
aws ec2 modify-instance-metadata-options \
--instance-id <your-instance-id> \
--http-tokens required \
--http-put-response-hop-limit 1

Short term

5. Audit webhooks for internal destinations — check for 127.0.0.1, 10., 172.16-31., 192.168., 169.254., 100.64. addresses.

6. Review per-branch maintainer assignments — anyone with branch-scoped access could have escalated before the patch.

7. Rotate TOTP seeds for high-privilege accounts if you suspect the window was exploited.

8. Review unexpected forks and Actions workflow runs in organizations, especially repositories created by users who should not have repository creation rights.

Detection

Look for these indicators in your access logs:

Terminal window
# X-WEBAUTH-USER header from unexpected IPs
grep "X-WEBAUTH-USER" /var/log/nginx/access.log
# Webhook deliveries to internal ranges
# Check Gitea admin → webhooks → recent deliveries
# Filter for 169.254.x.x, 10.x.x.x, 172.x.x.x destinations
# Unusual admin actions from unexpected IPs
grep "admin" /path/to/gitea/log/gitea.log | grep -v "your.proxy.ip"

The Bigger Pattern

Looking at this advisory cluster, a pattern emerges: Gitea’s security model has accumulated assumptions that were reasonable individually but dangerous in combination. Trusted proxy wildcard defaults. TOTP codes checked but not consumed. Permission results cached across refs. Web and API authorization checks drifting apart.

None of these feel like careless mistakes — they feel like the natural accumulation of feature additions over time, where security properties weren’t re-verified holistically. The lesson for anyone running self-hosted infrastructure: your threat surface grows with every feature you enable, and defaults that seemed safe at deployment may not stay safe as the platform evolves.

Self-hosted Git is worth running. But it requires the same patch discipline you’d apply to any internet-facing service.



Sources