CVE-2019-5089
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.
Investintech Able2Extract Professional 14.0.7 x64
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-190: Integer Overflow or Wraparound
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=??
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
---------
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
Discovered by Piotr Bania of Cisco Talos.