This post is a tribute to the hundreds of bugs that never quite were serious, and the emotional roller coaster ride on which they take researchers.
Some brief background. The skill in finding serious bugs these days isn't in being a demon code auditor or a furious fuzzer; there are thousands of these. The skill lies instead in finding a piece of software, or a piece of functionality, that has the curious mix of being important yet not having seen much scrutiny.
Onto today's almost-bug: an interesting integer condition in the ZIP parser of Sun's JDK. The ZIP parser is a critical piece of code because not only is it used to parse JARs, but also server-side Java applications will often parse untrusted ZIPs. (Such direct server-side attacks, along the lines of my JDK ICC parser vulnerabilities last year, are nasty, and starting to recently become in-vogue for Python, Ruby and Perl too). The affected API is
java.util.zip.ZipFile
. Best I know,
java.util.zip.ZipInputStream
is not backed by the same native code, and thereby unaffected.
The interesting code, in zip_util.c follows:
/* Following are unsigned 32-bit */
jlong endpos, cenpos, cenlen;
...
/* Get position and length of central directory */
cenlen = ENDSIZ(endbuf);
if (cenlen > endpos)
ZIP_FORMAT_ERROR("invalid END header (bad central directory size)");
cenpos = endpos - cenlen;
jlong
is a signed 64-bit type. The
ENDSIZ
macro, because of the way it is formulated, returned a signed int. Therefore, the assignment to
cenlen
triggers sign extension. This means that
cenlen
can end up being negative, rather than the stated intent of being treated as an unsigned 32-bit quantity. The negative value will of course bypass the security check and lead to subsequent undesirable state. (Note that the best fix is not to enhance the check, but to add a cast to unsigned int to the underlying macro as it is used in multiple places).
So why does this appear to be just a bug and not a security vulnerability? Well, on systems without
mmap()
, a huge allocation will either cleanly fail, or a
read()
attempt past EOF will cleanly fail. On systems with
mmap()
, things are more interesting. A 32-bit build will attempt a 2Gb large mapping on a potentially much smaller file. This could lead to interesting SIGBUS conditions as a server DoS. By quite some luck, the Sun JVM process seems to spray mappings liberally through the address space, leaving no room for a contiguous 2Gb mapping.
The same sign-extension bug exists in other parts of the ZIP handling, and leads to some interesting negative values getting to some interesting places. But lower-level sanity checks save the day in the cases that I could find.
A zip file capable of triggering the interesting log line "mmap failed for CEN and END part of zip file" is available at
http://scary.beasts.org/misc/mmap_fail.zip.
Ah well, maybe next time. Come to thing of it, my pipeline does include real JDK vulns. Watch this space.