
Detailed technical analysis and exploit implementation for CVE-2015-0057, a win32k.sys use-after-free vulnerability, covering 32-bit and 64-bit Windows systems from XP to 8.1.
Author: Aaron Adams
Translation: 55-AA
Translator's Note: Some parts of this article are freely translated. Please refer to the original text for any questions.
Terminology:
Earlier this year, I encountered an interesting vulnerability in win32k.sys (CVE-2015-0057) and achieved stable exploitation on both 32-bit and 64-bit systems, ranging from XP to Windows 8.1 (with some exceptions). This article details how I accomplished the exploitation on these two platforms, with some additional content at the end. It also describes how to achieve exploitation at low integrity level on Windows 8.1 with SMEP enabled.
This article is quite long. I have tried to provide as many details as possible to show the complexity of exploiting this vulnerability, rather than hiding them. Of course, I have also omitted some details. I hope these details are helpful to everyone.
On February 10, 2015, Microsoft disclosed the details of MS15-010. This bug was first discovered by Udi Yavo from enSilo. Udi gave an excellent analysis on the breaking malware blog: "one bit rule-bypassing windows 10 protections using single bit". I recommend reading this article carefully to understand the bug deeply, although I will provide as many details as possible in this article, covering some obstacles that must be overcome when triggering the vulnerability. The exploitation of this vulnerability is very interesting. Many details come from Udi's blog. Here is his statement:
Reasonable disclosure: Although this blog is technical, we will not disclose any code or complete details to prevent any technical expert from reproducing this exploit.
As an additional reward for exploiting this vulnerability, we get a Pokémon evolution: Tech Pokémon. I think I need to give Udi some credit for discovering this bug and providing relevant information and details on the blog, which were extremely useful.
Before this, I had never exploited a win32k.sys vulnerability, nor was I familiar with user-mode callbacks and many related APIs. Therefore, I also thank some well-known security researchers for providing novel resources on the internet, such as Skywing, Tarjei Mandt, Alex Ionescu, and j00ru. These people have provided so much technical information publicly; they all deserve praise. I heavily referenced an article by Tarjei Mandt: Win32k.sys exploitation paper.
While I was writing this exploit, an excellent reverse engineer achieved stable exploitation of CVE-2015-1701. The example code for user-mode callbacks was very useful. Thanks to the author.
It is worth noting that my analysis below was done on Windows 7 because it seems to be the only version where all structures in win32k.sys have corresponding symbols. Most of these symbols are applicable to structures in other versions of win32k.sys. For some reason, Microsoft removed these symbols starting from Windows 8.
Finally, I want to say that my exploitation method is quite complex. It is entirely possible that there is an easier way to do it; I just haven't found it. I would love to hear about different methods used by others. Either way, I hope all of this is helpful for researching win32k.sys vulnerabilities.
Let's take a look at the bug in the disassembly of win32k!xxxEnableWndSBArrows. This is a rather subtle bug:
Unpatched version:
.text:FFFFF97FFF1B157D mov r8d, r13d
.text:FFFFF97FFF1B1580 mov rdx, r14
.text:FFFFF97FFF1B1583 call xxxDrawScrollBar ; triggers usermode callback
.text:FFFFF97FFF1B1588 jmp short loc_FFFFF97FFF1B1519
[...]
.text:FFFFF97FFF1B1519 mov eax, [rbx] ; references tagSBINFO pointer without checking
.text:FFFFF97FFF1B151B mov ebp, 0FFFFFFFBh
.text:FFFFF97FFF1B1520 xor eax, esi
In the code above, Win32K!xxxDrawScrollBar can call back to user space under appropriate circumstances. In user space code, the tagSBINFO pointer may be freed by the attacker. When returning to the code above, the instruction at 0xFFFFF97FFF1B1519 will reference an invalid pointer.
Patched version:
.text:FFFFF97FFF1D69C3 xor r8d, r8d
.text:FFFFF97FFF1D69C6 mov rdx, rbp
.text:FFFFF97FFF1D69C9 call xxxDrawScrollBar ; triggers usermode callback
.text:FFFFF97FFF1D69CE cmp rbx, [rdi+0B0h] ; checks if tagSBINFO pointer is correct
---.text:FFFFF97FFF1D69D5 jz short loc_FFFFF97FFF1D69E4; if correct, continue original flow
| .text:FFFFF97FFF1D69D7 mov rcx, rbp
| .text:FFFFF97FFF1D69DA call _ReleaseDC
| .text:FFFFF97FFF1D69DF jmp loc_FFFFF97FFF1D6958 ; jump to function exit
|->.text:FFFFF97FFF1D69E4 mov eax, [rbx] ; safely use correct tagSBINFO pointer
.text:FFFFF97FFF1D69E6 xor eax, r14d
In the patched version above, we see a null check before using the tagSBINFO pointer. Relevant structure information will be provided later.
In implementing this exploit, we performed multiple corruptions. The vulnerability was triggered during one of these corruptions.
The technical root cause of this bug is a use-after-free (UAF) on the desktop heap. Initially, this confused me because I was unfamiliar with the user-mode callback mechanism in win32k.sys and how it works. Therefore, I thought it was a lock race condition leading to UAF. In fact, the locks on that structure were correctly used, and the flow was as expected. In short, the real reason is:
That's it. Without considering user-mode callbacks, this stage is quite straightforward.
But how do we perform the corruption, and why? As mentioned in Udi's blog, you can set or clear 2 bits in a location that the system code considers the WSBflags field in the tagSBINFO structure. This is not a standard UAF exploitation approach, but the article gave a hint on how to do it, which I will explain in the following sections. First, let's understand how to manipulate these bits.
tagSBINFO structure (consistent on 32-bit and 64-bit):