Libtiff and Libjpeg segfault them all!!

7 minute read Published:

Libtiff and Libjpeg segfault them all!!

Blog Post by Aladdin Mubaied

As a security enthusiast, one of the things I really enjoy about code breaking is finding subtle bugs in modern softwares. As someone could imagine, searching for those bugs is a hectic and daunting task. For example, you could spend hours and maybe days manually reviewing kernel code for the sake of finding just one null pointer dereference. Although this manual technique takes a lot of time, it’s no doubt very essential when hunting for complex logic flaws that static and dynamic scanners can’t detect. The other approach that most hackers prefer is finding bugs through a technique called “fuzzing”!

What is Fuzzing?

Fuzzing is a technique used by mostly modern hackers in order to find coding errors and security loopholes in softwares easily and effectively with little overhead. The most notable tool known for hackers these days is AFL (American fuzzy lop) created by Michal Zalewski. The fuzzing approach makes finding bugs an easy process compared to the manual code review which may take a while and requires an extensive thorough look at the code paths and functions in which you can easily get distracted.

Modern fuzzers such as AFL find bugs because it uses not just bits flipping but the compile-time instrumentation and genetic algorithms that make the fuzzer covers many corner cases that normal human eye can’t usually detect. Generally speaking, in fuzzing, we are more interested in finding inputs that cross a trust boundary in the software, those inputs usually the software wasn’t originally designed to handle them which can cause unexpected behavior that could lead to crashes and memory corruption flaws.

What target to fuzz

To start a fuzzing session in AFL you basically need a target written in C/C++ language. if you search github.com you end up with hundreds and thousands of projects and tools to choose from but you can’t just randomly choose from that list, thus we need criteria to pick our target?

Since we want to make an impact we need a target that is currently used in many modern softwares. For example, you can target Linux binaries, kernel, image processing tools, text processing and others. For me, I decided to pick two different libraries for my target, Libtiff, and Libjpeg, notably those libraries are being used by hundreds of softwares in the wide these days. Both libraries were built using C language.

Fuzzing the target

In this post I’m not going to cover the details of installing and running AFL assuming that has already been done.

Since we have picked up our targets, now what we need to do is building both libraries (Libjpeg and Libtiff) from source with afl-gcc compiler to create what we call a binary with AFL instrumentations. The following will configure Libjpeg with AFL compiler:

 CC=/usr/bin/afl-gcc ./configure --prefix=/opt/libjpeg

After compiling the binaries with AFL, you can start creating a fuzzing session. Here you have two options, either you pick the binaries shipped with Libjpeg/Libtiff or build your own binary based on those libraries. For me, i decided to use the binaries shipped already with Libjpeg/Libtiff. To start an ALF session you can invoke the following command:

afl-fuzz -i input_directory -o output_directory /usr/bin/your_binary

A few hours after I started my AFL sessions, I noticed a couple of unique crashes from AFL. I started digging to figure out if they are valid. After spending a decent amount of time, I found that both libraries were vulnerable to a two known memory corruption flaws, buffer overflow and null pointer dereference.

Libjpeg

CVE-2016-3616 is the first bug i reported to RedHat security team. This bug is a NULL pointer dereference in Libjpeg, although NULL pointer dereferences are typically not exploitable for arbitrary code execution, it can cause a crash or denial of service. the bug lives in the cjpeg utility which was shipped with libjpeg library. The main goal of cjpeg is to compress an image to a JPEG file.

Now to give you a little insight about the bug, cjpeg implements a function called “get_text_gray_row” and what it does is basically it just reads one row of pixels from the input provided by the user. The function takes two inputs cinfo and sinfo. The function performs the compression on the input JPEG image and outputs the file to the directory specified. Here is a snapshot of the “get_text_gray_row” code:

get_text_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)  
/* This version is for reading text-format PGM files with any maxval */
{
  ppm_source_ptr source = (ppm_source_ptr) sinfo;
  FILE * infile = source->pub.input_file;
  register JSAMPROW ptr;
  register JSAMPLE *rescale = source->rescale;
  JDIMENSION col;
  int maxval = source->maxval;
  ptr = source->pub.buffer[0];
  for (col = cinfo->image_width; col > 0; col--) {
    *ptr++ = rescale[read_pbm_integer(cinfo, infile, maxval)];
  }
  return 1;
}

Feeding the tool a malicious file would cause the tool to crash. using gdb we can see that the tool crashes because it could not handle the pointer dereference:

$ gdb bin/cjpeg
$ run malicious-file
Program received signal SIGSEGV, Segmentation fault.  
$ bt
Stopped reason: SIGSEGV  
get_text_gray_row (cinfo=0x7fffffffe2b0, sinfo=<optimized out>) at rdppm.c:153  
153 *ptr++ = rescale[read_pbm_integer(cinfo, infile)];  
0x407b08 <get_text_gray_row+200>: movzx  esi,BYTE PTR [r13+rcx*1+0x0]  
$ p $r13+$rcx*1+0x0 0x658e6a: <Address 0x658e6a out of bounds>

As you can see, at 0x407b08 address, the tool is trying to access the following pointer PTR [r13+rcx*1+0x0] but it fails due to null pointer dereference.

Libtiff

CVE-2016-3186 The other bug i’m going to talk about next is an interesting buffer overflow in libtiff. Libtiff is a library for reading and writing Tagged Image File Format (TIFF) files and is widely used by many open source tools. Notably this is not the first time Libtiff was vulnerable to buffer overflow, previously multiple buffer overflows were reported in Libtiff. The library is also shipped with utility called giff2tiff and what it does is by giving it a format of GIF87 image, it will convert it to a tiff file. Providing that any malicious file can be fed to giff2tiff, fuzzing this utility is a great target. The bug i identified lives in a function called “readextension” in “gif2tiff.c:353” which simply reads a block of GIF extension.

here is a snapshot of the code:

Int readextension(void) {  
    int count;
    char buf[255];
    int status = 1;
    (void) getc(infile);
    while ((count = getc(infile)) && count <= 255)
        if (fread(buf, 1, count, infile) != (size_t) count) {
            fprintf(stderr, "short read from file %s (%s)\n",
                    filename, strerror(errno));
            status = 0;
            break;
        }
    return status;
}

At line 353, the code does not have a check on the count which will keep reading continuously and causing a crash. This bug is of a type buffer over-read.

$ bt
…(output cropped)…
6:  0x0000000000403aab in fread (__stream=<optimized out>, __n=0xffffffffffffffff, __size=0x1, __ptr=0x7fffffffe390) at /usr/include/bits/stdio2.h:290  
7:  readextension () at gif2tiff.c:353  
8:  0x0000000000407a0d in convert () at gif2tiff.c:222  
9:  0x0000000000402db9 in main (argc=<optimized out>, argv=0x7fffffffe5e8) at gif2tiff.c:178  
…(output cropped)…

Please note that the two vulnerabilities above were fixed in the upstream code, thanks to the red hat security team.

Exploitability

With modern Linux memory protections such as NX/DEP, ASLR and stack cookies, it’s not a straightforward to deliver a fully working exploit against the two libraries. However, it’s still possible to deliver a working exploit using ROP (Return-oriented programming - which I’m going to cover in my future blog posts) if those utilities were used to receive a direct input from the user with either ASLR off or ASLR on with known memory leak.

Conclusion

Finding bugs is a very interesting journey, from the point you find a crash to the point you deliver a working exploit. Since I mainly used AFL, I would say it’s undoubtedly a great tool and makes the process of identifying very complex memory corruption bugs easier for hackers and security researchers in the industry.