Tuesday, January 27, 2015

CoreGraphics CCITT Memory Corruption - CVE-2014-4481

Apple CoreGraphics framework fails to validate the input when parsing CCITT group 3 encoded data resulting in a heap overflow condition. A small heap memory allocation can be overflowed with controlled data from the input resulting in arbitrary code execution in the context of Mobile Safari. 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 proof of concept 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-4481
  • Permalink: http://blog.binamuse.com/2015/01/coregraphics-ccitt-memory-corruption.html
  • Date published: 2015-01-27
  • Date of last update: 2015-01-27
  • Class: Client side / Integer Overflow / Memory Corruption
  • Advisory: HT204245

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 CCITT group 3 encoded data. A small heap memory allocation can be overflowed with controled data from the input enabling arbitrary code execution in the context of Mobile Safari.
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_malloc 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_malloc and x_free are used very frequently to allocate and de-allocate memory fast.
The x_malloc function behaves as a normal malloc 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_malloc ( size_t size )
    {
        void * buffer = x_mem_alloc0_s ize (( size + 31) & -16) ;
        *( size_t *) buffer = ( size + 31) & -16;
        return buffer + 16;
    }
This function is prone to integer overflow because it doesn’t check for size to be less than MAXUINT-16. Then if a programmer tries to allocate a size in the range [-16,-1] x_malloc will allocate 0(zero) or 16 bytes (instead of the immense value required) without triggering any exception.

CCITTFaxDecode group 3 x_malloc bug

A x_malloc bug is triggered when decoding CCITT group 3 compressed data. The CCITTFaxDecode pdf filter decodes image data that has been encoded using either Group 3 or Group 4 CCITT facsimile (fax) encoding. CCITT encoding is designed to achieve efficient compression of monochrome (1 bit per pixel) image data at relatively low resolutions, and so is useful only for bitmap image data, not for color images, grayscale images, or general data. Any PDF Stream can be compressed using a different set of filters as described in PDFSPEC::7.3.8::Streams. Under certain conditions the implelentation of the CCITTFaxDecode filter fails to allocate the correct amount of memory for its internal state. It allocates an overly small array that can be overflowded with controlled data. The decoding parmeters of any pdf filter are controled via the /decodeparams field of the stream dictionary. The CCITTFaxDecode filter parses its decoding parameters at the function pdf_source_create_ccitt_fax_filter (see appendix for pseudocode). After the decode parameters are parsed an validated the CCITTFaxDecode internal state is allocated at functions pdf_FaxDecodeStateAlloc. Setting a /Columns value of 0x3fffffff-4 enables the attacker to reach x_malloc with a size in the [16,-1] range (without trigering any exceptions). Thus, the pdf_FaxDecodeStateAlloc function will reseve a small amount of memory for holding a potentially big array of encoded CCITT group3 1D runlenghts values (See rfc804). PDF CCITTFaxDecode filter can handle a number of CCITT encodings, K=0 selects the vulnerable code (namely CCITT group 3 or G31D). An example of the /DecodeParams dictionary nedded to reach the vulnerable code follows:
        /DecodeParms << /Rows 0
                    /EndOfBlock true
                    /K 0
                    /BlackIs1 true
                    /EncodedByteAlign false
                    /Columns 1073741819
                    /EndOfLine false
                    >>
The pdf_FaxDecodeStateAlloc function silently fails to reserve enough space to decode a line of potentially 0x3fffffff-4 runlengths codes. The actual decoding is done at the function pdf_FaxDecode. This function decodes the input stream of CCITT G31D encoded runlength. CCITT group 3 works in a line by line pace, at each line it will iterativelly consume white an black runlengths. Each decoded runlength is appended to the internaly allocated array of 16 bit words. The decompression algorithm is best described in [3]. As the attaker controls the raw input it may craft a list of CCITT runlength codes to overwrite the overly short 16bit word array with a controlled amount of controlled data. Function lookup table based on firmware iPhone5,1-7.1.2, follows:
    0x2D57C824 pdf_FaxDecodeStateAlloc
    0x2D57EF64 pdf_source_create_ccitt_fax_filter
    0x2D57C8D8 pdf_FaxDecode
Assume dyld_shared_cache base address is 0x2c00000.

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.

Listings

int __cdecl pdf_source_create_ccitt_fax_filter(void *arg0, int a1)
{
  ccitt *ccitt; 
  int result; 
  int K; 
  bool flags_a; 
  signed int ccitt_group_; 
  int flags_b; 
  int flags_c; 
  int flags; 
  char blackIsZERO; 
  FaxDecodeState *state; 
  int ccitt_group; 
  char value_bool; 
  int value_int; 

  ccitt = x_calloc(1, 32);
  result = 0;
  if ( ccitt )
  {
    ccitt->K = 0;
    ccitt->EOL = 0;
    ccitt->EncodedByteAlign = 0;
    ccitt->Columns = 1728;
    ccitt->Rows = 0;
    ccitt->EndOfBlock = 1;
    ccitt->BlackIs1 = 0;
    ccitt->DamagedRowsBeforeError = 0;
    if ( a1 )
    {
      if ( CGPDFDictionaryGetInteger(a1, "K", &value_int) )
        ccitt->K = value_int;
      if ( CGPDFDictionaryGetBoolean(a1, "EndOfLine", &value_bool) )
        ccitt->EOL = value_bool != 0;
      if ( CGPDFDictionaryGetBoolean(a1, "EncodedByteAlign", &value_bool) )
        ccitt->EncodedByteAlign = value_bool != 0;
      if ( CGPDFDictionaryGetInteger(a1, "Columns", &value_int) )
      {
        if ( value_int < 0 )
          pdf_error("/%s is outside the range of allowed values.", "Columns");
        else
          ccitt->Columns = value_int;
      }
      v2 = 3793108;
      if ( CGPDFDictionaryGetInteger(a1, "Rows", &value_int) )
      {
        if ( value_int < 0 )
          pdf_error("/%s is outside the range of allowed values.", "Rows");
        else
          ccitt->Rows = value_int;
      }
      if ( CGPDFDictionaryGetBoolean(a1, "EndOfBlock", &value_bool) )
        ccitt->EndOfBlock = value_bool != 0;
      if ( CGPDFDictionaryGetBoolean(a1, "BlackIs1", &value_bool) )
        ccitt->BlackIs1 = value_bool != 0;
      if ( CGPDFDictionaryGetInteger(a1, "DamagedRowsBeforeError", &value_int) )
      {
        if ( value_int < 0 )
          pdf_error("/%s is outside the range of allowed values.", "DamagedRowsBeforeError");
        else
          ccitt->DamagedRowsBeforeError = value_int;
      }
    }
    K = ccitt->K;
    if ( K < 0 )
    {
      ccitt_group_ = 4;
      flags_a = 0;
    }
    else
    {
      flags_a = K > 0;
      ccitt_group_ = 3;
    }
    ccitt_group = ccitt_group_;
    flags_b = flags_a + 2;
    if ( !ccitt->EOL )
      flags_b = flags_a;
    flags_c = flags_b | 4;
    if ( !ccitt->EncodedByteAlign )
      flags_c = flags_b;
    if ( ccitt->EndOfBlock )
    {
      ccitt->Rows = 0;
      flags_c |= 8u;
    }
    flags = flags_c | 0x10;
    blackIsZERO = ccitt->BlackIs1 == 0;
    LOBYTE(ccitt->flags) = 0;
    if ( !blackIsZERO )
      flags = flags_c;
    ccitt->source = CGPDFSourceRetain(arg0);
    state = pdf_FaxDecodeStateAlloc(ccitt->Columns, ccitt_group, flags);
    ccitt->fax_decode_state = state;
    if ( !state
      || (state->getc = (ccitt_fax_filter_getc),
          ccitt->fax_decode_state->stream = arg0,
          (result = CGPDFSourceCreateFilter(
                      ccitt,
                      (ccitt->Columns + 
                               (((ccitt->Columns + 7) >> 31) >> 29) + 7) >> 3,
                      &pdf_source_create_ccitt_fax_filter_callbacks)) == 0) )
    {
      ccitt_fax_filter_finalize(ccitt);
      result = 0;
    }
  }
  return result;
}
FaxDecodeState *__cdecl pdf_FaxDecodeStateAlloc(int columns, int group, int flags)
{
  FaxDecodeState *retval; 
  int size; 
  FaxDecodeState *state; 
  int N; 
  _WORD *buf; 
  unsigned int last; 
  int ptr; 
  char is4; 

  retval = 0;
  size = columns;
  if ( columns <= 0x3FFFFFFF )
  {
    state = x_calloc(1, 0x38);
    if ( state )
    {
      state->group = group;
      state->flags = flags;
      state->columns = columns;
      state->columns_rnd = (columns + (((columns + 7) >> 31) >> 29) + 7) >> 3;
      is4 = (group == 4) | flags & 1;
      if ( is4 )
        size = (2 * columns + 62) & 0xFFFFFFC0;
      N = 2 * size + 3;
      if ( N >= 0 )
      {
        buf = x_malloc(2 * N);
        state->line_buffer = buf;
        if ( buf )
        {
          state->line_buffer2 = buf;
          last = size >> 1;
          if ( is4 & 1 )
            size = size >> 1;
          state->N = size;
          if ( is4 & 1 )
          {
            ptr = &buf[last];
            state->tail = ptr;
          }
          else
          {
            state->tail = 0;
            ptr = 0;
          }
          state->code_bitcount = 0;
          state->bitaccu = 0;
          state->field_18 = 0;
          retval = state;
          if ( ptr )
          {
            *ptr = LOWORD(state->columns);
            *(ptr + 2) = 0;
            retval = state;
          }
        }
      }
    }
  }
  return retval;
}

C Structures

The structures used.
00000000 ccitt           struc ; (sizeof=0x20)
00000000 source          dd ?
00000004 flags           dd ?
00000008 K               dd ?
0000000C Rows            dd ?
00000010 Columns         dd ?
00000014 EOL             db ?
00000015 EncodedByteAlign db ?
00000016 EndOfBlock      db ?
00000017 BlackIs1        db ?
00000018 DamagedRowsBeforeError dd ?
0000001C fax_decode_state dd ?                   ; offset
00000020 ccitt           ends
00000020
00000000 ; ---------------------------------------------------------------------
00000000
00000000 FaxDecodeState  struc ; (sizeof=0x38)
00000000 group           dd ?
00000004 flags           dd ?                    ; 0x10 BlackIsZero0
00000004                                         ; 0x08 EndOfBlock
00000004                                         ; 0x04 EncodedByteAlign
00000004                                         ; 0x02 EOL
00000004                                         ; 0x01 K>0
00000008 columns_rnd     dd ?
0000000C columns         dd ?
00000010 bitaccu         dd ?
00000014 code_bitcount   dd ?
00000018 field_18        dd ?
0000001C line_buffer     dd ?                    ; offset
00000020 tail            dd ?                    ; offset
00000024 line_buffer2    dd ?                    ; offset
00000028 N               dd ?
0000002C field_2C        dd ?
00000030 getc            dd ?                    ; offset
00000034 stream          dd ?                    ; offset
00000038 FaxDecodeState  ends
00000038

Demo

Soon.. maybe.

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

Wednesday, July 31, 2013

Using symbolic execution to solve a tiny ASCII maze.

In this post we'll exercise the symbolic execution engine KLEE over a funny ASCII Maze (yet another toy example)!
LLVM

VS.

Maze dimensions: 11x7
Player pos: 1x1 Iteration no. 0
Program the player moves with
a sequence of 'w', 's', 'a' or 'd'
Try to reach the prize(#)!
           +-+---+---+
           |X|     |#|
           | | --+ | |
           | |   | | |
           | +-- | | |
           |     |   |
           +-----+---+
The match is between a tiny maze-like game coded in C versus the full-fledged LLVM based symbolic execution engine, KLEE.

How many solutions do you think it has?

The Maze

The thing is coded in C and the impatient can download it from here. This simple ASCII game asks you first to feed it with directions. You should enter them as a batch list of actions. As "usual"; a is Left, d is Right, w is Up and s is Down. It has this looks ...
Player pos: 1x4
Iteration no. 2. Action: s.
+-+---+---+
|X|     |#|
|X| --+ | |
|X|   | | |
|X+-- | | |
|     |   |
+-----+---+
It's really small I know! But the code hides a nasty trick, and at the end, you'll see, it has more than one way to solve it.

The KLEE

KLEE is a symbolic interpreter of LLVM bitcode. It runs code compiled/assembled into LLVM symbolically. That's running a program considering its input(or some other variables) to be symbols instead of concrete values like 100 or "cacho". In very few words, a symbolic execution runs through the code propagating symbols and conditions; forking execution at symbol dependant branches and asking the companion SMT solver for path feasibility or counter-examples. For more info on this check out this, this or even this. Find it interesting? Keep reading!

Wednesday, July 24, 2013

Autocad DWG-AC1021 Heap Corruption

AutoCAD is a software for computer-aided design (CAD) and technical drawing in 2D/3D, being one of the world leading CAD design tools. It is developed and sold by Autodesk, Inc.
  • Title: AutoCAD DWG-AC1021 Heap Corruption
  • CVE Name: CVE-2013-3665
  • Permalink: http://blog.binamuse.com/2013/07/autocad-dwg-ac1021-heap-corruption.html
  • Advisory: http://binamuse.com/advisories/BINA-20130724.txt
  • Patch: http://usa.autodesk.com/adsk/servlet/ps/dl/item?id=21972896&linkID=9240618&siteID=123112
  • Vendor notified on: 2013-03-27
  • Patch/Fix Released: 2013-07-10
  • Advisory Published: 2013-07-24

AutoCad is vulnerable to an arbitrary pointer dereference vulnerability, which can be exploited by malicious remote attackers to compromise a user’s system. This issue is due to AutoCad’s failure to properly bounds-check data in a DWG file before using it to index and copy heap memory values. This can be exploited to execute arbitrary code by opening a specially crafted DWG file, version AC1021.

This version was the native fileformat of AutoCAD Release 2007. New versions of the format emerged but AC1021 is still supported in modern AutoCADs for back ward compatibility.

Details

The DWG R2007 file format The R2007 dwg format has sections and pages. There are system sections and data sections. The system sections contain information about where the data sections and their pages are in the file. The system sections are built based in two main data structures: a first header and a second header. In addition, there are two important sections in the file structure, the page map and the section map. Each one of this sections should be decoded using Reed Solomon algorithm and also could be compressed with a proprietary algorithm (which we will ignore). The file structure looks like this:
The DWG R2007 also known as AC1021 is well documented by the reversing effort of opendesign.

Vulnerability Details

Not surprisingly AutoCAD starts by parsing the 1st header. Among other things it reads the size and location of the 2nd header. Then from the second header it reads the position in the file where the page map is stored, the number of pages present in the file (page-count) and the maximum id (page-max-id ) a page shall have. The page map is stored in a single system section page and it is composed by tuples (Id, Size) where the Id is the page number. After the loading of the page map, begins the processing of the section map that eventually will load all the objects present in the draw. Graphically the data representing the page map on the file looks like this:
When each PageMap node is read two data structures are updated, a double linked list of page map nodes called PMapList and an id indexed array of node pointers called PMapArray. A quick description of this three entities follows.

The page map nodes

In memory the structure holding a page map node has the following fields:
  • acc: Accumulator of the field size (64 bits)
  • size: Size of the page. (64 bits)
  • id: Number of the page. (64 bits)
  • prev: Address in memory of the previous node. (32 bits)
  • next: Address in memory of the next node.(32 bits)
  • unknown: there are 2 unknown fields of 64 bits (Not used).

The page map linked list

And they are linked in the PMapList that looks like this:

The page map array

PMapArray is an array of node pointers maintained for quick access of the page map nodes. It maps the id to the actual page map node. A memory chunk of pages-maxid size is allocated for it as declared in the 2nd header.

The bug

When each new page map node is created its address is stored in the corresponding id position of the PMapArray array without checking its boundaries. Thus, enabling an arbitrary heap offset overwrite with a pointer to the recently created node. Details on the exploitation technique can be found in Joshep white paper or in the actual proof of concept exploit.

Friday, May 31, 2013

Multiple vulnerabilities on sketchup

SketchUp is a 3D modeling program marketed by Trimble Navigation Limited(previously Google) and designed for architectural, civil, and mechanical engineers as well as filmmakers, game developers, and related professions.
SketchUp fails to validate the input when parsing different types of embedded textures. Exploitation of this issues will lead to the execution of arbitrary code on the client machine with the privileges of the user running the Sketchup. This vulnerabilities can be triggered when a malicious .skp file is open in SketchUp or previewed in Windows Explorer.
CVE Title Fixed in version
CVE-2013-3662MAC Pict Material Stack Corruption8M2
CVE-2013-3663BMP RLE8 Heap Overflow8M3
CVE-2013-3664MAC Pict Material Stack Corruption 22013
CVE-2013-3664BMP RLE4 Heap Overflow2013
The native SketchUp fileformat can handle textured 3D content. Sketchup can create realistic materials taken from image files such as .jpg pictures taken with a digital camera. A number of this images can be embedded into the main .skp file and loaded every time the 3D scene is open. As the same image library is used for in any case the issues addressed here can also be triggered when Windows Explorer reads the embedded thumbnail in a .skp file. Arbitrary code execution is proved possible in 4 different ways after an .skp file with a malicious texture or thumbnail or background image is opened.

Sketchup MAC Pict Material Palette Stack Corruption

Sketchup fails to validate the input when parsing an embedded MAC Pict texture, leading to an arbitrary stack offset overwrite and finally to an arbitrary code execution. The issue arises when SketchUp tries to load the color palette table of a MAC Pict material (or embedded image). A Mac Pict file can hold palettes of up to 64k colors. It is encoded so the number of colours to read from the file is the firsts 16bit unsigned value of the encoded palette.
                        '>H'  numColors
Then it follows a list of up to numColors palette entries.
                        [
                         '>H'  color index 
                         'BBB' RGB
                        ] * numColors

Each entry is a pair of index and RGB color and the entries can be put in any order. The only constraint is that each index must be less or equal than numColor. SketchUp reads this potentially 64k entries length table in a 256 entries length stack buffer.

`` In Windows this buffer is guarded by a /GS cookie but this protection is rendered useless because of the arbitrary color index. An attacker could select the indexes passed on the encoded palette so the cookie is never altered. ``

So an arbitrary RGB color can be placed at an arbitrary position in the range of the 64k entries from the beginning of the original stack buffer. The only problem is that an RGB color is 3 bytes sized and there is room allocated for a 4th byte in each color entry, probably for an alpha channel. This 4th byte (the most significant byte of the resultant 32bit word) is forced to 00.

Thus, is fair to say that an almost arbitrary offset of the stack can be written with an almost arbitrary value. Playing with the stacked local values of the calling functions it is possible to capture the execution flow and execute arbitrary code.

Exploitation of the above problem will lead to the execution of arbitrary code on the client machine with the privileges of the user running the SketchUp.

In the exploit, as it is possible to embed several materials two different exploiting primitives are devised.

  1. write a zero byte followed by 3 controlled bytes to any low memory address.
  2. control the program counter via a memory deference

So each time a MAC Pict image is parsed by Google SketchUp either of these things can be done. This exploit needs less than 24 texture images to control the program behavior.

In Windows the writable memory at address 0x100000 is used as a pivot. The stack is switched to it and a ROP like chain is mastered to bypass DEP. Yes there are fixed executable memory maps loaded at the same addres in W7/XP(0x100000) where to search for ROP gadgets. This dll being fixed at 0x100000 is the only source of unreliability of the exploit.

Debug

The function that reads the MAC Pict material color pallete is at 0x017449b0. The .skp file can contain up to 24 preseted materials possibly replaced by PICT images; so a breakpoint there can happen up to 24 times with this settings. A copy of the vulnerable function dissemble is here.

Summary and exploit

SketchUp BMP RLE8 Heap Overflow

Sketchup fails to validate the input when parsing an embedded BMP RLE8 compressed texture, leading to an arbitrary stack offset overwrite and finally to arbitrary code execution.

The code parsing BMP/RLE images seem to be taken from a discontinued open source project, paintlib. The problematic function looks like this:

void PLBmpDecoder::decodeRLE8
    ( PLDataSource * pDataSrc,
      PLBmpBase * pBmp
    )
            // Decodes a compressed 256-color-bitmap
{
  int y;                    // Current row

  PLBYTE * pDest;           // Current destination
  PLBYTE * pSrc;            // Current position in file
  PLBYTE   RunLength;       // Length of current run
  bool   bEOL;              // true if end of line reached
  bool   bEOF=false;        // true if end of file reached
  PLBYTE ** pLineArray = pBmp->GetLineArray();
                            // Pointers to dest lines

  Trace (2, "Decoding RLE8-compressed bitmap.\n");

  for (y=0; y<pBmp->GetHeight() && !bEOF; y++)
  {                         // For each line...
    pDest = pLineArray[pBmp->GetHeight()-y-1];
    bEOL=false;
    while (!bEOL)
    {                       // For each packet do
      pSrc = pDataSrc->Read1Byte();
      RunLength = *pSrc;
      if (RunLength==0)
      { // Literal or escape.
        pSrc = pDataSrc->Read1Byte();
        RunLength = *pSrc;
        switch (RunLength)
        {
          case 0:           // End of line escape
            bEOL = true;
            break;
          case 1:           // End of file escape
            bEOF = true;
            bEOL = true;
            break;
          case 2:           // Delta escape.
            // I have never seen a file using this
            raiseError (PL_ERRFORMAT_NOT_SUPPORTED,
                        "Encountered delta escape.");
            bEOL = true;
            bEOF = true;
            break;
          default:
                            // Literal packet
            pSrc = pDataSrc->ReadNBytes(RunLength);
            memcpy (pDest, pSrc, RunLength);
            pDest += RunLength;
            // Word alignment at end of literal packet.
            if (RunLength & 1) pDataSrc->Skip(1);
        }
      }
      else
      {                     // Encoded packet:
                            // RunLength pixels, 
                                // all with the same value
        pSrc = pDataSrc->Read1Byte();
        memset (pDest, *pSrc, RunLength);
        pDest += RunLength;
      }
    }
  }
}

Note that the 'while' statement ends only when it reaches an EOF token (or similar condition) on the input. It decodes RLE packets into the previously allocated buffer "pLineArray". The pLineArray is supposed to by a bitmap. Its size (height*width) is controlled by the input. So we can allocate any buffer size and then write and overflow it with whatever content we want.

Exploitation of the above problem will lead to the execution of arbitrary code on the client machine with the privileges of the user running the Sketchup.

Sumary and exploit

MAC Pict Material Stack Corruption 2

Similarly to the previous PICT bug this is triggered when SketchUp loads the color palette table of a MAC Pict material (or embedded image). We found another way to reach the vulnerable code, which was still there.

Sumary and exploit

BMP RLE4 Heap Overflow

Similarly to the previous BMP bug this bug is triggered when SketchUp loads BMP material with RLE4 compression.

The code parsing BMP/RLE4 images seem to be taken from a discontinued open source project, paintlib. The problematic function looks like this:

void PLBmpDecoder::decodeRLE4
    ( PLDataSource * pDataSrc,
      PLBmpBase * pBmp
    )
    // Decodes a compressed 16-color-bitmap.
{
  int y;                              // Current row

  PLBYTE * pSrc;
  PLBYTE * pDest;                       // Current destination.
  int    XSize = pBmp->GetWidth();  // Width of bitmap in pixels.
  PLBYTE   SrcByte;                     // Source byte cache.

  PLBYTE   RunLength;    // Length of current run.
  bool   bOdd;         // true if current run has odd length.

  bool   bEOL;         // true if end of line reached.
  bool   bEOF=false;   // true if end of file reached.

  PLBYTE * pLineBuf;     // Current line as uncompressed nibbles.
  PLBYTE * pBuf;         // Current position in pLineBuf.
  PLBYTE ** pLineArray = pBmp->GetLineArray();
                                   // Pointers to dest. lines.

  Trace (2, "Decoding RLE4-compressed bitmap.
");

  // Allocate enough memory for DWORD alignment in original 4 bpp
  // bitmap.
  pLineBuf = new PLBYTE [XSize*4+28];

  for (y=0; y<pBmp->GetHeight() && !bEOF; y++)
  { // For each line...
    pBuf = pLineBuf;
    bEOL=false;
    while (!bEOL)
    { // For each packet do
      pSrc = pDataSrc->Read1Byte();
      RunLength = *pSrc;
      if (RunLength==0)
      { // Literal or escape.
        pSrc = pDataSrc->Read1Byte();
        RunLength = *pSrc;
        switch (RunLength)
        {
          case 0: // End of line escape
            bEOL = true;
            break;
          case 1: // End of file escape
            bEOF = true;
            bEOL = true;
            break;
          case 2: // Delta escape.
            // I have never seen a file using this.
            delete [] pLineBuf;
            raiseError (PL_ERRFORMAT_NOT_SUPPORTED,
                        "Encountered delta escape.");
            break;
          default:
            // Literal packet
            bOdd = (RunLength & 1);
            RunLength /= 2; // Convert pixels to bytes.
            for (int i=0; i<RunLength; i++)
            { // For each source byte...
              pSrc = pDataSrc->Read1Byte();
              decode2Nibbles (pBuf, *pSrc);
              pBuf += 2;
            }
            if (bOdd)
            { // Odd length packet -> one nibble left over
              pSrc = pDataSrc->Read1Byte();
              *pBuf = (*(pSrc))>>4;
              pBuf++;
            }
            // Word alignment at end of literal packet.
            if ((RunLength + bOdd) & 1) pDataSrc->Skip(1);
        }
      }
      else
      { // Encoded packet:
        // RunLength 4 bpp pixels with 2 alternating
        // values.
        pSrc = pDataSrc->Read1Byte();
        SrcByte = *pSrc;
        for (int i=0; i<RunLength/2; i++)
        {
          decode2Nibbles (pBuf, SrcByte);
          pBuf += 2;
        }
        if (RunLength & 1)
        {
          *pBuf = (*(pSrc))>>4;
          pBuf++;
        }
      }
    }
    pDest = pLineArray[pBmp->GetHeight()-y-1];
    memcpy (pDest, pLineBuf, XSize);
  }
  delete [] pLineBuf;
}

Note that the for-loop inside the 'Encoded packet' branch decode/copy two nibbles to pBuf every time, executing this RunLength/2 times. And because there is no check about the pBuf's length before advancing the pointer two positions, a heap overflow (pBuf based) may arise.

The pBuf is initialized to pLineBuf wich is a fresh allocated buffer of size XSize*4+28, being XSize the BMP's width (controlled value). So we can allocate almost any buffer size and then write and overflow it with words like 0x0X0Y (X,Y controlled nibble values).

Exploitation of the above problem will lead to the execution of arbitrary code on the client machine with the privileges of the user running the Sketchup.

Sumary and exploit