CVE-2014-4113 Detailed Vulnerability and Patch Analysis

  • Posted on: 24 October 2014
  • By: siteadm

As you might have heard, Microsoft recently patched some vulnerabilities; vulnerabilities related to Sandworm CVE-2014-4114 (Powerpoint exploit) and font parsing (CVE-2014-4148). But in this article, I'm more interested to talk about CVE-2014-4113, which is a local kernel vulnerability that successful exploitation would give you SYSTEM access. So I started analyzing patch (KB3000061) and mid-analysis, I found a PoC for this vulnerability in the wild. Therefore I combined my patch analysis and reverse engineering of PoC binary together to deeply understand this vulnerability and exploitation technique. Here, I'll share it step by step, with all details, so you'll know everything about CVE-2014-4113.

First of all, I downloaded KB3000061 and I noticed that it just does have win32k.sys inside. So I created two folders called Vulnerable and Patched and placed vulnerable and patched versions of win32k.sys inside them. Next, I loaded both of them in IDA and saved both databases. As next step I had to see what've changed in patched version, so I chose TurboDiff for this job. TurboDiff simply gives you a plain-text table of changed functions. For this particular patch, it gave me 25 changed function. I started checking each changed function and one of them caught my attention. In the internal function of xxxHandleMenuMessages, I noticed that patched version does have an additional check for returned value from xxxMNFindWindowFromPoint (internal function). That check was a call to IsMFMWFPWindow function and parameter to IsMFMWFPWindow was exactly return value of xxxMNFindWindowFromPoint. So I figured out that here something was wrong and Microsoft added a code to check return value of xxxMNFindWindowFromPoint. You can see change here:

Vulnerable win32k.sys

Patched version:

Take a look at both functions with zoom out, vulnerable part:

Patched part:

Knowing this, I started to think about exploitation method of this vulnerability, how to trigger this vulnerability and make xxxHandleMenuMessages API to call xxxSendMessage with an invalid HANDLE. As soon as I saw that the vulnerability is related to window system and possible NULL value during xxxHandleMenuMessage process, I just remembered this. For exploiting that, you had to map zero/null page, create a fake object at zero page and trigger the vulnerability. So this was the general idea, now I had to find a way to trigger this vulnerability by causing xxxHandleMenuMessage call xxxSendMessage with a NULL handle. Luckily I suddenly saw that there is a PoC published online for this vulnerability, so as a lazy person, instead of trying to solve it on my own as a practice/challenge, I just downloaded the sample and started to analyze it. Lots of things was as I thought, the only missing part in chain of exploitation was how to make that NULL in xxxHandleMenuMessage. It was done by hooking and altering parameters in user mode, you can read more about these tricks here and here.

So basically the PoC deletes the menu and returns -5, so xxxSendMessage will use a tagWND object starting from -5 (0xFFFFFFFB) to positive values which is in user-mode. The PoC allocated zero-page using ZwAllocateVirtualMemory with 0x01 as base address and writes a fake tagWND object here. Windows allows zero page allocation for 16-bit application compatibility/support. So the tagWND object have two important parts, one is WS_EXECUTE_IN_KERNEL flag which is at offset ((BYTE*)&pWnd->state)+0x02 and the other one is callback function which is at offset 0x60.  So by setting 0x16 to 0x04, you are telling kernel that the callback function at 0x60 needs to be executed in kernel. So the PoC modifies return value (returns -5) by hooking, but before that, it writes 0x04 to 0x16 - 0x05 (0x11) offset (WS_EXECUTE_IN_KERNEL flag) and address of shellcode at 0x60 - 0x05 (0x5B) -which will be executed in kernel-. See:

The shellcode does nothing other than replacing current process token with SYSTEM process (pid = 4) token. Therefore, any process created by current process will have SYSTEM token too. Here is the shellcode:

and here is what zero page looks like:

Cyan color holds address of shellcode and yellow background holds WS_EXECUTE_IN_KERNEL flag.

For triggering the vulnerability, simply it creates two layer popup menu (one main popup menu and one sub menu inside it), then it calls TrackPopupMenu to trigger the hook. In hook function, it replaces window handler using SetWindowLongA API, see:

and new handler function, simply deletes popup menu and returns 0xFFFFFFFB (-5):

That was it. Using this simple hooking and altering popup menu, kernel will call and execute callback function at null page, in kernel space. I think I've said enough about this bug. So please update your OS, specially servers, as soon as possible, because lowest access (for example ASP.NET shell with ASP.NET user account) can get SYSTEM access using this vulnerability.

Comments

Excellent post, you write very good and detailed. Please keep it up.

Nice post, just a question. You said using ZwAllocateVirtualMemory you can map zero page and reason for this is "16-bit application support", can you explain this a little more?

16-bit support in modern windows systems are done by NTVDM. NTVDM, in order to implement kernel mode INT10 Video API (see this) which modifies value in zero page, needs to map the zero page. Therefore Windows have to allow this null page mapping.

I like your blog and your posts, I really do, just sometimes I don't like software you use for the job. For example, why Turbodiff? Why not DarunGrim, BinDiff, Patchdiff?

BinDiff is commercial and I think they don't sell it anymore. I prefer TurboDiff because it's "turbo", does the job fast and gives you a plain text file. I just like it. :)
 

Your write is very special. everything clear and understood.
Please keep it on... :)

Hi,

I have a question. I guess you are wrong regarding ZwAllocateVirtualMemory().

Excerpt from MSDN:
BaseAddress [in, out]
A pointer to a variable that will receive the base address of the allocated region of pages. If the initial value of this parameter is non-NULL, the region is allocated starting at the specified virtual address rounded down to the next host page size address boundary. If the initial value of this parameter is NULL, the operating system will determine where to allocate the region.

According to MSDN, if you provide BaseAddress as 0x0, Windows will decide where to allocate the memory. I have reversed the exploit and, they too are not using BaseAddress as 0x0 instead, they are using 0x1.

Let me know if I'm wrong.

Thank you for reading the article carefully, yes, you are definitely right, I fixed article accordingly. You need to pass 0x01 to map null page.

shellcode is written terribly illiterate. cause PsLookupProcessByProcessId and not cause ObfDereferenceObject then! and that in the process contains no direct pointer to the token, but EX_FAST_REF author knows ?? he carries incorrectly cached references to the system token. (not backed by real links in the token). as result if enough times call PsReferencePrimaryToken/ObfDereferenceObject for the system and the current process - you can get a BSOD. and of course the original process token author did not even think to release !! for such code quality need kill. and the rest of the code beneath criticism. a bunch of global variables, no data encapsulation, not the best solutions. offset to Token field in process is hardcoded based on system version :)) found way in runtime calc this, without hardcode author cannot ? too hard for him ?? complex search for THREADINFO*, when it direct in TEB stored.. in general be able to find errors in the system is one thing, but to be able to qualitatively encode - is another

Thanks for your comment and I should say I totally agree. Token offset was hardcoded based on Windows version, cached reference to system token can (even though rare) case BSOD and use of bunch of global variables in the PoC doesn't seem to be a good idea. I think sample PoC, as it's name says, was just "proof-of-concept". So it did the job as "proof-of-concept", but definitely it wasn't most efficient code to exploit this vulnerability.

Of course this "proof-of-concept" but if already spread on the public you should hone code. so that the code can be proud :) personally took me about 2 days to process this "proof-of-concept" in filigree code (of which the first day on the reverse) - with correct token replace, no hardcoded token offset, no global vars,etc... I think the author would be worth to spend 1-2 days longer to finalize the code)

I do not understand something.
Weakness occurs when not checked the returns -5 (0xFFFFFFFb) and pass it to function xxxSendMessage.
According to the first image (Vulnerable win32k.sys) actually seems that ensure that the value -5 inserted.

cmp ebx, 0FFFFFFFbh
jz short loc_BF93C094

What am I missing?

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.