Talos Vulnerability Report

TALOS-2016-0186

Apple Core Graphics BMP Framework img_decode_read Remote Code Execution Vulnerability

July 18, 2016
CVE Number

CVE-2016-4637

SUMMARY

An exploitable out of bounds write exists in the handling of BMP images on Apple OS X and iOS. A crafted BMP document can lead to an out of bounds write resulting in remote code execution. Vulnerability can be triggered via a saved BMP file delivered by other means when opened in any application using the Apple Core Graphics API.

TESTED VERSIONS

OS X El Capitan - 10.11.5

PRODUCT URLs

https://developer.apple.com/osx/download

CVSSv3 SCORE

6.3 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N

DETAILS

This vulnerability is present in the Apple CoreGraphics framework which is used for 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 on OS X and iOS.

There exists a vulnerability in the parsing and handling of BMP images. A specially crafted BMP image file can lead to an out of bounds write and ultimately to remote code execution.

The BMP file format, also known as bitmap image file or device independent bitmap file format or simply a bitmap, is a raster graphics image file format used to store bitmap digital images, independently of the display device (such as a graphics adapter), especially on Microsoft Windows. BMP handles multiple different forms of compression as well making it a somewhat difficult graphics format to parse.

A dump of the files relevant header components is shown below:

<class bmp.BITMAPINFOHEADER> 'bmiHeader'
[e] <instance bmp.DWORD 'biSize'> 0x00000028 (40)
[12] <instance bmp.LONG 'biWidth'> 0x0000e803 (59395)
[16] <instance bmp.LONG 'biHeight'> 0x0000ff01 (65281)
[1a] <instance bmp.WORD 'biPlanes'> 0x0001 (1)
[1c] <instance bmp.WORD 'biBitCount'> 0x0004 (4)
[1e] <instance bmp.__biCompression 'biCompression'> BI_RLE4(0x2)
[22] <instance bmp.DWORD 'biSizeImage'> 0x00000200 (512)
[26] <instance bmp.LONG 'biXPelsPerMeter'> 0x00000b12 (2834)
[2a] <instance bmp.LONG 'biYPelsPerMeter'> 0x00000b12 (2834)
[2e] <instance bmp.DWORD 'biClrUsed'> 0x00000010 (16)
[32] <instance bmp.DWORD 'biClrImportant'> 0x00000010 (16)
```

And running the file through Qlmanage with guard malloc enabled shows us this crash:

```
rax = 0x0000001a1afd5500
rbx = 0x00000000fffffffe
rcx = 0x0000000000000002
rdx = 0x0000000000000000
rdi = 0x000000000000e804
rsi = 0x0000001e1b1f47b0
rbp = 0x0000001a7e877c70
rsp = 0x0000001a7e877c48
r8 = 0x0000001a1afd5500
r9 = 0x0000001e1b1f47b0
r10 = 0x0000000000000004
r11 = 0x0000000000000003
r12 = 0x0000000000000001
r13 = 0x000000000000e804
r14 = 0x000000000003a00c
r15 = 0x0000000000000002
rip = 0x00007fff8e2ba109  CoreGraphics`decode_byte_8bpc_3 + 369

CoreGraphics`decode_byte_8bpc_3:
    0x7fff8e2ba109 <+369>: mov    bl, byte ptr [r12 + rax]
    0x7fff8e2ba10d <+373>: mov    cl, byte ptr [r15 + rax]
    0x7fff8e2ba111 <+377>: mov    dl, byte ptr [r11 + rax]
    0x7fff8e2ba115 <+381>: mov    byte ptr [rsi], bl
    0x7fff8e2ba117 <+383>: mov    byte ptr [rsi + 0x1], cl
    0x7fff8e2ba11a <+386>: mov    byte ptr [rsi + 0x2], dl
    0x7fff8e2ba11d <+389>: add    rax, r10
    0x7fff8e2ba120 <+392>: add    edi, -0x1

BACKTRACE

* thread #12: tid = 0x5e16, 0x00007fff8e2ba109 CoreGraphics`decode_byte_8bpc_3 + 369, queue = 'com.apple.root.default-qos', stop reason = EXC_BAD_ACCESS (code=1, address=0x1a1afd5501)
  * frame #0: 0x00007fff8e2ba109 CoreGraphics`decode_byte_8bpc_3 + 369
    frame #1: 0x00007fff8e25c3e2 CoreGraphics`decode_data + 16158
    frame #2: 0x00007fff8e257a58 CoreGraphics`img_decode_read + 378
    frame #3: 0x00007fff8e2577d7 CoreGraphics`img_colormatch_read + 363
    frame #4: 0x00007fff8e25571f CoreGraphics`img_data_lock + 8852
    frame #5: 0x00007fff8e2525af CoreGraphics`CGSImageDataLock + 151
    frame #6: 0x00007fff9e8f41d4 libRIP.A.dylib`ripc_AcquireImage + 972
    frame #7: 0x00007fff9e8f2c7e libRIP.A.dylib`ripc_DrawImage + 1011
    frame #8: 0x00007fff8e251b31 CoreGraphics`CGContextDrawImageWithOptions + 571
    frame #9: 0x00007fff8e2518da CoreGraphics`CGContextDrawImage + 51

Disassembling down from the backtrace we can spot where the vulnerability arises in img_decode_read.

__text:00000000000399F6 _img_decode_read:                       ; CODE XREF: _img_imagemask_read+1AAp
__text:00000000000399F6                                         ; _img_imagemask_read+266p ...
__text:00000000000399F6                 push    rbp
__text:00000000000399F7                 mov     rbp, rsp
__text:00000000000399FA                 push    r15
__text:00000000000399FC                 push    r14
__text:00000000000399FE                 push    r13
__text:0000000000039A00                 push    r12
__text:0000000000039A02                 push    rbx
__text:0000000000039A03                 sub     rsp, 98h
__text:0000000000039A0A                 mov     [rbp-68h], r8
__text:0000000000039A0E                 mov     r9, rcx
__text:0000000000039A11                 mov     r10d, edx
__text:0000000000039A14                 mov     r14d, esi          [0]

...

__text:0000000000039B0D                 call    _get_image_pointer [1]
__text:0000000000039B12                 test    rax, rax
__text:0000000000039B15                 jz      loc_39D19
__text:0000000000039B1B                 mov     esi, [r13+78h]
__text:0000000000039B1F                 mov     r8d, [r13+88h]
__text:0000000000039B26                 movsxd  rcx, r14d 
__text:0000000000039B29                 imul    r14d, r8d
__text:0000000000039B2D                 mov     rdi, [r13+0A0h]
__text:0000000000039B34                 mov     [r13+58h], rcx
__text:0000000000039B38                 movsxd  rcx, r14d [2]
__text:0000000000039B3B                 add     rax, rcx

This function is used in a loop to continually grab more data from the image with an increasing value passed in via RSI (at [0]) until it reaches the image height value. When the value grows large enough a sign extension error occurs. In this case it is 0xff00 that causes the problem. A pointer to a valid image data buffer (at [1]) is returned and then some indexing is done on it to get to the proper element. The problem arises because the image height is not properly checked so when the value in R14 is promoted to a larger type stored in RCX the remaining bits gets filled in with 0xF causing an invalid index into the array. As shown previously the invalid index is calculated via the image height allowing this vulnerability to potentially be leveraged into an information leak or full remote code execution.

CRASH INFORMATION

Crashed thread log =
: Dispatch queue: com.apple.main-thread
0   libOpenEXR.dylib                0x00000001068b20b2 Imf_2_2::InputFile::readPixels(int, int) + 1068
1   libOpenEXR.dylib                0x000000010693c7d6 exrReadRGBFloat(char const*, int*, int*, unsigned int*, void*) + 621
2   com.apple.ImageIO.framework     0x00007fff8d3460cf copyImageBlockSetOpenEXR + 856
3   com.apple.ImageIO.framework     0x00007fff8d2f40f4 ImageProviderCopyImageBlockSetCallback + 651
4   com.apple.CoreGraphics          0x00007fff93f15cb4 CGImageProviderCopyImageBlockSetWithOptions + 132
5   com.apple.CoreGraphics          0x00007fff93f1739c CGImageProviderCopyImageBlockSet + 205
6   com.apple.CoreGraphics          0x00007fff93f4e7fd img_blocks_create + 517
7   com.apple.CoreGraphics          0x00007fff93f19c9f img_data_lock + 1788
8   com.apple.CoreGraphics          0x00007fff93f186c7 CGSImageDataLock + 151
9   libRIP.A.dylib                  0x00007fff933ee1d4 ripc_AcquireImage + 972
10  libRIP.A.dylib                  0x00007fff933ecc7e ripc_DrawImage + 1011
11  com.apple.CoreGraphics          0x00007fff93f17c48 CGContextDrawImageWithOptions + 571
12  com.apple.CoreGraphics          0x00007fff93f179f1 CGContextDrawImage + 51
13  com.apple.ImageIO.framework     0x00007fff8d31579e CGImageCreateCopyWithParametersNew + 2575
14  com.apple.ImageIO.framework     0x00007fff8d314b95 CGImageSourceCreateThumbnailAtIndex + 3821
15  com.apple.imageKit              0x00007fff8b1ce044 -[IKImageContentView _newCGImageFromImgSrc:index:displayProperties:imageScale:createBitmapImmediately:] + 747
16  com.apple.imageKit              0x00007fff8b1ce49c __69-[IKImageContentView setImageURL:imageAtIndex:withDisplayProperties:]_block_invoke + 57
17  com.apple.imageKit              0x00007fff8b1ce38b -[IKImageContentView setImageURL:imageAtIndex:withDisplayProperties:] + 799
18  com.apple.Preview               0x0000000101626162 0x10160b000 + 110946
19  com.apple.Preview               0x000000010161fe5d 0x10160b000 + 85597
20  com.apple.Preview               0x0000000101616b47 0x10160b000 + 47943
21  com.apple.AppKit                0x00007fff9978ea2b -[NSWindowController _windowDidLoad] + 592
22  com.apple.AppKit                0x00007fff9972b542 -[NSWindowController window] + 110
23  com.apple.Preview               0x0000000101614d9a 0x10160b000 + 40346
24  com.apple.AppKit                0x00007fff9991b03d -[NSWindowController showWindow:] + 36
25  com.apple.Preview               0x000000010161619e 0x10160b000 + 45470
26  com.apple.Foundation            0x00007fff97316f4e -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 1115
27  com.apple.Foundation            0x00007fff97316a75 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] + 131
28  com.apple.Preview               0x00000001016160df 0x10160b000 + 45279
29  com.apple.Preview               0x0000000101614f13 0x10160b000 + 40723
30  com.apple.Preview               0x00000001016ffdfe 0x10160b000 + 1003006
31  libdispatch.dylib               0x00007fff9c53693d _dispatch_call_block_and_release + 12
32  libdispatch.dylib               0x00007fff9c52b40b _dispatch_client_callout + 8
33  libdispatch.dylib               0x00007fff9c53ec1c _dispatch_main_queue_callback_4CF + 1685
34  com.apple.CoreFoundation        0x00007fff8e4a39e9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
35  com.apple.CoreFoundation        0x00007fff8e4628dd __CFRunLoopRun + 1949
36  com.apple.CoreFoundation        0x00007fff8e461ed8 CFRunLoopRunSpecific + 296
37  com.apple.HIToolbox             0x00007fff95160935 RunCurrentEventLoopInMode + 235
38  com.apple.HIToolbox             0x00007fff9516076f ReceiveNextEventCommon + 432
39  com.apple.HIToolbox             0x00007fff951605af _BlockUntilNextEventMatchingListInModeWithFilter + 71
40  com.apple.AppKit                0x00007fff9971aefa _DPSNextEvent + 1067
41  com.apple.AppKit                0x00007fff9971a32a -[NSApplication _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 454
42  com.apple.AppKit                0x00007fff9970ee84 -[NSApplication run] + 682
43  com.apple.AppKit                0x00007fff996d846c NSApplicationMain + 1176
44  libdyld.dylib                   0x00007fff911725ad start + 1

---
exception=EXC_BAD_ACCESS:signal=11:is_exploitable=yes:instruction_disassembly=movb  %bl,(%rax,%rcx):instruction_address=0x00000001068b20b2:access_type=write:access_address=0x00007e214703dc0c:
Crash accessing invalid address.  Consider running it again with libgmalloc(3) to see if the log changes.
bootstrap_look_up: (os/kern) unknown error code (44e)
+ EXIT_VALUE=255
+ exit 255

TIMELINE

2016-06-15 - Vendor Disclosure
2016-07-18 - Public Release

Credit

Discovered by Tyler Bohan of Cisco Talos