CVE-2016-4637
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.
OS X El Capitan - 10.11.5
https://developer.apple.com/osx/download
6.3 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N
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.
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
2016-06-15 - Vendor Disclosure
2016-07-18 - Public Release
Discovered by Tyler Bohan of Cisco Talos