New issue
Advanced search Search tips
Starred by 1 user
Status: WontFix
Owner:
Closed: Apr 2016
Cc:



Sign in to add a comment
Windows Kernel ATMFD.DLL NamedEscape 0x2511 out-of-bounds read
Project Member Reported by mjurczyk@google.com, Mar 25 2016 Back to list
The Adobe Type Manager Font Driver (ATMFD.DLL) responsible for handling PostScript and OpenType fonts in the Windows kernel provides a channel of communication with user-mode applications via an undocumented gdi32!NamedEscape API call. The nature of the channel is similar to IOCTLs [1] of type METHOD_BUFFERED [2], in that it also uses a 32-bit escape code (equivalent to control codes in IOCTL), and input and output buffers employed to pass data to the driver and receive information back. We suspect that this little known interface was originally designed to be more universal, but since ATMFD has remained the only third-party font driver in Windows for the past two decades, in reality it can only be used to interact with this single module.

Considering that there hasn't been any public research into the security of ATMFD's NamedEscape handlers, it is likely that it hasn't been thoroughly audited since the creation of the code. The first public vulnerability disclosed in the interface was a 0-day pool corruption (stemming from a 16-bit signedness issue) discovered in Hacking Team's leaked data dump [3], and subsequently fixed by Microsoft in the MS15-077 bulletin [4]. That security issue motivated us to have a deeper look into the security posture of the code area.

The main hurdle in approaching the NamedEscape attack surface is that Microsoft provides no symbols or other debugging information for the ATMFD.DLL library via the Microsoft Symbol Server, which means that all functionality needs to be reverse-engineered from the ground up, with no hints available whatsoever. At least that was what we believed until recently, when we discovered that the user-mode counterpart of ATMFD is ATMLIB.DLL -- a legacy library rarely used by the operating system nowadays, but which comes with debug symbols and implements many of its features by making NamedEscape calls to the kernel-mode driver. This lead to the further discovery of the "Adobe Type Manager Software API for Windows 95 and Windows NT 4" [5] and "Adobe Type Manager Software API: Windows" [6] documents, which greatly helped us understand the semantics of most of the escape codes, some of the underlying structures and other specifics of the code.

All further analysis presented below is relevant to an ATMFD.DLL file found on Windows 7 32-bit, version 5.1.2.247, md5sum e85bed746bbddcd29ad63f6085e1ce78. The driver currently supports 13 different escape codes in the range of 0x2500 - 0x2514. The bug discussed in this report resides in the handler of code 0x2511, whose semantics we have been unable to determine (it is not referenced by ATMLIB). For future reference, we have called it "ATMUnspecifiedScramble".

The escape code requires the i/o buffer passed by the client to be exactly 32 bytes long:

--- cut ---
      case 0x2511:
        if ( cbInput == 32 )
        {
          ret = ATMUnspecifiedScramble(lpBuffer);
          goto label_return;
        }
        break;
--- cut ---

The actual functionality of the short ATMUnspecifiedScramble routine isn't very important at this point -- in short, the function does not operate on any fonts, but instead scrambles some input values using an internal seed and data passed from user-mode using arithmetic and logic operators, and saves the result in several static arrays. While it is unclear what the actual purpose of the function is, it is important to note that it can read up to 32 DWORD values from the i/o buffer, starting at offset 16:

--- cut ---
      if ( *((_WORD *)lpBuffer + 6) > 0x20u )
        *((_WORD *)lpBuffer + 6) = 32;
      for ( i = 0; i < *((_WORD *)lpBuffer + 6); ++i )
      {
        v3 = sub_124F4(*((_DWORD *)lpBuffer + i + 4), dword_56924);
        v4 = &dword_56880[i];
        if ( !*v4 )
          *v4 = v3;
        byte_56900[i] = *v4 != v3;
      }
--- cut ---

As the buffer is enforced to be 32 bytes long, if the client sets the WORD at offset 12 to anything beyond 31, the loop will read 32 * 4 = 128 bytes; 16 bytes from the end of the valid i/o buffer, and then further 112 bytes beyond it. While we haven't figured out if or how the OOB data could be recovered back into user-mode, just reading beyond a pool-based buffer should be sufficient to trigger a kernel crash. What elevates the impact even more is the fact that the bug could be reached from a website loaded in Internet Explorer with a specially crafted EMF image embedded, as the NamedEscape attack surface is accessible to EMF files via the EMR_NAMEDESCAPE record. In other words, a malicious website could crash an entire operating system by exploiting this bug.

If one tries to reproduce the issue, however, the system surprisingly won't crash, even with Special Pools enabled. After a short investigation, we have found that this is caused by the following optimization in the win32k!NtGdiExtEscape function:

--- cut ---
  if (NumberOfBytes > 32) {
    lpBuffer = ExAllocatePoolWithTag(...);
  } else {
    lpBuffer = &local_buffer;
  }
--- cut ---

As shown in the above pseudo-code snippet, the system call handler only allocates pool-based i/o buffers when its size is larger than 32 bytes, and otherwise uses a stack-based byte array. Since the buffer size for the vulnerable escape code 0x2511 is enforced to be exactly 32 bytes, ATMUnspecifiedScramble always ends up with a stack-based buffer. The distance from the local buffer to the end of the stack is always greater than 112 bytes, making it impossible to trigger a memory access beyond the stack page mapping. To our current knowledge, this coincidence essentially makes the vulnerability non-exploitable, as long as there is no other way to reach the NamedEscape handler with a pool-based i/o buffer, or extract the out-of-bounds values read by ATMUnspecifiedScramble back to user-mode.

Even though the issue doesn't seem to have a practical impact today, we have still decided to report it, as any unrelated change to any of the mitigating conditions in the future may lead to the bug becoming a remotely exploitable kernel DoS. Attached you can find a proof of concept program, which triggers the out-of-bounds reads discussed in this report.

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.

References:
[1] https://msdn.microsoft.com/pl-pl/library/windows/desktop/aa363219%28v=vs.85%29.aspx
[2] https://msdn.microsoft.com/pl-pl/library/windows/hardware/ff565356%28v=vs.85%29.aspx
[3] https://bugs.chromium.org/p/project-zero/issues/detail?id=473
[4] https://technet.microsoft.com/library/security/ms15-077
[5] https://partners.adobe.com/public/developer/en/atm/5642.ATMWin95andNT.pdf
[6] https://partners.adobe.com/public/developer/en/atm/5073.ATM.API_Win.pdf
 
poc.zip
51.0 KB Download
Project Member Comment 1 by mjurczyk@google.com, Mar 25 2016
Labels: MSRC-33012
Project Member Comment 2 by mjurczyk@google.com, Apr 13 2016
Labels: -Restrict-View-Commit
Status: WontFix
Microsoft have concluded that the bug is not exploitable and therefore doesn't meet the bar for a security bulletin. This analysis is compatible with our assessment, so I'm lifting the view restriction.
Sign in to add a comment