CVE-2018-3931
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.
Office Server Document Converter version V6.1 Pro MR2 for Linux64 (6,1,2018,0312)
https://www.rainbowpdf.com/batch-office-server-document-converter/
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-121: Stack-based Buffer Overflow
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.
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)
2018-05-21 - Vendor Disclosure
2018-07-10 - Public Release
Discovered by Marcin 'Icewall' Noga of Cisco Talos.