Exploit-DB #47259
Description: Adobe Acrobat CoolType (AFDKO) - Memory Corruption in the Handling of Type 1 Font load/store Operators
Type: dos
Platform: windows
Date: 2019-08-15
Author: Google Security Research

Exploit:
-----=====[ Background ]=====-----

AFDKO (Adobe Font Development Kit for OpenType) is a set of tools for examining, modifying and building fonts. The core part of this toolset is a font handling library written in C, which provides interfaces for reading and writing Type 1, OpenType, TrueType (to some extent) and several other font formats. While the library existed as early as 2000, it was open-sourced by Adobe in 2014 on GitHub [1, 2], and is still actively developed. The font parsing code can be generally found under afdko/c/public/lib/source/*read/*.c in the project directory tree.

We have recently discovered that parts of AFDKO are compiled in in Adobe's desktop software such as Adobe Acrobat. Within a single installation of Acrobat, we have found traces of AFDKO in four different libraries: acrodistdll.dll, Acrobat.dll, CoolType.dll and AdobePDFL.dll. According to our brief analysis, AFDKO is not used for font rasterization (there is a different engine for that), but rather for the conversion between font formats. For example, it is possible to execute the AFDKO copy in CoolType.dll by opening a PDF file with an embedded font, and exporting it to a PostScript (.ps) or Encapsulated PostScript (.eps) document. It is uncertain if the AFDKO copies in other libraries are reachable as an attack surface and how.

It is also interesting to note that the AFDKO copies in the above DLLs are much older than the latest version of the code on GitHub. This can be easily recognized thanks to the fact that each component of the library (e.g. the Type 1 Reader - t1r, Type 1 Writer - t1w, CFF reader - cfr etc.) has its own version number included in the source code, and they change over time. For example, CoolType's version of the "cfr" module is 2.0.44, whereas the first open-sourced commit of AFDKO from September 2014 has version 2.0.46 (currently 2.1.0), so we can conclude that the CoolType fork is at least about ~5 years old. Furthermore, the forks in Acrobat.dll and AdobePDFL.dll are even older, with a "cfr" version of 2.0.31.

Despite the fact that CoolType contains an old fork of the library, it includes multiple non-public fixes for various vulnerabilities, particularly a number of important bounds checks in read*() functions declared in cffread/cffread.c (e.g. readFDSelect, readCharset etc.). These checks were first introduced in CoolType.dll shipped with Adobe Reader 9.1.2, which was released on 28 May 2009. This means that the internal fork of the code has had many bugs fixed for the last 10 years, which are still not addressed in the open-source branch of the code. Nevertheless, we found more security vulnerabilities which affect the AFDKO used by CoolType, through analysis of the publicly available code. This report describes one such issue reachable through the Adobe Acrobat file export functionality.

-----=====[ Description ]=====-----

The "Type 2 Charstring Format" specification from 5 May 1998 introduced two storage operators: store and load, which were both deprecated in the next iteration of the specs in 2000. These operators were responsible for copying data between the transient array (also known as the BuildCharArray, or BCA) and the so-called "Registry object".

As the document stated:

"""
    The Registry provides more permanent storage for a number of items that have predefined meanings. The items stored in the Registry do not persist beyond the scope of rendering a font. Registry items are selected with an index, thus:

    0 Weight Vector
    1 Normalized Design Vector
    2 User Design Vector

    The result of selecting a Registry item with an index outside this list is undefined.
"""

The Type 1 CharString interpreter implemented in t1Decode() (c/public/lib/source/t1cstr/t1cstr.c) supports the load and store operators:

--- cut ---
  1450                      case t1_store:
  1451                          result = do_store(h);
  1452                          if (result)
  1453                              return result;
  1454                          continue;
[...]
  1470                      case t1_load:
  1471                          result = do_load(h);
  1472                          if (result)
  1473                              return result;
  1474                          continue;
--- cut ---

The do_store() and do_load() functions are as follows:

--- cut ---
   664  /* Select registry item. Return NULL on invalid selector. */
   665  static float *selRegItem(t1cCtx h, int reg, int *size) {
   666      switch (reg) {
   667          case T1_REG_WV:
   668              *size = T1_MAX_MASTERS;
   669              return h->aux->WV;
   670          case T1_REG_NDV:
   671              *size = T1_MAX_AXES;
   672              return h->aux->NDV;
   673          case T1_REG_UDV:
   674              *size = T1_MAX_AXES;
   675              return h->aux->UDV;
   676      }
   677      return NULL;
   678  }
   679
   680  /* Execute "store" op. Return 0 on success else error code. */
   681  static int do_store(t1cCtx h) {
   682      int size;
   683      int count;
   684      int i;
   685      int j;
   686      int reg;
   687      float *array;
   688
   689      CHKUFLOW(4);
   690
   691      count = (int)POP();
   692      i = (int)POP();
   693      j = (int)POP();
   694      reg = (int)POP();
   695      array = selRegItem(h, reg, &size);
   696
   697      if (array == NULL ||
   698          i < 0 || i + count + 1 >= TX_BCA_LENGTH ||
   699          j < 0 || j + count + 1 >= size)
   700          return t1cErrStoreBounds;
   701
   702      memcpy(&array[j], &h->BCA[i], sizeof(float) * count);
   703      return 0;
   704  }
   705
[...]
   736
   737  /* Execute "load" op. Return 0 on success else error code. */
   738  static int do_load(t1cCtx h) {
   739      int size;
   740      int count;
   741      int i;
   742      int reg;
   743      flo