Friday, September 19, 2014

CoreGraphics Memory Corruption - CVE-2014-4377

Apple CoreGraphics library fails to validate the input when parsing the colorspace specification of a PDF XObject resulting in a heap overflow condition. A small heap memory allocation can be overflowed with controlled data from the input in any application linked with the affected framework. Using a crafted PDF file as an HTML image and combined with a information leakage vulnerability this issue leads to arbitrary code execution. A complete 100% reliable and portable exploit for MobileSafari on IOS7.1.x. can be downloaded from github
Quick links: White paper, PoC Exploit generator in python

Summary

  • Title: Apple CoreGraphics Memory Corruption
  • CVE Name: CVE-2014-4377
  • Permalink: http://blog.binamuse.com/2014/09/coregraphics-memory-corruption.html
  • Date published: 2014-09-18
  • Date of last update: 2014-09-19
  • Class: Client side / Integer Overflow / Memory Corruption
  • Advisory: HT6441 HT6443

Vulnerability Details

Safari accepts PDF files as native image format for the < image > html tag. Thus browsing an html page in Safari can transparently load multiple pdf files without any further user interaction. CoreGraphics is the responsible of parsing the PDF files.
Apple Core Graphics framework fails to validate the input when parsing the colorspace specification of a PDF XObject. A small heap memory allocation can be overflowed with controlled data from the input enabling arbitrary code execution in the context of Mobile Safari (A memory layout information leak is needed).
The Core Graphics framework is a C-based API that is based on the Quartz advanced drawing engine. It provides low-level, lightweight 2D rendering. This is used in a wide range of applications to handle path-based drawing, transformations, color management, offscreen rendering, patterns, gradients and shadings, image data management, image creation, masking, and PDF document creation, display, and parsing.
CoreGraphics library implements the functionality to load and save several graphic formats such as PDFs and it is used by most common applications that deal with images, including Safari, Preview, Skype, etc. The 32 bit version of the framework also implements the x_alloc heap, a set of low level procedures to manage an internal heap memory structure used when processing images of different types (including PDF files). The functions x_calloc and x_free are used very frequently to allocate and de-allocate memory fast.
The x_calloc function behaves as a normal calloc except it allocates a bit extra memory for metadata. It actually allocates an extra 2*sizeof(void*) bytes and pad the resulting size to the next 16 byte border. When the chunk is in use (allocated) this extra space is used to hold the size of the chunk, thus making it super fast to free. On the other hand when the chunk is free the metadata space is used to form a free-list linked list, the first metadata value in a free chunk points to the next free chunk. This is its pseudocode. Think x_mem_alloc0_size as a plain malloc.

void* __cdecl x_calloc(size_t nmemb, size_t size)
{
  void * buffer = x_mem_alloc0_size((nmemb * size + 31) & -16);
  *(size_t *)buffer = (nmemb * size + 31) & -16;
  return buffer + 16;
}
This function is prone to integer overflow because it doesn’t check for (nmemb * size) to be less than MAXUINT-16. Then if a programmer tries to allocate a size in the range [-16,-1] x_alloc will allocate 0(zero) or 16 bytes (instead of the immense value required) without triggering any exception.

An x_calloc bug

There is a x_calloc related bug in how PDF handles the colorspace for embedded XObjects. Forms and images of different types can be embedded in a PDF file using an XObject pdf stream. As described in the pdf specification, an XObject can specify the colorspace in which the intended image is described. The CoreGraphics library fails to validate the input when parsing the /Indexed colorspace definition of an XObject pdf stream and enables an attacker to reach x_calloc with a size in the [16,-1] range.
The indexed colorspace definition is table based and relies on a base colorspace. For example, the following definition defines an indexed colorspace of 200 colors based on a RGB base colorspace with the table defined in the indirect object 8 0 R. For more info on indexed colorspaces see section 8.6.6.3 of the pdf specification.
    /ColorSpace [/Indexed /DeviceRGB 200 8 0 R]
The following is an excerpt of the function _cg_build_colorspace. It was taken from the dyld_shared_cache_armv7 of the iPhone3,1 (iPhone4) iOS version 7.1.1. The function can be disassembled at address 0x2D59F260 considering that the dyld_shared_cache is loaded at 0x2C000000.

/* cs_index_array should represent something like this:
    [/Indexed /DeviceRGB 200 8 0 R]
*/

/* Sanity check, array must have 4 elements */
if ( CGPDFArrayGetCount(cs_index_array) != 4 ) {
    message = "invalid `Indexed' color space: incorrect number of entries in color space array.";
    goto EXIT;
}

/* Sanity check, 2nd element should be an object */
if ( !CGPDFArrayGetObject(cs_index_array, 1, &base_cs_obj) ) {
    message = "invalid `Indexed' color space: second color space array entry is not an object.";
    goto EXIT;
}

/* build the base colorspace */
base_cs = cg_build_colorspace(base_cs_obj);
if ( !base_cs ) {
    message = "invalid `Indexed' color space: invalid base color space.";
    goto EXIT;
}

/* get the 3rd element. N, the number of indexed colors in the table */
if ( CGPDFArrayGetInteger(cs_index_array, 2, &N) ) {
    message = "invalid `Indexed' color space: high value entry is not an integer.";
    goto RELEASE_EXIT;
}

/* Sanity check. N should be positive */
if ( N <= -1 ) {
    message = "invalid `Indexed' color space: high value entry is negative.";
    goto RELEASE_EXIT;
}

/* cs is the resultant colorspace, init to NULL */
cs = 0;

/* if 4th element is a pdf stream get it and do stuff ...*/
if ( CGPDFArrayGetStream(cs_index_array, 3, &lookup_stream) == 1 ) {

    lookup_buffer = CGPDFStreamCopyData(lookup_stream);
    if ( lookup_buffer ) {
        
        /* N is a fully controled _positive_ integer and the number of components
           of the base colorspace is up to 32. Thus lookup_size is almost arbitrary.*/
        lookup_size = (N + 1) * CGColorSpaceGetNumberOfComponents(base_cs);

        /* Unsigned check. This will not hold with a lookup_size big enough (e.x. -10)*/
        if ( CFDataGetLength(lookup_buffer) >= lookup_size ) {
            data = CFDataGetBytePtr(lookup_buffer);
            cs = CGColorSpaceCreateIndexed(base_cs, N_, data);
        }
        else {
            /* HERE is the interesting bit. A lookup_size in the [-16,-1] range
               will silently allocate a very small buffer */
            overflow_buffer = x_calloc_2D5143B4(1, lookup_size);
            _data = CFDataGetBytePtr(lookup_buffer);
            _size = CFDataGetLength(lookup_buffer);
            /* But memove will copy all the available data in the stream 
               OVERFLOW!! 
             */
            memmove(overflow_buffer, _data, _size);

            /* CGColorSpaceCreateIndexed is a nop when N is greater than 256 */
            cs = CGColorSpaceCreateIndexed(base_cs, N_, overflow_buffer);
            if ( overflow_buffer )
              x_free(overflow_buffer);
        }
        CFRelease(lookup_buffer);
    }

    goto RELEASEANDEXIT;
}

/* else if 4th element is a pdf string get it and do stuff ...*/
if ( CGPDFArrayGetString(cs_index_array, 3, &lookup_str) == 1 ) {
    lookup_size_ = (N + 1) * CGColorSpaceGetNumberOfComponents(base_cs);
    if ( lookup_size_ != CGPDFStringGetLength(lookup_str) ) {
        message = "invalid `Indexed' color space: invalid lookup entry.";
        goto RELEASEANDEXIT;
    }
    buffer = CGPDFStringGetBytePtr(lookup_str);
    cs = CGColorSpaceCreateIndexed(base_cs, N__, buffer);
    goto RELEASEANDEXIT;

}

/* at this point cs is NULL */
message = "invalid `Indexed' color space: lookup entry is not a stream or a string.";

RELEASE_EXIT:
    CGColorSpaceRelease(base_cs);
EXIT:
    log_2D5A0DA8(message);
/* result in cs */
CGPDFArrayGetInteger gets an object from an arbitrary position in a pdf array, starting at 0. The number of indexed colors is read from the third array position (index 2), multiplied by the number of color components of the base colorspace and then (if using pdf stream) allocated with a x_calloc. We basically control the size passed to x_calloc. If the resultant value is for instance -10, x_calloc will allocate a small amount of memory and return a valid pointer. If the inner colorspace is /DeviceRGB with 3 color components, passing a value of 0x55555555 will do the trick.
Then the memmove will potentially overflow the small buffer with an arbitrary number of arbitrary bytes. The function CGColorSpaceCreateIndexed can be considered as a no-op as it will use this buffer only if our controlled size is a positive less than 0xff, not the interesting case.

Exploitation

A 100% reliable PoC exploit can be downloaded from the github project. This exploit needs a companion information leakage vulnerability to bypass ASLR, DEP and Code signing iOS exploit mitigations. The exploit is presented as a cgi script that expects to get the dyld_shared_cache address, the shellcode address and the iOS version as GET parameters. It executes arbitrary code in the context of SafariMobile.

Demo

There is an live online demo for iPhone4 and iPhone5 with IOS7.1.2 here

Thursday, September 18, 2014

CoreGraphics Information Disclosure - CVE-2014-4378

This article explores the exploitability of MobileSafari on IOS 7.1.x. Using a crafted PDF file as an HTML image makes it possible to leak information about the memory layout to the browser Javascript interpreter. Apple CoreGraphics library fails to validate input when parsing the colorspace specification of an inline image embedded in a PDF content stream. he issue results in an information leak vulnerability that improves the adversary capability of exploit other vulnerabilities in any application linked with this library. This is also proved useful to bypass a several exploit mitigations such as ASLR, DEP and CodeSigning. A 100% reliable PoC leak-exploit can be downloaded from github
Quick links: White paper, PoC Exploit generator in python
  • Title: Apple CoreGraphics Information Disclosure
  • CVE Name: CVE-2014-4378
  • Permalink: http://blog.binamuse.com/2014/09/coregraphics-information-disclosure.html
  • Date published: 2014-09-18
  • Date of last update: 2014-09-18
  • Class: Client side / Out of bounds memory read
  • Advisory: HT6441 HT6443

Vulnerability Details

Safari accepts PDF files as native image format for the < image > html tag. Thus browsing an html page in Safari can transparently load multiple pdf files without any further user interaction. CoreGraphics is the responsible of parsing the PDF files.
On a PDF, a sampled image may be specified in the form of an inline image as an alternative to the image XObjects described in 8.9.5 ’Image Dictionaries’. This type of image shall be defined directly within the content stream of a pdf page. Each inline image specification may contain the declaration of a colorspace, this colorpace specification complies to a different syntax than the one used in the specification of images as eXternal Objects. For the specification of colorpaces in XObjects see 8.9.5 ’Image Dictionaries’. An inline image embedded in a PDF content stream looks like this:
q
17 0 0 17 298 388 cm                           % Save graphics state
                                               % Scale and translate coordinate space
BI                                             % Begin inline image object
/W 17                                          % Width in samples
/H 17                                          % Height in samples
/CS $COLORSPACE-SPEC$                          % Color space  !!!!!!!!!!!!!!!!!!
/BPC 8                                         % Bits per component
/F [ /A85 /LZW ]                               % Filters
ID                                             % Begin image data
J1/gKA>.]AN&J?]-
EI                                             % End inline image object
Q                                              % Restore graphics state
Where $COLORSPACE-SPEC$ is a placeholder for a colorspace specification as described in section 8.6.4 'Device Colour Spaces'. An Indexed color space shall be defined by a four-element array:
[ /Indexed base hival lookup ]
The first element shall be the color space family name /Indexed (or /I). The base parameter shall be an array or name that identifies the base color space. The hival parameter shall be an integer that specifies the maximum valid index value. The color table shall be defined by the lookup parameter, which for inline images shall be a byte string.
The information leak bug resides in the Indexed type colorspace specification parsing code. It fails to validate hival with the size of the lookup table. This enables an attacker to read past the end of the lookup table, and use the uncontroled memory immediately after the memory of the table in an unexpected way. This research is based on the iPhone3,1(iPhone4) iOS 7.1.1. Considering the dyld_shared_cache_armv7 file is loaded at 0x2c00000000, the function that parses inline image colorspace is at 0x2D527C3C.
Pseudocode follows:

/* Read the colorspace definition under the /ColorSpace or /CS field of dictionary*/
if ( CGPDFDictionaryGetObject(dict, "ColorSpace", &cs_obj) || 
             CGPDFDictionaryGetObject(dict, "CS", &cs_obj)    ) {
    CS = 0;
    if ( cs_obj ) {
    /* In case the colorspace definition is of type PDFName (5) initialize a standar colorpace object */
    if ( CGPDFObjectGetValue(cs_obj, 5, &cs_name) == 1 ) {
        CS = mk_CSDefault_2D52850C(cs_name);
        if ( !CS ) {
            cs_strm = CGPDFContentStreamGetColorSpace(v3, cs_name);
            CS = CGColorSpaceRetain(cs_strm);
        }
    }
    else {
        if (  /* If cs_object is of type PDFArray */
            CGPDFObjectGetValue(cs_obj, 7, &cs_array) == 1  &&
            /* ... and it has 4 elements ...*/
            CGPDFArrayGetCount(cs_array) == 4               &&
            /* ... and the first element is a PDFName ... */
            CGPDFArrayGetName(cs_array, 0, &cs_name) == 1   &&
            /* ... and this PDFName is "/Indexed" or "/I" ... */
            ( !strcmp(cs_name, "Indexed") || !strcmp(cs_name, "I") ) &&
            /* ... and the second element is also a PDFName ...*/
            CGPDFArrayGetName(cs_array, 1, &cs_name) == 1   &&
            /* ... and The third element is an integer N ... */
            CGPDFArrayGetInteger(cs_array, 2, &N) == 1      &&
            /* ... and the forth element, the lookup table is a PDFString */
            CGPDFArrayGetString(cs_array, 3, &index_table) == 1 ) {
                /* get the string raw data */
                str = (void *)CGPDFStringGetBytePtr(index_table);
                if ( str ) {
                    /* prepare the inner simple colorspace */
                    default_cs = mk_CSDefault_2D52850C(cs_name);
                    if ( default_cs ) {
                        /* and build the indexed colorspace object */
                        CS = CGColorSpaceCreateIndexed(default_cs, N, str);
                        CGColorSpaceRelease(default_cs);
                    }
                }
        }
    }
}
Note that the length of the pdf string str is never compared to N. CGColorSpaceCreateIndexed will build a colorspace object with a lookup table composed of str and the memory that happens to follow that.

Exploitation details

The data in the Inline Image that specifies the buggy indexed colorspace will be translated into actual pixels using the lookup table, and the data that resides immediately after the pdf string str will be put as pixel data of the image. This is easy to access via javascript machinery. Something like this..
var img = document.getElementById("pdfleakimage")
var canvas = document.createElement('canvas');
var pixelData;
var content;
/* set the canvas size to the image size */
canvas.width = img.width;
canvas.height = img.height;

/*paste te image on the canvas */
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
/* pixelData is the actual RGBA pixel data :) */
pixelData = canvas.getContext('2d').getImageData(0, 0, img.width, img.height).data;

/* only problem is that the 4th byte of each pixel is not usable ... */
var leak = new Array();
for (var i=0; i<img.width*img.height*4; i+=1){
    if ( (1+i)%4 == 0 )
        continue;
    leak[leak.length] = pixelData[i];
}

/* leak contains the values read from memory */

A 100% reliable PoC leak-exploit can be generated using the github project. Some heap massaging is needed to improve the probability of leaking an interesting address pointer and to elude the corner case in which the str is allocated close to the end of a memory page. The Poc detects the browser device type, firmware version, dyld_shared_cache base address and it also plants a configured shellcode and leaks its starting address. This is ready to implement a 100% reliable code execution exploit (IOS) when combining it with another vulnerability.

Demo

Basically it plants a shellcode, detects the firmware version and pass all the addresses to MobileSafari Javascript. Tested iPhone5 7.1.1. 7.1.2