Talos Vulnerability Report

TALOS-2016-0180

Apple Image I/O EXR Color Component Remote Code Execution Vulnerability

July 18, 2016
CVE Number

CVE-2016-4629

SUMMARY

An exploitable heap based buffer overflow exists in the handling of EXR images on OS X. A crafted EXR document can lead to a heap based buffer overflow resulting in remote code execution. Vulnerability can be triggered via a saved EXR file delivered by other means when opened in any application using the Apple Image I/O API.

TESTED VERSIONS

OSX El Capitan - 10.11.4

PRODUCT URLs

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

CVSSv3 SCORE

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

DETAILS

This vulnerability is present in the Apple Image I/O API which is used for all image handling on OS X including rendering images in Preview.

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

OpenEXR is a high dynamic-range (HDR) image file format developed by Industrial Light & Magic for use in computer imaging applications and is used in all motion pictures currently in production. EXR uses 16-bit floating-point color component values. Since the IEEE-754 floating-point specification does not define a 16-bit format, EXR created the “half” format. Half values have 1 sign bit, 5 exponent bits, and 10 mantissa bits. This information is then read in as rows of x and y coordinates to draw the image. The vulnerability arises when the values read in are not properly sanitized.

The relevant code for the reading of the image is shown below.

```
for (int y = yStart; y <= maxYThisRow; y += toSlice.ySampling)
{
    // Set the pointers to the start of the y scanline in
    // this row of tiles
    
    fromPtr = fromSlice.base + (y - tileRange.min.y) * fromSlice.yStride + xStart * fromSlice.xStride;

    toPtr = toSlice.base +
            divp (y, toSlice.ySampling) * toSlice.yStride +
            divp (xStart, toSlice.xSampling) * toSlice.xStride; [1]

    // Copy all pixels for the scanline in this row of tiles

    for (int x = xStart; x <= levelRange.max.x; x += toSlice.xSampling)
    {
      for (int i = 0; i < size; ++i)
             toPtr[i] = fromPtr[i];

      fromPtr += fromSlice.xStride * toSlice.xSampling;
      toPtr += toSlice.xStride;
    }
}
```

The problem here arises when toPtr is calculated, [1], and the values of xStride and yStride are user controlled. If these variables are signed it causes the resulting calculation to be sign extended and points the destination buffer out of bounds. Shown below is where the toPtr is calculated and the corresponding values.

```
  RAX: 0xFFFFFFFFDEDEDE00  RBX: 0x00000000DEDEDE00  RBP: 0x00007FFF5FBFA200  RSP: 0x00007FFF5FBFA120  o d I t S z a p c
  RDI: 0x00000000DEDEDEDE  RSI: 0x0000000000000002  RDX: 0x0000000196C68D20  RCX: 0xFFFFFE3242423620  RIP: 0x0000000168475097
  R8:  0x0000000196C68D20  R9:  0x0000000000000002  R10: 0x00000000DEDEDEDE  R11: 0x00000000DEDEDE00  R12: 0x0000000100000001
  R13: 0x0000000000000002  R14: 0x0000000000000010  R15: 0x00000000DEDEDEDE

libOpenEXR.dylib`Imf_2_2::InputFile::readPixels:
->  0x168475097 <+1041>: imul   rax, r14
    0x16847509b <+1045>: add    rax, rcx
    0x16847509e <+1048>: add    rax, qword ptr [rbp - 0x78] [1]
    0x1684750a2 <+1052>: mov    edi, dword ptr [rbp - 0x4c]
    0x1684750a5 <+1055>: mov    ecx, 0x0
    0x1684750aa <+1060>: test   r9d, r9d
    0x1684750ad <+1063>: jle    0x1684750c1               ; <+1083>
    0x1684750af <+1065>: mov    bl, byte ptr [rdx + rcx]

```

Notice RAX is sign extended and contains a very large negative value. When the base of the buffer is added to it, [1], it is pointing well out of bounds, causing an out of bounds write. With proper calculation and set up this vulnerability could potentially be leveraged into a remote code execution vulnerability and give an attacker full control. The resulting crash is shown below.

```
  RAX: 0xFFFFFE3196C5C21C  RBX: 0x00000000DEDEDEAA  RBP: 0x00007FFF5FBFA200  RSP: 0x00007FFF5FBFA120  o d I t s z a p c
  RDI: 0x00000000DEDEDE00  RSI: 0x0000000000000002  RDX: 0x0000000196C68D20  RCX: 0x0000000000000000  RIP: 0x00000001684750B2
  R8:  0x0000000196C68D20  R9:  0x0000000000000002  R10: 0x00000000DEDEDEDE  R11: 0x00000000DEDEDE00  R12: 0x0000000100000001
  R13: 0x0000000000000002  R14: 0x0000000000000010  R15: 0x00000000DEDEDEDE

libOpenEXR.dylib`Imf_2_2::InputFile::readPixels:
->  0x1684750b2 <+1068>: mov    byte ptr [rax + rcx], bl
    0x1684750b5 <+1071>: add    rcx, 0x1
    0x1684750b9 <+1075>: cmp    esi, ecx
    0x1684750bb <+1077>: jne    0x1684750af               ; <+1065>
    0x1684750bd <+1079>: mov    r15d, dword ptr [rbp - 0x30]
    0x1684750c1 <+1083>: add    rdx, r13
    0x1684750c4 <+1086>: add    rax, r14
    0x1684750c7 <+1089>: add    edi, r12d
```

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-05-16 - Vendor Disclosure
2016-07-18 - Public Release

Credit

Tyler Bohan of Cisco Talos