Talos Vulnerability Report

TALOS-2016-0214

Iceni Argus PDF TextToPolys Rasterization Code Execution Vulnerability

February 27, 2017
CVE Number

CVE-2016-8389

Summary

An exploitable integer-overflow vulnerability exists within Iceni Argus. When it attempts to convert a malformed PDF to XML, it will attempt to convert each character from a font into a polygon and then attempt to rasterize these shapes. When rasterizing these shapes, the tool will perform a multiplication to determine the bounds at which the shape can be filled as well as use it to perform an allocation. Due to a lack of bounds checking, this multiplication can result in an integer larger than 32-bits which is an integer overflow. As the application attempts to iterate through the rows and initializing the polygon shape in the buffer, it will write outside of the bounds of said buffer. 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 an integer overflow that occurs in Iceni Argus. This tool is used primarily by MarkLogic Server to convert PDF files to (X)HTML form. Due to the integer overflow, two different lengths will be used for an allocation and for a copy. The object that contains the objects with the sizes which are used in the overflow is created in the ripPDF function. Inside this function an object of size 0xadf8 is allocated.

 8175f06:	c7 04 24 f8 ad 00 00 	movl   $0xadf8,(%esp)
 8175f0d:	89 85 d8 f9 ff ff    	mov    %eax,-0x628(%ebp)        ; size
 8175f13:	8d 83 dd f1 49 ff    	lea    -0xb60e23(%ebx),%eax     ; "RENDER"
 8175f19:	89 44 24 04          	mov    %eax,0x4(%esp)
 8175f1d:	e8 4e b6 02 00       	call   81a1570 <icnMalloc>
 8175f22:	85 c0                	test   %eax,%eax
 8175f24:	89 c7                	mov    %eax,%edi
 8175f26:	0f 84 be 03 00 00    	je     81762ea <ripPDF+0x40a>

For every single text item inside the document that uses the showTextOld opcode, the showTextOld function is called. Inside this function the addFontToStoredFonts function will copy a pointer from the +4 field (+0xaa0c:4) into the +8 field (+0xaa0c:8) of the 0xadf8 sized object.

 817888c:	8b 45 e4             	mov    -0x1c(%ebp),%eax
 817888f:	89 44 24 14          	mov    %eax,0x14(%esp)
 8178893:	8b 8d ec fd ff ff    	mov    -0x214(%ebp),%ecx
 8178899:	89 4c 24 10          	mov    %ecx,0x10(%esp)
 817889d:	8b 45 08             	mov    0x8(%ebp),%eax       ; 0xadf8 object
 81788a0:	89 44 24 0c          	mov    %eax,0xc(%esp)
 81788a4:	8b 55 18             	mov    0x18(%ebp),%edx
 81788a7:	89 54 24 08          	mov    %edx,0x8(%esp)
 81788ab:	8b 4d 0c             	mov    0xc(%ebp),%ecx
 81788ae:	89 4c 24 04          	mov    %ecx,0x4(%esp)
 81788b2:	8b 45 14             	mov    0x14(%ebp),%eax
 81788b5:	89 04 24             	mov    %eax,(%esp)
 81788b8:	e8 63 26 15 00       	call   82caf20 <addFontToStoredFonts>
\
 82cb0fa:	8b 45 14             	mov    0x14(%ebp),%eax
 82cb0fd:	8b 90 0c aa 00 00    	mov    0xaa0c(%eax),%edx    ; +0xaa0c
 82cb103:	8b 45 1c             	mov    0x1c(%ebp),%eax
 82cb106:	8b 4a 04             	mov    0x4(%edx),%ecx       ; +0xaa0c:4 to %ecx
 82cb109:	89 81 d4 00 00 00    	mov    %eax,0xd4(%ecx)
 82cb10f:	8b 02                	mov    (%edx),%eax
 82cb111:	89 4a 08             	mov    %ecx,0x8(%edx)       ; write to +0xaa0c:8
 82cb114:	85 c0                	test   %eax,%eax
 82cb116:	74 08                	je     82cb120 <addFontToStoredFonts+0x200>

For each character, the tool will processor a character, convert it into a TextPath and then convert it into a polygon. After a polygon is completed, the tool will call enumPolys to fill each of the polygons. While calling enumPolys the application will execute the following code in processText. This code will take the product of the dword at 0x108(%esi) and the one at 0x10c(%esi) and store it in %edi at 0x821432d. Later this register will be passed as an argument to icnMalloc at 0x821434d which will allocate space that will later be written to. Due to a missing check that the product of these two values are not larger than 31-bits, the resulting product may wrap which will cause the allocation size to be smaller than expected. The buffer that’s allocated will then be written to +0x108 (+0xaa0c:8:108) of the 0xadf8 object. These lengths will later be used as boundaries for rasterizing a polygon.

 8214fc5:	c7 04 24 00 00 00 00 	movl   $0x0,(%esp)
 8214fcc:	8b 55 08             	mov    0x8(%ebp),%edx
 8214fcf:	89 f1                	mov    %esi,%ecx
 8214fd1:	8b 45 14             	mov    0x14(%ebp),%eax
 8214fd4:	e8 f7 eb ff ff       	call   8213bd0 <processText>    ; \
\
 8213c9d:	8b 40 08             	mov    0x8(%eax),%eax
 8213ca0:	85 f6                	test   %esi,%esi
 8213ca2:	89 45 e0             	mov    %eax,-0x20(%ebp)
...
 8214317:	6b 45 dc 54          	imul   $0x54,-0x24(%ebp),%eax
 821431b:	8b 55 e0             	mov    -0x20(%ebp),%edx
 821431e:	8d 34 10             	lea    (%eax,%edx,1),%esi
 8214321:	8b 86 08 01 00 00    	mov    0x108(%esi),%eax
 8214327:	8b be 10 01 00 00    	mov    0x110(%esi),%edi     ; XXX: size > 16-bits
 821432d:	0f af be 0c 01 00 00 	imul   0x10c(%esi),%edi     ; XXX: size > 16-bits
 8214334:	85 c0                	test   %eax,%eax
 8214336:	74 08                	je     8214340 <processText+0x770>
...
 8214340:	8d 83 3f 21 4a ff    	lea    -0xb5dec1(%ebx),%eax
 8214346:	89 44 24 04          	mov    %eax,0x4(%esp)       ; XXX: allocation size
 821434a:	89 3c 24             	mov    %edi,(%esp)
 821434d:	e8 1e d2 f8 ff       	call   81a1570 <icnMalloc>
 8214352:	85 c0                	test   %eax,%eax
 8214354:	89 86 08 01 00 00    	mov    %eax,0x108(%esi)

Later while filling a polygon, the tool will load these values into the floating point co-processor to perform some math. Afterwards, the instructions will store the results as integers into variables on the stack at -0x98(%ebp) and -0xa0(%ebp). The first Y index will also be stored in integer form at -0x14(%ebp) and within the %esi register.

 82b0fff:	d9 07                	flds   (%edi)
 82b1001:	d9 95 5c ff ff ff    	fsts   -0xa4(%ebp)      ; X
 82b1007:	d9 47 10             	flds   0x10(%edi)
 82b100a:	d9 7d f2             	fnstcw -0xe(%ebp)       ; fpcw
 82b100d:	d9 95 60 ff ff ff    	fsts   -0xa0(%ebp)      ; Y
 82b1013:	d9 c9                	fxch   %st(1)
 82b1015:	d8 83 28 2a 44 ff    	fadds  -0xbbd5d8(%ebx)
 82b101b:	0f b7 45 f2          	movzwl -0xe(%ebp),%eax  ; fpcw
 82b101f:	b4 0c                	mov    $0xc,%ah
 82b1021:	66 89 45 f0          	mov    %ax,-0x10(%ebp)
 82b1025:	d9 6d f0             	fldcw  -0x10(%ebp)
 82b1028:	db 5d ec             	fistpl -0x14(%ebp)      ; start Y (integer)
 82b102b:	d9 6d f2             	fldcw  -0xe(%ebp)       ; fpcw
 82b102e:	8b 75 ec             	mov    -0x14(%ebp),%esi ; start Y (integer)
 82b1031:	d9 6d f0             	fldcw  -0x10(%ebp)
 82b1034:	db 9d 68 ff ff ff    	fistpl -0x98(%ebp)      ; X
 82b103a:	d9 6d f2             	fldcw  -0xe(%ebp)       ; fpcw

Aftewards, the code will enter a loop that will iterate through using these values. Due to the only the product of these integers overflowing, the call to drawPixel will be called for every pixel inside the under-allocated buffer as defined by the integers stored in -0x38(%ebp) and -0xa4(%ebp). The drawPixel function will essentially wrap setCharMapPixel which actually performs the write..

 82b10a2:	89 44 24 0c          	mov    %eax,0xc(%esp)
 82b10a6:	8b 55 c8             	mov    -0x38(%ebp),%edx     ; XXX: Y larger than 16-bits
 82b10a9:	89 54 24 08          	mov    %edx,0x8(%esp)
 82b10ad:	d9 85 5c ff ff ff    	flds   -0xa4(%ebp)          ; XXX: X larger than 16-bits
 82b10b3:	d9 6d f0             	fldcw  -0x10(%ebp)
 82b10b6:	db 5c 24 04          	fistpl 0x4(%esp)            ; X
 82b10ba:	d9 6d f2             	fldcw  -0xe(%ebp)
 82b10bd:	8b 45 08             	mov    0x8(%ebp),%eax
 82b10c0:	89 04 24             	mov    %eax,(%esp)          ; object
 82b10c3:	e8 18 d5 00 00       	call   82be5e0 <drawPixel>

Within setCharMapPixel the application will check to see if the current values are within the correct bounds, and then later use these values to write a null byte relative to the buffer at +0xaa0c:8:108. Due to the buffer being undersized while the tool attempts to fill the rasterized polygon, a buffer overflow may occur.

 82becc6:	8b 55 08             	mov    0x8(%ebp),%edx
 82becc9:	8b 4d 10             	mov    0x10(%ebp),%ecx      ; XXX: y value larger than 16-bits
 82beccc:	89 34 24             	mov    %esi,(%esp)
 82beccf:	8b 75 0c             	mov    0xc(%ebp),%esi       ; XXX: x value larger than 16-bits
 82becd2:	89 7c 24 04          	mov    %edi,0x4(%esp)
 82becd6:	8b 82 0c aa 00 00    	mov    0xaa0c(%edx),%eax    ; XXX: pointer to object (+0xaa0c)
 82becdc:	85 c9                	test   %ecx,%ecx
 82becde:	8b 78 08             	mov    0x8(%eax),%edi       ; XXX: pointer to object (+0xaa0c:8)
 82bece1:	0f b7 82 1c aa 00 00 	movzwl 0xaa1c(%edx),%eax
 82bece8:	78 2b                	js     82bed15 <setCharMapPixel+0x55>
 82becea:	0f b7 c0             	movzwl %ax,%eax
...
 82becf0:	01 f8                	add    %edi,%eax            ; XXX: base
...
 82bed08:	0f af ca             	imul   %edx,%ecx
 82bed0b:	03 b0 08 01 00 00    	add    0x108(%eax),%esi     ; XXX: under-allocated buffer
 82bed11:	c6 04 0e 00          	movb   $0x0,(%esi,%ecx,1)   ; XXX: write \x00 into buffer

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 2
Processing page 1

Catchpoint 4 (signal SIGSEGV), 0x082bed11 in setCharMapPixel ()


(gdb) bt 10


#0  0x082bed11 in setCharMapPixel ()
#1  0x082be68e in drawPixel ()
#2  0x082b10c8 in rendFillPoly ()
#3  0x08176fd5 in fillPoly ()
#4  0x082154c9 in enumPolys ()
#5  0x082cb837 in convertTextPathToPolys ()
#6  0x0817b351 in convertPathToPolys ()
#7  0x082cc5e1 in ProcessCharacter ()
#8  0x08179993 in showTextOld ()
#9  0x08179b39 in showText ()
#10 0x08183fb2 in processTextItem ()
#11 0x0818497d in icnProcessDisplayListX ()
#12 0x081856c0 in icnProcessDisplayList ()
#13 0x0818605d in icnRenderPage ()
#14 0x0817635d in ripPDF ()
#15 0x0817576e in renderZone ()
(More stack frames follow...)


(gdb) h


-=[registers]=-
[eax: 0xf7237e40] [ebx: 0x08f57000] [ecx: 0xfffdaab0] [edx: 0x000269c2]
[esi: 0x9f23a8e9] [edi: 0xf7237e40] [esp: 0xfffbe820] [ebp: 0xfffbe828]
[eflags: NZ SF OF NC ND NI]

-=[stack]=-
fffbe820 | 0001a858 098ddfc0 fffbe858 082be68e | X.......X.....+.
fffbe830 | 098ddfc0 000238e1 0001a858 fffbeb10 | .....8..X.......
fffbe840 | 00000010 57fe85a8 f7200040 08f57000 | .......W@. ..p..
fffbe850 | 000238e2 f7236628 fffbe958 082b10c8 | .8..(f#.X.....+.

-=[disassembly]=-
=> 0x82bed11 <setCharMapPixel+81>:      movb   $0x0,(%esi,%ecx,1)
   0x82bed15 <setCharMapPixel+85>:      mov    (%esp),%esi
   0x82bed18 <setCharMapPixel+88>:      mov    0x4(%esp),%edi
   0x82bed1c <setCharMapPixel+92>:      mov    %ebp,%esp
   0x82bed1e <setCharMapPixel+94>:      pop    %ebp
   0x82bed1f <setCharMapPixel+95>:      ret

Timeline

2016-10-10 - Vendor Disclosure
2017-02-27 - Public Release

Credit

Discovered by Marcin Noga of Cisco Talos.