Hello,
I am writing to report 4 memory safety vulnerabilities in GNU Wget
1.25.0, all confirmed present at master HEAD on GNU Savannah (2026-06-20).
These cover the Metalink URL parser, HTTP Content-Range header handling,
iconv filename conversion, and HTML link conversion.
Summary of findings:
1. Heap buffer underread in clean_metalink_string() -- crash (CVSS 7.5)
2. Signed integer overflow in parse_content_range() -- UB/desync (CVSS 5.3)
3. Heap buffer overflow in convert_fname() iconv E2BIG handler (CVSS 6.1)
4. Integer overflow in html_quote_string() size counter (CVSS 5.9)
Findings #1 and #2 are triggered by a malicious server without user
interaction beyond initiating the download. Finding #3 requires
--remote-encoding or IRI mode. Finding #4 requires recursive download
with --convert-links and a very large HTML attribute.
All 4 have been lab-verified with AddressSanitizer and/or
UndefinedBehaviorSanitizer. Individual details are attached.
I would appreciate an acknowledgment of receipt and CVE assignment for
each confirmed finding.
Thank you and please let me know if you need anything else.
Regards,
Tristan
GNU Wget 1.25.0 -- Heap Buffer Overflow in convert_fname() iconv E2BIG Handler
================================================================================
CVSS 3.1: 6.1 (AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:H)
CWE: CWE-122 (Heap-based Buffer Overflow)
Auth: None (requires --remote-encoding or IRI with non-UTF-8 content)
Version: 1.25.0, master HEAD (2026-06-20)
File: src/url.c, convert_fname(), lines 1624-1663
Root Cause:
The E2BIG error handler incorrectly calculates available output space after
reallocation. It sets outlen to the total new buffer size instead of the
remaining space from the write pointer. This tells iconv that `done` more
bytes are available than actually exist.
else if (errno == E2BIG)
{
done = len; // old total buffer size
len = outlen = done + inlen * 2; // BUG: outlen = total, not
remaining
converted_fname = xrealloc(converted_fname, outlen + 1);
s = converted_fname + done; // write pointer at old end
}
After this: actual space from s to buffer end = inlen*2 bytes.
But outlen claims: done + inlen*2 bytes. Overclaim = done bytes.
iconv writes past the buffer by up to `done` bytes.
Data Flow:
Server provides URL with characters expanding >2x under iconv
(e.g., ISO-8859-1 to WCHAR_T: 1 byte -> 4 bytes)
-> convert_fname() initial buffer (inlen*2) insufficient
-> iconv returns E2BIG
-> Buggy realloc: outlen overclaims by `done` bytes
-> Next iconv writes past buffer end
Lab Verification:
AddressSanitizer: heap-buffer-overflow WRITE of size 20 at 0 bytes after
31-byte region. Triggered with ISO-8859-1 to WCHAR_T conversion (4x
expansion ratio forces E2BIG on small input).
GNU Wget 1.25.0 -- Integer Overflow in html_quote_string() Leading to Heap
Overflow
======================================================================================
CVSS 3.1: 5.9 (AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:H)
CWE: CWE-190 (Integer Overflow) leading to CWE-122 (Heap Buffer Overflow)
Auth: None (requires recursive download with --convert-links)
Version: 1.25.0, master HEAD (2026-06-20)
File: src/convert.c, html_quote_string(), lines 1177-1235
Root Cause:
The output buffer size counter is declared as `int` (32-bit signed). Each
special character adds 4-6 to the counter. With ~358 million special
characters (e.g., `"` adds 6 each), the counter overflows INT_MAX, wrapping
to a small or negative value. xmalloc then allocates a tiny buffer, and the
expansion loop writes the full output past it.
int i; // 32-bit signed counter
for (i = 0; *s; s++, i++) {
if (*s == '&') i += 4; // 5 total per '&'
else if (*s == '"') i += 5; // 6 total per '"'
else if (*s == ' ') i += 4; // 5 total per ' '
...
}
res = xmalloc(i + 1); // undersized after overflow
Data Flow:
Malicious server returns HTML with ~358MB URL attribute filled with '"'
-> wget downloads in recursive mode (-r)
-> --convert-links triggers html_quote_string() on URLs not downloaded
-> int counter wraps: 357,913,942 * 6 = 2,147,483,652 -> -2,147,483,644
-> xmalloc(-2,147,483,643) interprets as huge size_t -> xalloc_die crash
-> OR on 32-bit: allocates tiny buffer -> ~2GB heap overflow
Lab Verification:
Arithmetic confirmed: 357,913,942 '"' chars produce true count 2,147,483,652,
which wraps to -2,147,483,644 as int. Expansion would write 2.15GB into a
tiny allocation.
GNU Wget 1.25.0 -- Signed Integer Overflow in parse_content_range()
=====================================================================
CVSS 3.1: 5.3 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L)
CWE: CWE-190 (Integer Overflow or Wraparound)
Auth: None (malicious server response)
Version: 1.25.0, master HEAD (2026-06-20)
File: src/http.c, parse_content_range(), lines 913-960
Root Cause:
Manual digit-by-digit accumulation of Content-Range header values has no
overflow check. Since `num` is wgint (int64_t), values exceeding INT64_MAX
cause signed integer overflow (undefined behavior). Compare with
Content-Length parsing which properly uses str_to_wgint/strtoll + ERANGE.
for (num = 0; c_isdigit(*hdr); hdr++)
num = 10 * num + (*hdr - '0'); // no overflow check
Data Flow:
Malicious server sends: Content-Range: bytes 9223372036854775810-...
-> parse_content_range() overflows int64_t
-> contlen = last_byte - first_byte + 1 produces arbitrary int64
-> fd_read_body uses corrupted contlen for download size
-> On keep-alive: incorrect content draining -> protocol desync
Lab Verification:
UBSan: "signed integer overflow: 999999999999999999 * 10 cannot be
represented in type 'long int'" (3 instances). Resulting first_byte
becomes -9223372036854775806.
GNU Wget 1.25.0 -- Heap Buffer Underread in clean_metalink_string()
=====================================================================
CVSS 3.1: 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
CWE: CWE-125 (Out-of-bounds Read)
Auth: None (malicious server response)
Version: 1.25.0, master HEAD (2026-06-20)
File: src/metalink.c, clean_metalink_string(), lines 1040-1073
Root Cause:
The backward-scanning loop that strips trailing whitespace has no lower
bound check. When the input string consists entirely of whitespace, the
forward scan advances `beg` past all content to the NUL terminator, then
the backward scan reads bytes before `beg` (before the heap allocation).
beg = *str;
while ((c = *beg) && (c == '\n' || c == '\r' || c == '\t' || c == ' '))
beg++; // all whitespace: beg points to NUL
end = beg;
while ((c = *end) && (c != '\r') && (c != '\n'))
end++; // exits immediately (NUL)
if (*end == '\0')
while ((c = *(end - 1)) && (c == '\n' || c == '\r' || c == '\t' || c == '
'))
end--; // BUG: no lower bound, reads before allocation
len = end - beg; // wraps to huge size_t (beg > end)
new = xmemdup0(beg, len); // xalloc_die() or massive memcpy
Data Flow:
Malicious server serves Metalink XML with all-whitespace <url> </url>
-> retrieve_from_metalink() calls clean_metalink_string(&mres->url)
-> backward scan reads before heap buffer
-> wrapped len causes xalloc_die() (crash)
Lab Verification:
AddressSanitizer: heap-buffer-overflow READ of size 1, 1 byte before
4-byte region. Confirmed crash on strdup(" ") input.