CoreGraphics CCITT Memory Corruption - CVE-2014-4481

2015-01-28T00:39:00
ID BINAMUSE:F61C45CDC72EEDA3B26D9A56201D5E74
Type binamuse
Reporter feliam (noreply@blogger.com)
Modified 2015-01-28T00:40:23

Description

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 2sizeof(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 &lt;&lt; /Rows 0  
                    /EndOfBlock true  
                    /K 0  
                    /BlackIs1 true  
                    /EncodedByteAlign false  
                    /Columns 1073741819  
                    /EndOfLine false  
                    &gt;&gt;

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-&gt;K = 0;  
    ccitt-&gt;EOL = 0;  
    ccitt-&gt;EncodedByteAlign = 0;  
    ccitt-&gt;Columns = 1728;  
    ccitt-&gt;Rows = 0;  
    ccitt-&gt;EndOfBlock = 1;  
    ccitt-&gt;BlackIs1 = 0;  
    ccitt-&gt;DamagedRowsBeforeError = 0;  
    if ( a1 )  
    {  
      if ( CGPDFDictionaryGetInteger(a1, "K", &value_int) )  
        ccitt-&gt;K = value_int;  
      if ( CGPDFDictionaryGetBoolean(a1, "EndOfLine", &value_bool) )  
        ccitt-&gt;EOL = value_bool != 0;  
      if ( CGPDFDictionaryGetBoolean(a1, "EncodedByteAlign", &value_bool) )  
        ccitt-&gt;EncodedByteAlign = value_bool != 0;  
      if ( CGPDFDictionaryGetInteger(a1, "Columns", &value_int) )  
      {  
        if ( value_int &lt; 0 )  
          pdf_error("/%s is outside the range of allowed values.", "Columns");  
        else  
          ccitt-&gt;Columns = value_int;  
      }  
      v2 = 3793108;  
      if ( CGPDFDictionaryGetInteger(a1, "Rows", &value_int) )  
      {  
        if ( value_int &lt; 0 )  
          pdf_error("/%s is outside the range of allowed values.", "Rows");  
        else  
          ccitt-&gt;Rows = value_int;  
      }  
      if ( CGPDFDictionaryGetBoolean(a1, "EndOfBlock", &value_bool) )  
        ccitt-&gt;EndOfBlock = value_bool != 0;  
      if ( CGPDFDictionaryGetBoolean(a1, "BlackIs1", &value_bool) )  
        ccitt-&gt;BlackIs1 = value_bool != 0;  
      if ( CGPDFDictionaryGetInteger(a1, "DamagedRowsBeforeError", &value_int) )  
      {  
        if ( value_int &lt; 0 )  
          pdf_error("/%s is outside the range of allowed values.", "DamagedRowsBeforeError");  
        else  
          ccitt-&gt;DamagedRowsBeforeError = value_int;  
      }  
    }  
    K = ccitt-&gt;K;  
    if ( K &lt; 0 )  
    {  
      ccitt_group_ = 4;  
      flags_a = 0;  
    }  
    else  
    {  
      flags_a = K &gt; 0;  
      ccitt_group_ = 3;  
    }  
    ccitt_group = ccitt_group_;  
    flags_b = flags_a + 2;  
    if ( !ccitt-&gt;EOL )  
      flags_b = flags_a;  
    flags_c = flags_b | 4;  
    if ( !ccitt-&gt;EncodedByteAlign )  
      flags_c = flags_b;  
    if ( ccitt-&gt;EndOfBlock )  
    {  
      ccitt-&gt;Rows = 0;  
      flags_c |= 8u;  
    }  
    flags = flags_c | 0x10;  
    blackIsZERO = ccitt-&gt;BlackIs1 == 0;  
    LOBYTE(ccitt-&gt;flags) = 0;  
    if ( !blackIsZERO )  
      flags = flags_c;  
    ccitt-&gt;source = CGPDFSourceRetain(arg0);  
    state = pdf_FaxDecodeStateAlloc(ccitt-&gt;Columns, ccitt_group, flags);  
    ccitt-&gt;fax_decode_state = state;  
    if ( !state  
      || (state-&gt;getc = (ccitt_fax_filter_getc),  
          ccitt-&gt;fax_decode_state-&gt;stream = arg0,  
          (result = CGPDFSourceCreateFilter(  
                      ccitt,  
                      (ccitt-&gt;Columns +   
                               (((ccitt-&gt;Columns + 7) &gt;&gt; 31) &gt;&gt; 29) + 7) &gt;&gt; 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 &lt;= 0x3FFFFFFF )  
  {  
    state = x_calloc(1, 0x38);  
    if ( state )  
    {  
      state-&gt;group = group;  
      state-&gt;flags = flags;  
      state-&gt;columns = columns;  
      state-&gt;columns_rnd = (columns + (((columns + 7) &gt;&gt; 31) &gt;&gt; 29) + 7) &gt;&gt; 3;  
      is4 = (group == 4) | flags & 1;  
      if ( is4 )  
        size = (2 * columns + 62) & 0xFFFFFFC0;  
      N = 2 * size + 3;  
      if ( N &gt;= 0 )  
      {  
        buf = x_malloc(2 * N);  
        state-&gt;line_buffer = buf;  
        if ( buf )  
        {  
          state-&gt;line_buffer2 = buf;  
          last = size &gt;&gt; 1;  
          if ( is4 & 1 )  
            size = size &gt;&gt; 1;  
          state-&gt;N = size;  
          if ( is4 & 1 )  
          {  
            ptr = &buf[last];  
            state-&gt;tail = ptr;  
          }  
          else  
          {  
            state-&gt;tail = 0;  
            ptr = 0;  
          }  
          state-&gt;code_bitcount = 0;  
          state-&gt;bitaccu = 0;  
          state-&gt;field_18 = 0;  
          retval = state;  
          if ( ptr )  
          {  
            *ptr = LOWORD(state-&gt;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&gt;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.