Talos Vulnerability Report

TALOS-2016-0059

Libgraphite Context Item Code Execution Vulnerability

February 5, 2016

Report ID

CVE-2016-1523

Description

An exploitable heap-based buffer overflow exists in the context item handling functionality of Libgraphite. A specially crafted font can cause a buffer overflow resulting in potential code execution. An attacker can provide a malicious font to trigger this vulnerability.

Tested Versions

Libgraphite 2-1.2.4

Product URLs

http://sourceforge.net/projects/silgraphite/files/graphite2/

Details

If a malicious font is provided then an arbitrary length buffer overflow can occur when handling context items.

In Code.cpp at lines 158-161, memory is allocated for _code and _data to be the expected maximum size of the code and data loaded from the bytecode. However, the actual size read from the file can be larger than the expected size.

  _code = static_cast(malloc((bytecode_end - bytecode_begin)
                                        * sizeof(instr)));
  _data = static_cast(malloc((bytecode_end - bytecode_begin)
                                        * sizeof(byte)));

At line 181 the function dec.load() is called with pointers to the beginning and end of the bytecode. This function, at lines 230-240, will then load opcodes from the bytecode until the end of the bytecode is reached. It will call emitopcode() at line 238 with the current value of the bytecode that is being handled.

  while (bc < bcend)
  {
     const opcode opc = fetchopcode(bc++);
     if (opc == vm::MAXOPCODE)
         return false;
     analyse_opcode(opc, reinterpret_cast<const int8 *>(bc));
     if (!emit_opcode(opc, bc))
         return false;
  }

If the opcode being handled by emitopcode refers to a context item (CNTXTITEM). Then the load function will be called recursively at line 500, with a new bytecodeend that is specified by the bytecode in instrskip.

  if (load(bc, bc + instr_skip))

The size checks to ensure that data isn’t read from outside of the buffer, occur in the validateopcode function at line 548 where it is checked that the new opcode being read will be less than _max.bytecode (which contains the value of bytecodeend).

However, when recursively handling multiple CNTXTITEMs, this means that while data is being copied to _data, it can be read past the new bytecodeend that is specified by the recursive call to load, this will terminate the loop once it reaches there, but in the mean time more data can be read than specified by instrskip, because when validateopcode is called, the current value of bytecode will still be lower than the end of the bytecode. Doing this multiple times, results in the same memory being handled multiple times and allowing data to become larger than bytecode allowing an out-of-bounds write. The same can occur with the code buffer. A proposed fix is to ensure that when returning from a CNTXTITEM, the current value of bytecode minus the begin value of bytecode is larger or equal to datasize and instrcount. A similar check to what is performed via an assert at lines 210 and 211.

The attached sample causes the overflow, in this case it will be cause an invalid size on the heap due to the buffer overflow overwriting the size field in the adjacent chunk. When realloc is called at line 213 to (normally) lower the size of the code and data buffers, the invalid size is detected.

Credit

Discovered by Yves Younan of Cisco Talos.