Talos Vulnerability Report

TALOS-2019-0881

Investintech Able2Extract professional JPEG decoding code execution vulnerability

November 4, 2019
CVE Number

CVE-2019-5089

Summary

An exploitable memory corruption vulnerability exists in Investintech Able2Extract Professional. A specially crafted JPEG file can cause an out-of-bounds memory write, allowing an attacker to execute arbitrary code on the victim machine. An attacker could exploit a vulnerability by providing the user with a specially crafted JPEG file.

Tested Versions

Investintech Able2Extract Professional 14.0.7 x64

Product URLs

https://www.investintech.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-190: Integer Overflow or Wraparound

Details

Able2Extract Professional 14 is a cross-platform PDF tool for Windows, Mac and Linux. It is a PDF converter, creator and editor that lets users create secure PDFs, sign PDFs, edit PDF paragraph text, add images, insert bates numbering, add blank PDF pages, annotate or redact PDFs, and more.

A specially crafted JPG file opened by Able2Extract can cause out-of-bounds memory write (memory corruption), vulnerable code is provided below:

; Memory allocation is done here
.text:00000000000541D3        mov     ecx, [rbp+5A0h+var_5B8_JPG_nr_comp?]
.text:00000000000541D6        imul    ecx, [rbp+5A0h+var_578_JPGHeight]
.text:00000000000541DA        imul    ecx, [rbp+5A0h+var_57C_JPGWidth] ; Size
.text:00000000000541DE        call    malloc_mem
.text:00000000000541E3        mov     rbx, rax
.text:00000000000541E6        mov     [rbp+5A0h+var_620_allocatedMEM], rax

As you can see there will be integer overflow in the malloc size calculation, since the malloc size is 32 bits, and the result of JPG_nr_comp * JPGHeight * JPGWidth can exceed 32-bit number. This will lead to memory corruption in the loop below:

; Corrupting function is executed here:
.text:0000000000054280 till_entire_Height:
.text:0000000000054280        lea     rax, [rdi+rbx]                    ; rdi changes, rbx allocated mem
                                                                        ; with size nr_comp*width*height
.text:0000000000054284        mov     [rsp+6A0h+var_638], rax           ; starting addr pointer for final dest
.text:0000000000054289        mov     r8d, 1
.text:000000000005428F        lea     rdx, [rsp+6A0h+var_638]
.text:0000000000054294        lea     rcx, [rbp+5A0h+Dst]
.text:0000000000054298        call    CorruptPrologue    ; 5429d
.text:000000000005429D        mov     eax, [rbp+5A0h+var_5B8_JPG_nr_comp?]
.text:00000000000542A0        imul    eax, [rbp+5A0h+var_57C_JPGWidth]
.text:00000000000542A4        add     rdi, rax                          ; increase ptr by JPG_nr_comp? * JPGWidth
.text:00000000000542A7        mov     eax, [rbp+5A0h+v39_loop__MAXVAL_JPGHeight] 
.text:00000000000542AA        cmp     [rbp+5A0h+v44_loop], eax          ; are we done?
.text:00000000000542AD        jb      short till_entire_Height          ; rdi changes, rbx allocated mem
                                                                        ; with size nr_comp*width*height
.text:00000000000542AF

This loop can be pseudo-translated to the following pseudo code:

for (loop_i = 0; loop_i < JPGHeight; loop_i++)
{
    mem[_index] = WRITE_HERE; (in CorruptPrologue)
    _index += (nr_comp * cinfo.output_width);       // due to integer overflow in calculating size for malloc
                                                    // this index will exceed the allocated memory boundaries
}

Corrupting function:

.text:0000000000A78C90        movzx   r10d, byte ptr [rbx+rax]
.text:0000000000A78C95        lea     r11, [r11+3]
.text:0000000000A78C99        movzx   r9d, byte ptr [rdi+rax]
.text:0000000000A78C9E        movzx   r8d, byte ptr [rax]
.text:0000000000A78CA2        lea     rax, [rax+1]
.text:0000000000A78CA6        mov     ecx, [r13+r9*4+0]
.text:0000000000A78CAB        add     ecx, r10d
.text:0000000000A78CAE        lea     r8, ds:0[r8*4]
.text:0000000000A78CB6        movsxd  rcx, ecx
.text:0000000000A78CB9        movzx   edx, byte ptr [rcx+rbp]
.text:0000000000A78CBD        mov     [r11-5], dl
.text:0000000000A78CC1        mov     ecx, [r12+r9*4]
.text:0000000000A78CC5        add     ecx, [r8+r15]
.text:0000000000A78CC9        sar     ecx, 10h
.text:0000000000A78CCC        add     ecx, r10d
.text:0000000000A78CCF        movsxd  rcx, ecx
.text:0000000000A78CD2        movzx   edx, byte ptr [rcx+rbp]
.text:0000000000A78CD6        mov     [r11-4], dl
.text:0000000000A78CDA        mov     ecx, [r8+r14]
.text:0000000000A78CDE        add     ecx, r10d
.text:0000000000A78CE1        movsxd  rcx, ecx
.text:0000000000A78CE4        movzx   edx, byte ptr [rcx+rbp]
.text:0000000000A78CE8        mov     [r11-3], dl     
.text:0000000000A78CEC        sub     rsi, 1
.text:0000000000A78CF0        jnz     short loc_A78C90

Due to lack of bounds checking on the pointer calculation memory corruption may happen:

First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
Able2ExtractPro!FT_GlyphSlot_Own_Bitmap+0x10838:
00007ff7`50698ce8 418853fd        mov     byte ptr [r11-3],dl ds:000001c4`ae58d000=??

Crash Information

0:008> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************


KEY_VALUES_STRING: 1

    Key  : AV.Fault
    Value: Write

    Key  : Analysis.CPU.Sec
    Value: 1

    Key  : Analysis.Elapsed.Sec
    Value: 39

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 100

    Key  : Timeline.OS.Boot.DeltaSec
    Value: 647765

    Key  : Timeline.Process.Start.DeltaSec
    Value: 79


PROCESSES_ANALYSIS: 1

SERVICE_ANALYSIS: 1

STACKHASH_ANALYSIS: 1

TIMELINE_ANALYSIS: 1

Timeline: !analyze.Start
    Name: <blank>
    Time: 2019-07-17T07:28:30.753Z
    Diff: 753 mSec

Timeline: Dump.Current
    Name: <blank>
    Time: 2019-07-17T07:28:30.0Z
    Diff: 0 mSec

Timeline: Process.Start
    Name: <blank>
    Time: 2019-07-17T07:27:11.0Z
    Diff: 79000 mSec

Timeline: OS.Boot
    Name: <blank>
    Time: 2019-07-09T19:32:25.0Z
    Diff: 647765000 mSec


DUMP_CLASS: 2

DUMP_QUALIFIER: 0

MODLIST_WITH_TSCHKSUM_HASH:  55aa3f395ffa047b8d077574d76a15cab93ab6dd

MODLIST_SHA1_HASH:  1cf1413d65d9932e43acbd1b34685dcc05653e10

NTGLOBALFLAG:  70

APPLICATION_VERIFIER_FLAGS:  0

PRODUCT_TYPE:  1

SUITE_MASK:  272

DUMP_TYPE:  fe

FAULTING_IP: 
Able2ExtractPro!FT_GlyphSlot_Own_Bitmap+10838
00007ff7`50698ce8 418853fd        mov     byte ptr [r11-3],dl

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 00007ff750698ce8 (Able2ExtractPro!FT_GlyphSlot_Own_Bitmap+0x0000000000010838)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 0000000000000001
   Parameter[1]: 000001c4ae58d000
Attempt to write to address 000001c4ae58d000

FAULTING_THREAD:  00003b1c

DEFAULT_BUCKET_ID:  INVALID_POINTER_WRITE

PROCESS_NAME:  Able2ExtractPro.exe

FOLLOWUP_IP: 
Able2ExtractPro!FT_GlyphSlot_Own_Bitmap+10838
00007ff7`50698ce8 418853fd        mov     byte ptr [r11-3],dl

WRITE_ADDRESS:  000001c4ae58d000 

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  0000000000000001

EXCEPTION_PARAMETER2:  000001c4ae58d000

WATSON_BKT_PROCSTAMP:  5c2fb034

WATSON_BKT_PROCVER:  14.0.7.0

PROCESS_VER_PRODUCT:  Able2Extract Professional

WATSON_BKT_MODULE:  Able2ExtractPro.exe

WATSON_BKT_MODSTAMP:  5c2fb034

WATSON_BKT_MODOFFSET:  a68ce8

WATSON_BKT_MODVER:  14.0.7.0

MODULE_VER_PRODUCT:  Able2Extract Professional

BUILD_VERSION_STRING:  18362.1.amd64fre.19h1_release.190318-1202

ANALYSIS_SESSION_HOST:  CLAB

ANALYSIS_SESSION_TIME:  07-17-2019 09:28:30.0753

ANALYSIS_VERSION: 10.0.18914.1001 amd64fre

THREAD_ATTRIBUTES: 
OS_LOCALE:  ENU

BUGCHECK_STR:  APPLICATION_FAULT_INVALID_POINTER_WRITE

PRIMARY_PROBLEM_CLASS:  APPLICATION_FAULT

PROBLEM_CLASSES: 

    ID:     [0n313]
    Type:   [@ACCESS_VIOLATION]
    Class:  Addendum
    Scope:  BUCKET_ID
    Name:   Omit
    Data:   Omit
    PID:    [Unspecified]
    TID:    [0x3b1c]
    Frame:  [0] : Able2ExtractPro!FT_GlyphSlot_Own_Bitmap

    ID:     [0n286]
    Type:   [INVALID_POINTER_WRITE]
    Class:  Primary
    Scope:  DEFAULT_BUCKET_ID (Failure Bucket ID prefix)
            BUCKET_ID
    Name:   Add
    Data:   Omit
    PID:    [Unspecified]
    TID:    [0x3b1c]
    Frame:  [0] : Able2ExtractPro!FT_GlyphSlot_Own_Bitmap

LAST_CONTROL_TRANSFER:  from 00007ff75069807f to 00007ff750698ce8

STACK_TEXT:  
00000073`816fd140 00007ff7`5069807f : 000001c4`00006400 000001c4`85c0c288 00000000`00000001 00000073`816fd310 : Able2ExtractPro!FT_GlyphSlot_Own_Bitmap+0x10838
00000073`816fd1b0 00007ff7`5069237c : 00000073`00000000 00000073`816fd350 00000073`816fd3a0 00000073`816fd308 : Able2ExtractPro!FT_GlyphSlot_Own_Bitmap+0xfbcf
00000073`816fd210 00007ff7`50688e3e : 00000073`816fd350 00000073`816fd3a0 00000073`816fd308 00000000`00000001 : Able2ExtractPro!FT_GlyphSlot_Own_Bitmap+0x9ecc
00000073`816fd270 00007ff7`4fc7429d : 00000073`00000000 000001c4`86cf6040 00000073`816ff508 00000000`000003e9 : Able2ExtractPro!FT_GlyphSlot_Own_Bitmap+0x98e
00000073`816fd2a0 00007ff7`4fc7e401 : 00000000`000003e9 00000073`816fda50 00000073`816ff508 00000073`816fdc70 : Able2ExtractPro+0x4429d
00000073`816fd950 00007ff7`4fc804e0 : 000001c4`859e7c50 00000000`00000147 000001c4`859dada0 00000073`816ff508 : Able2ExtractPro+0x4e401
00000073`816fdbf0 00007ff7`4fc7fe1f : 00000073`816ff048 00000000`00000147 00000073`816ff4a8 00000073`816ff508 : Able2ExtractPro+0x504e0
00000073`816fef40 00007ff7`5056867c : 000001c4`84e2bc30 000001c4`00000000 000001c4`859ccc10 000001c4`859d3f20 : Able2ExtractPro+0x4fe1f
00000073`816ff1b0 00007ff7`50487c73 : 000001c4`859d3f00 00000000`00000000 000001c4`fcbaf920 000001c4`85b08570 : Able2ExtractPro!FT_Library_SetLcdFilter+0x23dd7c
00000073`816ff6e0 00007ff7`50487f43 : 000001c4`859d3f00 00000000`00000000 000001c4`859efb60 000001c4`859efb60 : Able2ExtractPro!FT_Library_SetLcdFilter+0x15d373
00000073`816ff740 00007ffc`c461aa9c : 000001c4`84e1b260 000001c4`859ef8c0 000001c4`859ee280 000001c4`84e391f0 : Able2ExtractPro!FT_Library_SetLcdFilter+0x15d643
00000073`816ff7c0 00007ffc`c445f31f : 000001c4`859def90 000001c4`00000003 000001c4`85a02a00 00000000`00000000 : Qt5Core!QMetaObject::activate+0x5dc
00000073`816ff8e0 00007ffc`c446831e : 000001c4`859ef790 000001c4`85a02a00 00000000`00000000 000001c4`85a02a00 : Qt5Core!QThread::started+0x2f
00000073`816ff920 00007ffd`25237bd4 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : Qt5Core!QThread::start+0x32e
00000073`816ff960 00007ffd`2592ce71 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
00000073`816ff990 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21


STACK_COMMAND:  ~8s ; .cxr ; kb

THREAD_SHA1_HASH_MOD_FUNC:  738e351021e0161a05dfd6f9615c733ddc590de8

THREAD_SHA1_HASH_MOD_FUNC_OFFSET:  e1ef57b9f997eee8d0308310a5149c9b1e51f7f4

THREAD_SHA1_HASH_MOD:  06b0e8a671dfa5a218d508aad027c54ac2c4af62

FAULT_INSTR_CODE:  fd538841

SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  Able2ExtractPro!FT_GlyphSlot_Own_Bitmap+10838

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: Able2ExtractPro

IMAGE_NAME:  Able2ExtractPro.exe

DEBUG_FLR_IMAGE_TIMESTAMP:  5c2fb034

FAILURE_BUCKET_ID:  INVALID_POINTER_WRITE_c0000005_Able2ExtractPro.exe!FT_GlyphSlot_Own_Bitmap

BUCKET_ID:  APPLICATION_FAULT_INVALID_POINTER_WRITE_Able2ExtractPro!FT_GlyphSlot_Own_Bitmap+10838

FAILURE_EXCEPTION_CODE:  c0000005

FAILURE_IMAGE_NAME:  Able2ExtractPro.exe

BUCKET_ID_IMAGE_STR:  Able2ExtractPro.exe

FAILURE_MODULE_NAME:  Able2ExtractPro

BUCKET_ID_MODULE_STR:  Able2ExtractPro

FAILURE_FUNCTION_NAME:  FT_GlyphSlot_Own_Bitmap

BUCKET_ID_FUNCTION_STR:  FT_GlyphSlot_Own_Bitmap

BUCKET_ID_OFFSET:  10838

BUCKET_ID_MODTIMEDATESTAMP:  5c2fb034

BUCKET_ID_MODCHECKSUM:  1667b6e

BUCKET_ID_MODVER_STR:  14.0.7.0

BUCKET_ID_PREFIX_STR:  APPLICATION_FAULT_INVALID_POINTER_WRITE_

FAILURE_PROBLEM_CLASS:  APPLICATION_FAULT

FAILURE_SYMBOL_NAME:  Able2ExtractPro.exe!FT_GlyphSlot_Own_Bitmap

TARGET_TIME:  2019-07-17T07:29:09.000Z

OSBUILD:  18362

OSSERVICEPACK:  86

SERVICEPACK_NUMBER: 0

OS_REVISION: 0

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 10

OSEDITION:  Windows 10 WinNt SingleUserTS

USER_LCID:  0

OSBUILD_TIMESTAMP:  unknown_date

BUILDDATESTAMP_STR:  190318-1202

BUILDLAB_STR:  19h1_release

BUILDOSVER_STR:  10.0.18362.1.amd64fre.19h1_release.190318-1202

ANALYSIS_SESSION_ELAPSED_TIME:  9896

ANALYSIS_SOURCE:  UM

FAILURE_ID_HASH_STRING:  um:invalid_pointer_write_c0000005_able2extractpro.exe!ft_glyphslot_own_bitmap

FAILURE_ID_HASH:  {ece3cde7-f090-e20c-b8b2-c7dfaa8d3e91}

Followup:     MachineOwner
---------

Timeline

2019-08-01 - Initial Contact
2019-08-06 - Plain text file issued; Vendor confirmed
2019-09-23 - 30+ day follow up; Vendor acknowledged issue under review
2019-10-15 - 60+ day follow up 2019-11-01 - Vendor patched
2019-11-04 - Public Release

Credit

Discovered by Piotr Bania of Cisco Talos.