bingghost / google-security-research

Automatically exported from code.google.com/p/google-security-research
0 stars 0 forks source link

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

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
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;
  }
  else
  {
    v88 = font;
  }
  if ( !v88->BuildCharArray )
  {
    DbgPrint(
      &dword_57B00,
      "%s:%d: %s (%s)\n",
      "windows\\core\\ntgdi\\fondrv\\otfd\\bc\\t1interp.c",
      5983,
      "stack underflow in Get othersubr",
      "false");
    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
  else:
    # 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.

References:
[1] "The Type 2 Charstring Format, Technical Note #5177, 16 March 2000", 
http://partners.adobe.com/public/developer/en/font/5177.Type2.pdf
[2] "The Compact Font Format Specification, Technical Note #5176, Version 1.0, 
18 March 1998", http://www-cdf.fnal.gov/offline/PostScript/5176.CFF.pdf

Original issue reported on code.google.com by mjurc...@google.com on 19 Nov 2014 at 10:51

Attachments:

GoogleCodeExporter commented 8 years ago
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.

Original comment by mjurc...@google.com on 19 Nov 2014 at 12:09

GoogleCodeExporter commented 8 years ago

Original comment by mjurc...@google.com on 4 Dec 2014 at 4:37

GoogleCodeExporter commented 8 years ago

Original comment by mjurc...@google.com on 11 Dec 2014 at 10:17

GoogleCodeExporter commented 8 years ago
I'll handle this one.

Original comment by mjurc...@google.com on 6 Feb 2015 at 12:33

GoogleCodeExporter commented 8 years ago
Accidentally misclicked.

Original comment by mjurc...@google.com on 6 Feb 2015 at 12:35

GoogleCodeExporter commented 8 years ago

Original comment by mjurc...@google.com on 24 Mar 2015 at 10:06

GoogleCodeExporter commented 8 years ago

Original comment by cev...@google.com on 1 Apr 2015 at 12:11

GoogleCodeExporter commented 8 years ago

Original comment by mjurc...@google.com on 20 Apr 2015 at 2:07

GoogleCodeExporter commented 8 years ago

Original comment by mjurc...@google.com on 12 Jun 2015 at 4:02