Headline
CVE-2016-8388: TALOS-2016-0213 || Cisco Talos Intelligence Group
An exploitable arbitrary heap-overwrite vulnerability exists within Iceni Argus. When it attempts to convert a malformed PDF to XML, it will explicitly trust an index within the specific font object and use it to write the font’s name to a single object within an array of objects.
Summary
An exploitable arbitrary heap-overwrite vulnerability exists within Iceni Argus. When it attempts to convert a malformed PDF to XML, it will explicitly trust an index within the specific font object and use it to write the font’s name to a single object within an array of objects. Due to it explicitly trusting this index, one can specify an out-of-bounds index which will cause a pointer to a string to be written outside the bounds of the specified array. This can lead to code execution under the context of the account running it.
Tested Versions
Iceni Argus Version 6.6.04 (Sep 7 2012) NK
Product URLs
http://www.iceni.com/legacy.htm
CVSSv3 Score
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
Details
This is a heap-based arbitrary write vulnerability that occurs in Iceni Argus. This tool is used primarily by MarkLogic Server to convert PDF files to (X)HTML form. While attempting to adjust the glyphmap for a particular font embedded within a .PDF file, the tool will explicitly trust an index and use it to write a pointer to the font’s name outside the bounds of an array. Within the ipFontFromtDict function, the tool will allocate 0x17f4 bytes of space using icnChainAlloc, and write it to -0xdc(%ebp). Later the pointer returned will be passed to ipFontInstallEncoding.
809e887: 8b 95 1c ff ff ff mov -0xe4(%ebp),%edx
809e88d: c7 44 24 04 f4 17 00 movl $0x17f4,0x4(%esp)
809e894: 00
809e895: 8b 82 34 02 00 00 mov 0x234(%edx),%eax
809e89b: 89 04 24 mov %eax,(%esp)
809e89e: e8 ed 48 fc ff call 8063190 <icnChainAlloc>
809e8a3: 85 c0 test %eax,%eax
809e8a5: 89 85 24 ff ff ff mov %eax,-0xdc(%ebp) ; allocated pointer
809e8ab: 0f 84 9f 05 00 00 je 809ee50 <ipFontFromDict+0x780>
...
809ec93: 8b 85 24 ff ff ff mov -0xdc(%ebp),%eax ; allocated pointer
809ec99: 8b 95 1c ff ff ff mov -0xe4(%ebp),%edx
809ec9f: 89 7c 24 08 mov %edi,0x8(%esp)
809eca3: 89 44 24 04 mov %eax,0x4(%esp) ; target
809eca7: 89 14 24 mov %edx,(%esp)
809ecaa: e8 e1 ee ff ff call 809db90 <ipFontInstallEncoding>
809ecaf: 85 c0 test %eax,%eax
809ecb1: 0f 85 99 01 00 00 jne 809ee50 <ipFontFromDict+0x780>
Inside the ipFontInstallEncoding, the tool will take the pointer passed to it and add 0x10. Afterwards this resulting pointer will then be stored in -0x30(%ebp).
809dd7b: 8b 4d 0c mov 0xc(%ebp),%ecx
809dd7e: 83 c1 10 add $0x10,%ecx
809dd81: 89 4d d0 mov %ecx,-0x30(%ebp)
Within the same function the tool will search through a dictionary for a value with the key of “Differences”. This returns the pointer to an object that contains the array from the file containing the bad index. This object is stored in -0x20(%ebp).
809dd96: 8d 83 23 71 44 ff lea -0xbb8edd(%ebx),%eax ; "Differences"
809dd9c: c7 44 24 08 07 00 00 movl $0x7,0x8(%esp)
809dda3: 00
809dda4: 89 44 24 04 mov %eax,0x4(%esp)
809dda8: 89 34 24 mov %esi,(%esp)
809ddab: e8 70 5b ff ff call 8093920 <ipDictFindType>
809ddb0: 85 c0 test %eax,%eax
809ddb2: 89 45 e0 mov %eax,-0x20(%ebp) ; XXX: object
809ddb5: 0f 84 a4 05 00 00 je 809e35f <ipFontInstallEncoding+0x7cf>
Afterwards the object containing the index that is trusted is passed to ipGlyphMapAdjust. This function will iterate through the array that is within the object that was grabbed from the “Differences” dictionary.
809ddca: 8b 4d e0 mov -0x20(%ebp),%ecx ; XXX: object with index
809ddcd: 8b 41 04 mov 0x4(%ecx),%eax
809ddd0: 89 44 24 08 mov %eax,0x8(%esp) ; object
809ddd4: 8b 7d d0 mov -0x30(%ebp),%edi
809ddd7: 89 7c 24 04 mov %edi,0x4(%esp)
809dddb: 8b 45 0c mov 0xc(%ebp),%eax ; destination
809ddde: 89 04 24 mov %eax,(%esp)
809dde1: e8 ea d1 01 00 call 80bafd0 <ipGlyphMapAdjust>
809dde6: 8b 55 0c mov 0xc(%ebp),%edx
809dde9: 88 42 03 mov %al,0x3(%edx)
Once inside the ipGlyphMapAdjust function, the following loop will iterate through every glyph inside the object grabbed from the dictionary. Once inside this loop, the tool will compare an index that is grabbed to ensure it’s less than a maximum value. Due to a signedness issue, this check will pass. With the provided sample the bad index that is read out of the object is 0xedffffff. This index will get stored into the %edi register.
80bb023: 8b 45 10 mov 0x10(%ebp),%eax ; object
80bb026: 8d 34 d5 00 00 00 00 lea 0x0(,%edx,8),%esi ; grab index from object
80bb02d: 89 55 c8 mov %edx,-0x38(%ebp) ; store index
...
80bb03b: 83 c2 01 add $0x1,%edx
80bb03e: 8b 78 04 mov 0x4(%eax),%edi ; XXX: reads bad index into %edi
80bb041: 39 55 d0 cmp %edx,-0x30(%ebp) ; max
80bb044: 89 55 c8 mov %edx,-0x38(%ebp) ; index
80bb047: 7e ce jle 80bb017 <ipGlyphMapAdjust+0x47>
To determine the glyph’s name which will get written, the following code gets executed. This first calls ipNameToStr, followed by a call to glyphName. The resulting pointer is written to -0x24(%ebp).
80bb09e: 8b 40 04 mov 0x4(%eax),%eax
80bb0a1: 89 04 24 mov %eax,(%esp)
80bb0a4: e8 b7 2c 01 00 call 80cdd60 <ipNameToStr>
80bb0a9: 89 c6 mov %eax,%esi
80bb0ab: 89 04 24 mov %eax,(%esp) ; key
80bb0ae: e8 6d fe ff ff call 80baf20 <glyphName>
80bb0b3: 39 c6 cmp %eax,%esi
80bb0b5: 89 45 dc mov %eax,-0x24(%ebp) ; string name
After grabbing the name, the index that is stored is then checked if it’s larger or smaller. There is a signed-ness check here for both bounds, however the index is not modified and instead used at 0x80bb076 to write the glyph name too. Due to none of the checks modifying the %edi register, this value is explicitly multiplied by 4 and trusted to index into the destination array passed as the second argument. This can allow for an aggressor to write a pointer to a string at an arbitrary place in memory which can lead to memory corruption. Under the correct circumstances this can lead to code execution.
80bb060: 39 7d d4 cmp %edi,-0x2c(%ebp) ; XXX: check index in %edi is less than one in lvar
80bb063: 7e 03 jle 80bb068 <ipGlyphMapAdjust+0x98>
80bb065: 89 7d d4 mov %edi,-0x2c(%ebp) ; XXX: write to lvar if so
80bb068: 39 7d d8 cmp %edi,-0x28(%ebp) ; XXX: check index in %edi is larger than one in lvar
80bb06b: 7d 03 jge 80bb070 <ipGlyphMapAdjust+0xa0>
80bb06d: 89 7d d8 mov %edi,-0x28(%ebp) ; XXX: write to lvar if not
80bb070: 8b 45 dc mov -0x24(%ebp),%eax ; glyph name
80bb073: 8b 55 0c mov 0xc(%ebp),%edx ; destination
80bb076: 89 04 ba mov %eax,(%edx,%edi,4) ; XXX: use %edi as index
Crash Information
$ gdb --quiet --args /opt/MarkLogic/converters/cvtpdf/convert ~/config/
Reading symbols from /opt/MarkLogic/Converters/cvtpdf/convert...done.
(gdb) r
Starting program: /opt/MarkLogic/Converters/cvtpdf/convert /home/user/config/
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Loading configuration...
Parsing macros...
Macro synth-bookmarks='true'
Macro image-output='true'
Macro text-output='true'
Macro zones='false'
Macro ignore-text='true'
Macro remove-overprint='false'
Macro illustrations='true'
Macro line-breaks='true'
Macro image-quality='75'
Macro page-start=''
Macro page-end=''
Macro document-start=''
Macro document-end=''
features='11140221'
Processing...
Analysing '/home/user/poc.pdf'
Pages 1 to 1
Catchpoint 4 (signal SIGSEGV), 0x080bb076 in ipGlyphMapAdjust ()
(gdb) bt 5
#0 0x080bb076 in ipGlyphMapAdjust ()
#1 0x0809dde6 in ipFontInstallEncoding ()
#2 0x0809ecaf in ipFontFromDict ()
#3 0x080b5000 in ipfSetFontNameSize ()
#4 0x080e7ee2 in ipDocExecStack ()
(More stack frames follow...)
(gdb) h
-=[registers]=-
[eax: 0x083f7a44] [ebx: 0x08f57000] [ecx: 0x00000000] [edx: 0x09904e24]
[esi: 0x098af25c] [edi: 0xe3ffffff] [esp: 0xfffc0050] [ebp: 0xfffc0098]
[eflags: NZ SF OF CF ND NI]
-=[stack]=-
fffc0050 | 098af25c 0995e5f0 098a857c 083eb488 | \.......|.....>.
fffc0060 | 0000001e 00000000 00000025 e3ffffff | ........%.......
fffc0070 | 0000001b 083f7a44 000000f8 0809396f | ....Dz?.....o9..
fffc0080 | 0990694c 098ad3f4 00000100 08f57000 | Li...........p..
-=[disassembly]=-
=> 0x80bb076 <ipGlyphMapAdjust+166>: mov %eax,(%edx,%edi,4)
0x80bb079 <ipGlyphMapAdjust+169>: add $0x1,%edi
0x80bb07c <ipGlyphMapAdjust+172>: addl $0x1,-0x38(%ebp)
0x80bb080 <ipGlyphMapAdjust+176>: mov -0x38(%ebp),%edx
0x80bb083 <ipGlyphMapAdjust+179>: cmp %edx,-0x30(%ebp)
0x80bb086 <ipGlyphMapAdjust+182>: jle 0x80bb017 <ipGlyphMapAdjust+71>
Timeline
2016-10-10 - Vendor Disclosure
2017-02-27 - Public Release
Discovered by Marcin Noga of Cisco Talos.