Talos Vulnerability Report

TALOS-2017-0295

Apache OpenOffice DOC WW8Fonts Constructor Code Execution Vulnerability

October 26, 2017
CVE Number

CVE-2017-9806

Summary

An exploitable out of bound write vulnerability exists in the WW8Fonts::WW8Fonts functionality of Apache OpenOffice 4.1.3. A specially crafted doc file can cause an out of bound write potentially resulting in arbitrary code execution. An attacker can send/provide a malicious doc file to trigger this vulnerability.

Tested Versions

Apache OpenOffice 4.1.3

Product URLs

http://www.openoffice.org/

CVSSv3 Score

8.3 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H

Details

This vulnerability is present in Apache OpenOffice (formerly OpenOffice.org), a free open source office suite. A specially crafted DOC file can lead to an out of bound write and ultimately to remote code execution.

Let's investigate this vulnerability. After OpenOffice Writer opens the malformed doc file we see the following state:

gdb-peda$ context
[----------------------------------registers-----------------------------------]
EAX: 0xa9788010 --> 0x30003 
EBX: 0xa9c16000 --> 0x2a1b30 
ECX: 0x28 ('(')
EDX: 0x3 
ESI: 0xffff 
EDI: 0xa96a800c --> 0xffff0000 
EBP: 0xbfffd578 --> 0xbfffd778 --> 0xbfffd9a8 --> 0xbfffda48 --> 0xbfffdab8 --> 0xbfffdb78 --> 0xbfffdbf8 --> 0xbfffddd8 --> 0xbfffdea8 --> 0xbfffdf68 --> 0xbfffe018 -->    
0xbfffe058 --> 0xbfffe0e8 --> 0xbfffe138 --> 0xbfffe1f8 --> 0xbfffe378 --> 0xbfffe3d8 --> 0xbfffe6c8 --> 0xbfffe718 --> 0xbfffe738 --> 0xbfffe758 --> 0xbfffe778 --> 
0xbfffe818 --> 0xbfffe848 --> 0xbfffe898 --> 0xbfffe8c8 --> 0xbfffe8e8 --> 0x823f4b0 --> 0x2 
ESP: 0xbfffd500 --> 0x10 
EIP: 0xa9b5cdf7 (<WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1649>: mov    WORD PTR [eax],dx)
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xa9b5cdef <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1641>:    add    esp,0x10
   0xa9b5cdf2 <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1644>:    mov    edx,eax
   0xa9b5cdf4 <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1646>:    mov    eax,DWORD PTR [ebp-0x2c]
=> 0xa9b5cdf7 <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1649>:    mov    WORD PTR [eax],dx
   0xa9b5cdfa <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1652>:    add    DWORD PTR [ebp-0x2c],0x2
   0xa9b5cdfe <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1656>:    add    BYTE PTR [ebp-0x53],0x2
   0xa9b5ce02 <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1660>:    jmp    0xa9b5cddb <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1621>
   0xa9b5ce04 <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1662>:    mov    eax,DWORD PTR [ebp-0x30]
[------------------------------------stack-------------------------------------]
0000| 0xbfffd500 --> 0x10 
0004| 0xbfffd504 --> 0xa9c22110 --> 0xa96a800c --> 0xffff0000 
0008| 0xbfffd508 --> 0xa96a8008 --> 0xffff 
0012| 0xbfffd50c --> 0xa9768000 --> 0x0 
0016| 0xbfffd510 --> 0xffffffff 
0020| 0xbfffd514 --> 0xa9cea310 --> 0x8 
0024| 0xbfffd518 --> 0xa9c21f58 --> 0xb4b526a0 --> 0xb4b15a6a (<SotStorageStream::GetData(void*,   
unsigned long)>:       push   ebp)
0028| 0xbfffd51c --> 0xa9c22110 --> 0xa96a800c --> 0xffff0000 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
gdb-peda$ bt 1
#0  0xa9b5cdf7 in WW8Fonts::WW8Fonts (this=0xa9c22110, rSt=..., rFib=...) at /storage/aoo-
4.1.3/main/sw/source/filter/ww8/ww8scan.cxx:6571
(More stack frames follow...)
gdb-peda$ info line *$pc
Line 6571 of "/storage/aoo-4.1.3/main/sw/source/filter/ww8/ww8scan.cxx" starts at address 0xa9b5cde4 
<WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1630> and ends at 0xa9b5cdfa 
<WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1652>.
gdb-peda$ list *$pc
0xa9b5cdf7 is in WW8Fonts::WW8Fonts(SvStream&, WW8Fib&) (/storage/aoo-
4.1.3/main/sw/source/filter/ww8/ww8scan.cxx:6571).
6566                        sal_uInt8 nLength = sizeof( pVer8->szFfn ) / sizeof( SVBT16 );
6567                        nLength = std::min( nLength, sal_uInt8( pVer8->cbFfnM1+1 ) );
6568                        for( sal_uInt16* pTmp = pVer8->szFfn;
6569                            nLen < nLength; ++pTmp, nLen+=2 )
6570                        {
6571                            *pTmp = SVBT16ToShort( *(SVBT16*)pTmp );
6572                        }
6573                    }
6574    #endif // defined __WW8_NEEDS_COPY
6575

So we see that write access violation appeared in the WW8Fonts::WW8Fonts constructor. The definition of this function is located in file /storage/aoo-4.1.3/main/sw/source/filter/ww8/ww8scan.cxx:6571. Checking the pTmp pointer value we see:

gdb-peda$ p pTmp 
$25 = (sal_uInt16 *) 0xa9788010
gdb-peda$ vmmap 0xa9788010
Start      End        Perm      Name
0xa9788000 0xa9823000 r-xp      /storage/aoo-
4.1.3/main/instsetoo_native/unxlngi6.pro/Apache_OpenOffice/installed/install/en-
US/openoffice4/program/libunoxml.so

When an attempt to write to the address pointed to by the pTmp pointer, we encounter an access violation will because it points to mapped libunoxml.so library. Looking at source code we see (the lines here have different numbers than the lines in the original source code):

1   WW8Fonts::WW8Fonts( SvStream& rSt, WW8Fib& rFib )
2       : pFontA(0), nMax(0) 
3   {
4   //  sal_uInt16 nMax;
5   (...)
6       rSt.Seek( rFib.fcSttbfffn );
7 
8       sal_Int32 nFFn = rFib.lcbSttbfffn - 2;
9 
10      // allocate Font Array
11      sal_uInt8* pA   = new sal_uInt8[ nFFn ];
12      memset(pA, 0, nFFn);
13      WW8_FFN* p = (WW8_FFN*)pA;
14      
15      ww::WordVersion eVersion = rFib.GetFIBVersion();
16
17      if( eVersion >= ww::eWW8 )
18      {
19          // bVer8: read the count of strings in nMax
20          rSt >> nMax;
21      }   
22      
23  (...)
24              WW8_FFN_Ver8* pVer8 = (WW8_FFN_Ver8*)pA;
25              sal_uInt8 c2;
26              for(sal_uInt16 i=0; i<nMax; ++i, ++p)
27              {   
28              
29              (...)
30                      sal_uInt8 nLen = 0x28;
31                      sal_uInt8 nLength = sizeof( pVer8->szFfn ) / sizeof( SVBT16 );
32                      nLength = std::min( nLength, sal_uInt8( pVer8->cbFfnM1+1 ) );
33                      for( sal_uInt16* pTmp = pVer8->szFfn;
34                          nLen < nLength; ++pTmp, nLen+=2 )
35                      {
36                          *pTmp = SVBT16ToShort( *(SVBT16*)pTmp );
37                      }
38              (...)           
39                  // Zeiger auf Ursprungsarray einen Font nach hinten setzen
40                  pVer8 = (WW8_FFN_Ver8*)( ((sal_uInt8*)pVer8) + pVer8->cbFfnM1 + 1 );
41              }           

The loop at line 26 is based on nMax value. Each time at the end of this loop (at line 40) pVer8 pointer is set to new location based on the cbFfnM1 field value. pVer8 is a pointer to a dynamically allocated buffer, which is allocated at line 11, with a size equal to rFib.lcbSttbfffn - 2. As we can see there is no check to see whether after first iteration pVer8 is pointing outside buffer range or not. That situation leads to out of bound read/writes in certain places and finally can lead to remote code execution.

A dump of some important fields:

gdb-peda$ p rFib.lcbSttbfffn
$30 = 0xb6
gdb-peda$ p rFib.fcSttbfffn
$31 = 0x501
gdb-peda$ p nMax
$32 = 0xffff
gdb-peda$ p eVersion
$33 = ww::eWW8
gdb-peda$ p *(WW8_FFN_Ver8*)pA
$38 = {
  <WW8_FFN_BASE> = {
    cbFfnM1 = 0x0, 
    prg = 0x0, 
    fTrueType = 0x0, 
    ff = 0x0, 
    wWeight = 0xffff, 
    chs = 0x0, 
    ibszAlt = 0x0

Crash Information

[----------------------------------registers-----------------------------------]
EAX: 0xa9788010 --> 0x30003 
EBX: 0xa9c16000 --> 0x2a1b30 
ECX: 0x28 ('(')
EDX: 0x3 
ESI: 0xffff 
EDI: 0xa96a800c --> 0xffff0000 
EBP: 0xbfffd578 --> 0xbfffd778 --> 0xbfffd9a8 --> 0xbfffda48 --> 0xbfffdab8 --> 0xbfffdb78 --> 0xbfffdbf8 --> 
0xbfffddd8 --> 0xbfffdea8 --> 0xbfffdf68 --> 0xbfffe018 --> 
0xbfffe058 --> 0xbfffe0e8 --> 0xbfffe138 --> 0xbfffe1f8 --> 0xbfffe378 --> 0xbfffe3d8 --> 0xbfffe6c8 --> 0xbfffe718 
--> 0xbfffe738 --> 0xbfffe758 --> 0xbfffe778 --> 
0xbfffe818 --> 0xbfffe848 --> 0xbfffe898 --> 0xbfffe8c8 --> 0xbfffe8e8 --> 0x823f4b0 --> 0x2 
ESP: 0xbfffd500 --> 0x10 
EIP: 0xa9b5cdf7 (<WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1649>: mov    WORD PTR [eax],dx)
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xa9b5cdef <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1641>:    add    esp,0x10
   0xa9b5cdf2 <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1644>:    mov    edx,eax
   0xa9b5cdf4 <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1646>:    mov    eax,DWORD PTR [ebp-0x2c]
=> 0xa9b5cdf7 <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1649>:    mov    WORD PTR [eax],dx
   0xa9b5cdfa <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1652>:    add    DWORD PTR [ebp-0x2c],0x2
   0xa9b5cdfe <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1656>:    add    BYTE PTR [ebp-0x53],0x2
   0xa9b5ce02 <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1660>:    jmp    0xa9b5cddb 
 <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1621>
   0xa9b5ce04 <WW8Fonts::WW8Fonts(SvStream&, WW8Fib&)+1662>:    mov    eax,DWORD PTR [ebp-0x30]
[------------------------------------stack-------------------------------------]
0000| 0xbfffd500 --> 0x10 
0004| 0xbfffd504 --> 0xa9c22110 --> 0xa96a800c --> 0xffff0000 
0008| 0xbfffd508 --> 0xa96a8008 --> 0xffff 
0012| 0xbfffd50c --> 0xa9768000 --> 0x0 
0016| 0xbfffd510 --> 0xffffffff 
0020| 0xbfffd514 --> 0xa9cea310 --> 0x8 
0024| 0xbfffd518 --> 0xa9c21f58 --> 0xb4b526a0 --> 0xb4b15a6a (<SotStorageStream::GetData(void*, 
unsigned long)>:       push   ebp)
0028| 0xbfffd51c --> 0xa9c22110 --> 0xa96a800c --> 0xffff0000 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
gdb-peda$ exploitable 
Description: Access violation on destination operand
Short description: DestAv (9/29)
Hash: 35ea22a685538a48f0dc54bae45e3da2.afc0f1decc0f689aa835fa8a3e7bfbc3
Exploitability Classification: EXPLOITABLE
Explanation: The target crashed on an access violation at an address matching the destination operand of the 
instruction. This likely indicates a write access violation, 
which means the attacker may control the write address and/or value.
Other tags: AccessViolation (28/29)
gdb-peda$ bt
#0  0xa9b5cdf7 in WW8Fonts::WW8Fonts (this=0xa9c22110, rSt=..., rFib=...) at /storage/aoo-
4.1.3/main/sw/source/filter/ww8/ww8scan.cxx:6571
#1  0xa9ae4b57 in SwWW8ImplReader::CoreLoad (this=0xabc86c0c, pGloss=0x0, rPos=...) at /storage/aoo-
4.1.3/main/sw/source/filter/ww8/ww8par.cxx:4390
#2  0xa9ae8606 in SwWW8ImplReader::LoadThroughDecryption (this=0xabc86c0c, rPaM=..., pGloss=0x0) at 
/storage/aoo-4.1.3/main/sw/source/filter/ww8/ww8par.cxx:5163
#3  0xa9ae94dd in SwWW8ImplReader::LoadDoc (this=0xabc86c0c, rPaM=..., pGloss=0x0) at /storage/aoo-
4.1.3/main/sw/source/filter/ww8/ww8par.cxx:5430
#4  0xa9ae976a in WW8Reader::Read (this=0xa9cdff84, rDoc=..., rBaseURL=..., rPam=...) at /storage/aoo-
4.1.3/main/sw/source/filter/ww8/ww8par.cxx:5499
#5  0xaaa4d71e in SwReader::Read (this=0xa9c1e2b4, rOptions=...) at /storage/aoo-
4.1.3/main/sw/source/filter/basflt/shellio.cxx:189
#6  0xaab67bc1 in SwDocShell::ConvertFrom (this=0xacafe034, rMedium=...) at /storage/aoo-
4.1.3/main/sw/source/ui/app/docsh.cxx:258
#7  0xb7243e0f in SfxObjectShell::DoLoad (this=0xacafe034, pMed=0xa9cf4e90) at /storage/aoo-
4.1.3/main/sfx2/source/doc/objstor.cxx:753
#8  0xb728bcfc in SfxBaseModel::load (this=0xabb98ad4, seqArguments=...) at /storage/aoo-
4.1.3/main/sfx2/source/doc/sfxbasemodel.cxx:1878
#9  0xb7342be9 in SfxFrameLoader_Impl::load (this=0xabb7758c, rArgs=..., _rTargetFrame=...) at 
/storage/aoo-4.1.3/main/sfx2/source/view/frmload.cxx:607
#10 0xae5786d8 in framework::LoadEnv::impl_loadContent (this=0xaf31e964) at /storage/aoo-
4.1.3/main/framework/source/loadenv/loadenv.cxx:1205
#11 0xae574e16 in framework::LoadEnv::startLoading (this=0xaf31e964) at /storage/aoo-
4.1.3/main/framework/source/loadenv/loadenv.cxx:433
#12 0xae505c67 in framework::LoadDispatcher::impl_dispatch (this=0xaf31e918, rURL=..., lArguments=..., 
xListener=...) at /storage/aoo-4.1.3/main/framework/source/
dispatch/loaddispatcher.cxx:165
#13 0xae5059a8 in framework::LoadDispatcher::dispatchWithReturnValue (this=0xaf31e918, rURL=..., 
lArguments=...) at /storage/aoo-4.1.3/main/framework/source/dispatch/
loaddispatcher.cxx:102
#14 0xb782d5f4 in comphelper::SynchronousDispatch::dispatch (xStartPoint=..., sURL=..., sTarget=..., 
nFlags=0x0, lArguments=...) at /storage/aoo-4.1.3/main/comphelper/
source/misc/synchronousdispatch.cxx:81
#15 0xb7d753f4 in desktop::DispatchWatcher::executeDispatchRequests (this=0xad4129a8, 
aDispatchRequestsList=..., bNoTerminate=0x0) at /storage/aoo-4.1.3/main/desktop/
source/app/dispatchwatcher.cxx:333
#16 0xb7d83108 in desktop::OfficeIPCThread::ExecuteCmdLineRequests (aRequest=...) at /storage/aoo-
4.1.3/main/desktop/source/app/officeipcthread.cxx:993
#17 0xb7d562b4 in desktop::Desktop::OpenClients () at /storage/aoo-
4.1.3/main/desktop/source/app/app.cxx:3204
#18 0xb7d51dfd in desktop::Desktop::OpenClients_Impl (this=0xbfffed88) at /storage/aoo-
4.1.3/main/desktop/source/app/app.cxx:2536
#19 0xb7d51db1 in desktop::Desktop::LinkStubOpenClients_Impl (pThis=0xbfffed88, pCaller=0x0) at 
/storage/aoo-4.1.3/main/desktop/source/app/app.cxx:2532
#20 0xb5b07fc4 in Link::Call (this=0xae90c2ec, pCaller=0x0) at /storage/aoo-
4.1.3/main/solver/413/unxlngi6.pro/inc/tools/link.hxx:135
#21 0xb5efa9a1 in ImplHandleUserEvent (pSVEvent=0xacafa0f8) at /storage/aoo-      
4.1.3/main/vcl/source/window/winproc.cxx:1996
#22 0xb5efbc22 in ImplWindowFrameProc (pWindow=0xae90e1d4, nEvent=0x16, pEvent=0xacafa0f8) at 
/storage/aoo-4.1.3/main/vcl/source/window/winproc.cxx:2568
#23 0xb20e0fd5 in SalFrame::CallCallback (this=0xaf321a60, nEvent=0x16, pEvent=0xacafa0f8) at 
/storage/aoo-4.1.3/main/vcl/inc/salframe.hxx:281
#24 0xb20f4c25 in SalDisplay::DispatchInternalEvent (this=0xb22b4008) at /storage/aoo-
4.1.3/main/vcl/unx/generic/app/saldisp.cxx:2231
#25 0xb22582f4 in GtkXLib::userEventFn (data=0xb24c9308) at /storage/aoo-
4.1.3/main/vcl/unx/gtk/app/gtkdata.cxx:817
#26 0xb225820e in call_userEventFn (data=0xb24c9308) at /storage/aoo-
4.1.3/main/vcl/unx/gtk/app/gtkdata.cxx:790
#27 0xb1799610 in ?? () from /lib/i386-linux-gnu/libglib-2.0.so.0
#28 0xb179cd9b in g_main_context_dispatch () from /lib/i386-linux-gnu/libglib-2.0.so.0
#29 0xb179d189 in ?? () from /lib/i386-linux-gnu/libglib-2.0.so.0
#30 0xb179d254 in g_main_context_iteration () from /lib/i386-linux-gnu/libglib-2.0.so.0
#31 0xb22584e2 in GtkXLib::Yield (this=0xb24c9308, bWait=0x1, bHandleAllCurrentEvents=0x0) at 
/storage/aoo-4.1.3/main/vcl/unx/gtk/app/gtkdata.cxx:869
#32 0xb2103187 in X11SalInstance::Yield (this=0xb2cec290, bWait=0x1, bHandleAllCurrentEvents=0x0) at 
/storage/aoo-4.1.3/main/vcl/unx/generic/app/salinst.cxx:278
#33 0xb5b19e67 in ImplYield (i_bWait=0x1, i_bAllEvents=0x0) at /storage/aoo-
4.1.3/main/vcl/source/app/svapp.cxx:476
#34 0xb5b15a93 in Application::Yield (i_bAllEvents=0x0) at /storage/aoo-
4.1.3/main/vcl/source/app/svapp.cxx:510
#35 0xb5b15a2c in Application::Execute () at /storage/aoo-4.1.3/main/vcl/source/app/svapp.cxx:453
#36 0xb7d5051a in desktop::Desktop::Main (this=0xbfffed88) at /storage/aoo-
4.1.3/main/desktop/source/app/app.cxx:2232
#37 0xb5b20296 in ImplSVMain () at /storage/aoo-4.1.3/main/vcl/source/app/svmain.cxx:196
#38 0xb5b20423 in SVMain () at /storage/aoo-4.1.3/main/vcl/source/app/svmain.cxx:237
#39 0xb7d8543a in soffice_main () at /storage/aoo-4.1.3/main/desktop/source/app/sofficemain.cxx:45
#40 0x08048df5 in sal_main () at main.c:31
#41 0x08048ddb in main (argc=0x6, argv=0xbfffeea4) at main.c:30
#42 0xb798a637 in __libc_start_main (main=0x8048dbb <main>, argc=0x6, argv=0xbfffeea4, init=0x80493a0 
<__libc_csu_init>, fini=0x8049400 <__libc_csu_fini>, 
rtld_fini=0xb7fea780 <_dl_fini>, stack_end=0xbfffee9c) at ../csu/libc-start.c:291
#43 0x08048ce1 in _start ()

Timeline

2017-03-16 - Vendor Disclosure
2017-10-26 - Public Release

Credit

Discovered by Marcin 'Icewall' Noga of Cisco Talos.