Talos Vulnerability Report

TALOS-2018-0598

Antenna House Office Server Document Converter putShapeProperty Code Execution Vulnerability

July 10, 2018
CVE Number

CVE-2018-3931

Summary

An exploitable out-of-bounds write exists in the Microsoft Word document conversion functionality of the Antenna House Office Server Document Converter version V6.1 Pro MR2 for Linux64 (6,1,2018,0312). A crafted Microsoft Word (DOC) document can lead to an out-of-bounds write, resulting in remote code execution. This vulnerability occurs in the putShapeProperty method.

Tested Versions

Office Server Document Converter version V6.1 Pro MR2 for Linux64 (6,1,2018,0312)

Product URLs

https://www.rainbowpdf.com/batch-office-server-document-converter/

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-121: Stack-based Buffer Overflow

Details

This vulnerability is present in the Antenna House Office Server Document Converter, which is used as a document converter in many server enterprise solutions.
It can convert common formats such as Microsoft’s document formats into more usable and easily viewed formats. There is a vulnerability in the conversion process of a DOC to PDF, JPEG and several other formats. A specially crafted Microsoft Word file can lead to a stack-based buffer overflow and remote code execution. Let’s investigate this vulnerability. After we attempt to convert a malicious Microsoft Word document using the OSDC library we see the following state:

icewall@ubuntu:/usr/OfficeServerDocumentConverter$ valgrind bin/SBCCmd -d ./crashes/c655022e6c8b72d95983b99a6b888ccf -p @PDF -o /tmp/z.pdf
==6946== Memcheck, a memory error detector
==6946== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6946== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==6946== Command: bin/SBCCmd -d ./crashes/c655022e6c8b72d95983b99a6b888ccf -p @PDF -o /tmp/z.pdf
==6946== 
SBCCmd : Office Server Document Converter V6.1 Pro MR2 for Linux64 (6,1,2018,0312) 
		 Copyright (c) 1999-2018 Antenna House, Inc.

 ---------------------------------------
 This is an EVALUATION version.
 Prohibits the use of evaluation version
 for the real business activity.
 Expire Date : Jun 06, 2018
 ---------------------------------------

==6946== Invalid write of size 1
==6946==    at 0x4C30C30: strcat (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6946==    by 0xB51D81D: DfvDocReaderNS::DocAutoShape::putShapeProperty(OleCompNS::AHOleCompStream*, OleCompNS::AHOleCompStream*, AHCommonNS::AHPtr<DfvCommon::WordMLDocument>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>) (in /usr/OfficeServerDocumentConverter/lib/libDfvDocReader.so.6.1)
==6946==    by 0x2320663131353B2F: ???
==6946==    by 0x393B30303030302F: ???
==6946==    by 0x3030303830232065: ???
==6946==    by 0x6633232066313B33: ???
==6946==    by 0x3535363B3130332F: ???
==6946==    by 0x6631312320663632: ???
==6946==    by 0x383437333B34302F: ???
==6946==    by 0x3023206632393535: ???
==6946==    by 0x37363B303030302F: ???
==6946==    by 0x2066363339393331: ???
==6946==  Address 0xfff001000 is not stack'd, malloc'd or (recently) free'd
==6946== 
==6946== 
==6946== Process terminating with default action of signal 11 (SIGSEGV)
==6946==  Access not within mapped region at address 0xFFF001000
==6946==    at 0x4C30C30: strcat (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6946==    by 0xB51D81D: DfvDocReaderNS::DocAutoShape::putShapeProperty(OleCompNS::AHOleCompStream*, OleCompNS::AHOleCompStream*, AHCommonNS::AHPtr<DfvCommon::WordMLDocument>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>) (in /usr/OfficeServerDocumentConverter/lib/libDfvDocReader.so.6.1)
==6946==    by 0x2320663131353B2F: ???
==6946==    by 0x393B30303030302F: ???
==6946==    by 0x3030303830232065: ???
==6946==    by 0x6633232066313B33: ???
==6946==    by 0x3535363B3130332F: ???
==6946==    by 0x6631312320663632: ???
==6946==    by 0x383437333B34302F: ???
==6946==    by 0x3023206632393535: ???
==6946==    by 0x37363B303030302F: ???
==6946==    by 0x2066363339393331: ???

As we can see, a stack-based buffer overflow appeared during the strcat operation. Let’s take a look at the most important parts of a pseudo code representation of the putShapeProperty function where the strcat operation takes place:

Line 1 	__int64 __fastcall DfvDocReaderNS::DocAutoShape::putShapeProperty(struct_v8 *a1, _QWORD *a2, __int64 *a3, _QWORD *a4, __int64 a5, _QWORD *a6, struct _Unwind_Exception *a7)
Line 2 	{
Line 3 		(...)
Line 4 		srcBuffer = v8->_srcBuffer;
Line 5 		memset(dstBuffer, 0, 0x200uLL);
Line 6 		nElements = (v8->endSrcBuffer - (_QWORD)srcBuffer) >> 3;
Line 7 		if ( nElements )
Line 8 		{
Line 9 		  offset = 0LL;
Line 10		  index = 0;
Line 11		  if ( (signed int)nElements > 0 )
Line 12		  {
Line 13			do
Line 14			{
Line 15			  sprintf(
Line 16				(char *)&tmpBuffer,
Line 17				"%df #%02x%02x%02x",
Line 18				*(unsigned int *)&srcBuffer[offset + 4],
Line 19				srcBuffer[offset],
Line 20				srcBuffer[offset + 1],
Line 21				srcBuffer[offset + 2]);
Line 22			  if ( v8->dwordC74 - 1 > index )
Line 23				*(_WORD *)((char *)&tmpBuffer + strlen((const char *)&tmpBuffer)) = ';';
Line 24			  ++index;
Line 25			  strcat(dstBuffer, (const char *)&tmpBuffer);
Line 26			  srcBuffer = v8->_srcBuffer;
Line 27			  offset += 8LL;
Line 28			}
Line 29			while ( index < nElements );
Line 30		  }

As we can see, dst buffer is a fixed sized buffer of 0x200 bytes. Each time the while loop is executed, it adds 16 bytes to the buffer. The nElements variable which is the limits for the while loop in our case equals 0x3cb0. It’s obvious that in that situation, a stack-based buffer overflow will occur after a few iterations. Tracking the source of the nElements variable value and the srcBuffer content we land in GetShapePropery method:

Line 1 	case 0x197:
Line 2 	  OLEread)(v8, &someStruct, 6LL)
Line 3 	  bufStart = v7->imStruct.bufferStart;
Line 4 	  nElements = (signed __int16)someStruct.wSize;
Line 5 	  v7->dword19C4 = (signed __int16)__amount;
Line 6 	  v7->imStruct.bufferEnd = bufStart;
Line 7 	  if ( nElements > 0 )
Line 8 	  {
Line 9 		_nElements = nElements + 1;
Line 10		counter = 1;
Line 11		do
Line 12		{
Line 13		  OLEread(&intField,4LL);
Line 14		  element8Bytes = intField;
Line 15		  OLEread(&intField,4LL);
Line 16		  HIDWORD(element8Bytes) = (BYTE1(intField) << 8) | (unsigned __int8)intField | (BYTE2(intField) << 16) | (HIBYTE(intField) << 24);
Line 17		  _bufEnd = (_QWORD *)v7->imStruct.bufferEnd;
Line 18		  if ( _bufEnd == (_QWORD *)v7->imStruct.capacityEnd )
Line 19		  {
Line 20			std::vector<DfvDocReaderNS::COLORS,std::allocator<DfvDocReaderNS::COLORS>>::_M_emplace_back_aux<DfvDocReaderNS::COLORS const&>(	&v7->imStruct, &element8Bytes);
Line 21		  }
Line 22		  else
Line 23		  {
Line 24			if ( _bufEnd )
Line 25			{
Line 26			  *_bufEnd = element8Bytes;
Line 27			  v78 = v7->imStruct.bufferEnd;
Line 28			}
Line 29			else
Line 30			{
Line 31			  v78 = 0LL;
Line 32			}
Line 33			v7->imStruct.bufferEnd = v78 + 8;
Line 34		  }
Line 35		  ++counter;
Line 36		}
Line 37		while ( counter != _nElements );	

As you can see, the nElements variable value is read directly from the file at line 4. Next, during each iteration, eight bytes are read from the file and put into the buffer. This means that attackers fully control the amount of the bytes used for overflow and their content. In those circumstances, attackers using a properly malformed Microsoft Word document can overwrite the function return address and turn that into remote code execution.

Crash Information

Starting program: /usr/OfficeServerDocumentConverter/bin/SBCCmd -d ./crashes/c655022e6c8b72d95983b99a6b888ccf -p @PDF -o /tmp/z.pdf
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
SBCCmd : Office Server Document Converter V6.1 Pro MR2 for Linux64 (6,1,2018,0312) 
		 Copyright (c) 1999-2018 Antenna House, Inc.

 ---------------------------------------
 This is an EVALUATION version.
 Prohibits the use of evaluation version
 for the real business activity.
 Expire Date : Jun 06, 2018
 ---------------------------------------


Program received signal SIGSEGV, Segmentation fault.
__strcat_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:698
698     ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S: No such file or directory.
(gdb) bt 10
#0  __strcat_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:698
#1  0x00007ffff147481e in DfvDocReaderNS::DocAutoShape::putShapeProperty(OleCompNS::AHOleCompStream*, OleCompNS::AHOleCompStream*, AHCommonNS::AHPtr<DfvCommon::WordMLDocument>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>) () from /usr/OfficeServerDocumentConverter/lib/libDfvDocReader.so.6
#2  0x2320663131353b30 in ?? ()
#3  0x393b303030303030 in ?? ()
#4  0x3030303830232066 in ?? ()
#5  0x6633232066313b34 in ?? ()
#6  0x3535363b31303330 in ?? ()
#7  0x6631312320663633 in ?? ()
#8  0x383437333b343030 in ?? ()
#9  0x3023206632393536 in ?? ()
(More stack frames follow...)
(gdb) exploitable_active 
(gdb) exploitable -m
__main__:102: UserWarning: GDB v7.11 may not support required Python API
Warning: machine string printing is deprecated and may be removed in a future release.
EXCEPTION_FAULTING_ADDRESS:0x007ffffffff000
EXCEPTION_CODE:11
FAULTING_INSTRUCTION:mov    QWORD PTR [rdi],rcx
MAJOR_HASH:050f54e536817f4a46022150b961d22d
MINOR_HASH:1f4d670efa0d23394265fc1438272eb9
STACK_DEPTH:1000
STACK_FRAME:/lib/x86_64-linux-gnu/libc-2.23.so!__strcat_sse2_unaligned+0x0
STACK_FRAME:/usr/OfficeServerDocumentConverter/lib/libDfvDocReader.so.6.1!DfvDocReaderNS::DocAutoShape::putShapeProperty(OleCompNS::AHOleCompStream*, OleCompNS::AHOleCompStream*, AHCommonNS::AHPtr<DfvCommon::WordMLDocument>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>, AHCommonNS::AHPtr<DfvCommon::WordMLElement>)+0x0
STACK_FRAME:Unknown+0x0
STACK_FRAME:Unknown+0x0
STACK_FRAME:Unknown+0x0
STACK_FRAME:Unknown+0x0
STACK_FRAME:Unknown+0x0
STACK_FRAME:Unknown+0x0
(...)
INSTRUCTION_ADDRESS:0x007fffec8bcec6
INVOKING_STACK_FRAME:1
DESCRIPTION:Possible stack corruption
SHORT_DESCRIPTION:PossibleStackCorruption (8/29)
OTHER_RULES:AccessViolation (28/29)
CLASSIFICATION:EXPLOITABLE
EXPLANATION:GDB generated an error while unwinding the stack and/or the stack contained return addresses that were not mapped in the inferior's process address space and/or the stack pointer is pointing to a location outside the default stack region. These conditions likely indicate stack corruption, which is generally considered exploitable.
Description: Possible stack corruption
Short description: PossibleStackCorruption (8/29)
Hash: 050f54e536817f4a46022150b961d22d.1f4d670efa0d23394265fc1438272eb9
Exploitability Classification: EXPLOITABLE
Explanation: GDB generated an error while unwinding the stack and/or the stack contained return addresses that were not mapped in the inferior's process address space and/or the stack pointer is pointing to a location outside the default stack region. These conditions likely indicate stack corruption, which is generally considered exploitable.
Other tags: AccessViolation (28/29)

Timeline

2018-05-21 - Vendor Disclosure
2018-07-10 - Public Release

Credit

Discovered by Marcin 'Icewall' Noga of Cisco Talos.