Wednesday, March 29, 2017

0patching the "Immortal" CVE-2017-7269

A Remote Code Execution Bug on 60,000 Officially Unpatchable Servers

By Mitja Kolsek, 0patch Team

Two days ago, an interesting exploit by Zhiniang Peng and Chen Wu was published on GitHub. It's a simple Python script that you launch against any 32-bit Windows Server 2003 with WebDAV functionality enabled, and it executes calc.exe on that server. Needless to say, this exploit could easily be modified to download a malicious executable to the server and launch it, and we confirmed it can also be used against 64-bit servers. To fully compromise a server, the exploit would have to escalate its privileges from NETWORK SERVICE user to LOCAL SYSTEM, where tricks like this one or this one might come handy.

Now, what makes this exploit so interesting?

  1. According to Shodan, there are a little over 600,000 publicly accessible IIS 6.0 servers on the Internet, most of them likely running on Windows Server 2003. (This doesn't include an unknown number of servers not accessible from the Internet.)
  2. The extended support period for Windows Server 2003 ended 20 months ago, so there is no official security fix for this issue.

Fortunately, most 2003 servers don't have WebDAV functionality enabled, but it is likely enabled on many SharePoint Portal servers and sites that use it for remote Web authoring or similar. Iraklis Mathiopoulos ran a sample probe to determine how many of Internet-connected 2003 servers are WebDAV-enabled and found that approximately 10% of them are. So we seem to have about 60,000 servers out there exposed to this freshly published exploit.

Sure, some will say that everyone should have stopped using Windows Server 2003 long ago as it doesn't get security patches any more. But owners of these servers each have their own story, their own set of constraints to work within, and their own budgets that they would rather spend on something other than upgrading a server that works.

To help maintainers of Windows Server 2003 computers block almost inevitable attacks under these unfavorable circumstances, we decided to provide them a free solution: a micropatch for CVE-2017-7269, which they can apply on their machines not only without rebooting, but also without even restarting Internet Information Services. (For those new to 0patch, this is how we believe applying security patches should always look like.)

Let me describe the process of analyzing this vulnerability and creating a micropatch.

The first step in our analyses is always reproducing the exploit or proof-of-concept. In this case, it was trivial: exploit.py provided by Peng and Wu was simple and effective, and it reliably launched calc.exe on a local 32-bit Windows Server 2003.




A full-blown exploit is usually not ideal for analyzing the vulnerability; especially in a buffer overflow case, we would prefer the process crashing at its vulnerable location to launching the calculator. So I "dumbed down" the exploit by replacing all of its shell code with A's (0x41 bytes). I also used WinDbg's Global Flags to enable Page Heap on the vulnerable w3wp.exe process in hope to catch the buffer overflow immediately as it occurs.

Sure enough, after attaching to w3wp.exe and launching my simplified poc.py, an access violation was caught due to writing beyond an allocated memory block. The offending instruction was at location httpext!ScStoragePathFromUrl+0x360:

rep movs dword ptr es:[edi],dword ptr [esi]

Those familiar with Intel machine code will recognize rep movs as a memory copying procedure. So apparently the overflow occurs when one buffer is copied into another - which is not large enough. Setting a breakpoint at the beginning of the memory-copying code block revealed that a buffer pointed to by esi (source) contained a 433-wide-character string (i.e., 868 bytes including the trailing zero) "/aaaaaaaAAA...(many A's)...AAA>", which is the exact value of one of the tokens sent in the WebDAV request by poc.py. The destination buffer pointed to by edi, however, was approximately 50% too small to receive the string. This provided an initial hint that it could be a case of using wide (two-byte) characters but allocating a buffer based on the number of characters instead of the number of bytes they take.

Anyway, in order to find where the too-small destination buffer pointed to by edi was allocated, I used this nifty WinDbg extension !heap -p -a edi, which gave:

7c83d6d4 ntdll!RtlAllocateHeap+0x00000e9f
5b7e1a40 staxmem!MpHeapAlloc+0x000000f3
5b7e1308 staxmem!ExchMHeapAlloc+0x00000015
67125df9 httpext!CHeap::Alloc+0x00000017
67125ee1 httpext!ExAlloc+0x00000008
67125462 httpext!HrCheckIfHeader+0x0000013c
6712561e httpext!HrCheckStateHeaders+0x00000010
6711f659 httpext!CPropFindRequest::Execute+0x000000f0
6711f7c5 httpext!DAVPropFind+0x00000047
671296d2 httpext!CDAVExt::DwMain+0x0000012e
67117bc6 httpext!DwDavFSExtensionProc+0x0000003f

...

It seemed like the allocation of our destination buffer occurred in httpext!HrCheckIfHeader as a result of calling CStackBuffer::resize.

push    [ebp-434h]
lea     ecx, [
ebp-430h]
call    ?resize@?$CStackBuffer@G$0BAE@@@QAEPAGI@Z
test    eax, eax
jz      loc_155A2


So I set a breakpoint at the top of this code block to see what this resize was all about. It turned out that the local variable [ebp-434h] holds the length (in characters) of the input string, and the above code wants to resize an existing buffer to be able to hold this entire string.

My suspicion was confirmed: instead of specifying the number of bytes to allocate, the above resize call gets the number of characters - making the resulting buffer much too small. Looking around the above code, I found a similar code block where this calculation is done correctly, which further confirmed my analysis.

At this point, it was easy to decide how to patch: instead of pushing [ebp-434h] to stack as an argument to the resize function, we should push that same value multiplied by two and further increased by 2, to accommodate for the string-teminating zero. Since I didn't want to modify the local variable [ebp-434h] as it is also being used elsewhere, I decided to replace the push [ebp-434h] instruction with the following:

mov     eax, dword [ebp-434h]
lea     eax, [eax+eax+2]
push    eax


So I wrote a .0pp file for this and built a 0patch using our 0patch Builder (which, by the way, anyone can download as part of 0patch Agent for Developers), then I relaunched poc.py to see if my micropatch fixed the bug.

To my surprise, w3wp.exe still had an access violation in the same place. After checking that I didn't snafoo the patch, I traced the flaw again in the same way as before, only to discover that another, almost identical, incorrect buffer allocation takes place elsewhere in the code. Specifically, in httpext!CParseLockTokenHeader::HrGetLockIdForPath.

This second flaw was logically identical to the first one, and could be patched with effectively the same patch code using an additional patchlet. It was time to give poc.py another try and see how IIS reacts to it with the two patchlets applied.

Triumphantly, IIS returned "HTTP/1.1 207 Multi-Status" instead of reporting internal server error, and it did the same when the original exploit.py was launched against it. This was it, the bug was fully patched.

In conclusion, let's see the source code of this micropatch.


;target: Windows Server Standard 2003 R2 32bit, httpext.dll version 6.0.3790.4518 32bit
MODULE_PATH "C:\WINDOWS\system32\inetsrv\httpext.dll"
PATCH_ID 269
PATCH_FORMAT_VER 2
VULN_ID 2287
PLATFORM win32

patchlet_start
 PATCHLET_ID 1
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x00015451
 JUMPOVERBYTES 6 ; eliminate the original "push dword ptr [ebp-434h]"

 code_start

   mov     eax, dword [ebp-434h]
   lea     eax, [eax+eax+2]
   push    eax

 code_end
patchlet_end

patchlet_start
 PATCHLET_ID 2
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x0001574b
 JUMPOVERBYTES 6 ; eliminate the original "push dword ptr [ebp-22Ch]"

 code_start

   mov     eax, dword [ebp-22Ch]
   lea     eax, [eax+eax+2]
   push    eax

 code_end
patchlet_end


You can see that the patch comprises two patchlets, both containing the same three machine code instructions, just at different offsets into the vulnerable httpext.dll and with different offsets to the relevant stack-based local variable. Extremely lightweight, extremely unlikely to cause unwanted side effects, and extremely reviewable - just the way micropatching is supposed to be.

If you have 0patch Agent installed on your Windows Server 2003, patches ZP-269 and ZP-270, for 32-bit and 64-bit server, respectively, should already be present and applied. If not, you can download a free copy of 0patch Agent to protect your server from CVE-2017-7269. Note, however, that these patches are only applicable to a fully updated server with Service Pack 2 installed, as they were made specifically for 32-bit and 64-bit httpext.dll version 6.0.3790.4518. (Check your httpext.dll version in C:\Windows\System32\inetsrv.)

Finally, you should know that we're still in beta so keep an eye on your server and report any unusual events to us at support@0patch.com. Also let us know if you have some other version of httpext.dll and we'll try to port the patch for you.

@mkolsek
@0patch

Thursday, March 9, 2017

0patching another 0-day: Internet Explorer 11 Type Confusion (CVE-2017-0037)


By Luka Treiber, 0patch Team.

[Update 3/27/2017: After Microsoft has patched this issue, security researcher Alisa Esage posted a proof of its exploitability on Twitter. It is therefore clear that it was possible to both (1) set rax to a attacker-controlled address, and (2) bypass the Control Flow Guard check.]

A week has passed since my last post so it seems about time to release another 0patch into the vacuum between regular Microsoft Update Tuesdays. It's been an interesting week because our 0patch platform (with open beta in progress) got an infusion of new users providing a lot of useful feedback.

This time I present you with a patch for IE11, providing protection for a vulnerability more severe than the previous one. One that is said to potentially allow for remote code execution. Again I based my analysis on a bug report provided by Google Project Zero, who automatically derestricted it after 90 days deadline. This time Ivan Fratric holds the credit.

The PoC (which can be extracted from the top of Ivan's report) is a short HTML file in which - upon opening in IE11- JavaScript dynamically reformats StyleSheet properties of an HTML table in a way that causes type confusion, ending in a crash. Inspecting the crash with Application Verifier enabled and WinDbg attached I got the same results as reported:

(430.1a4c): Access violation - code c0000005 (!!! second chance !!!)
MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0xa4:
000007fe`ddeeba17 48833800        cmp     qword ptr [rax],0 ds:00000078`00000070=????????????????


I executed !heap -p -a @rbp to acquire the call-stack of corrupt object's creation. It showed that the illegal rax address originated in 

Array<int>::Create

There was no indication of a typical memory corruption such as a buffer overflow or a use-after-free directly related to the crash which could be analyzed and patched in an almost standard way (see our previous blog posts). I therefore concluded that a less deterministic and more tedious analysis was necessary to crack this bug. What was needed was a set of probes derived from the PoC that separate a correct execution flow from an illegal one. Those probes could then be traced in WinDbg and trace-dumps compared. What I found was that
  • the boom()call causes a crash only if triggered by an event separate from page load, e.g., a click or a timer event,
  • instead of "aaaaaaaaaaaaaaa", mediaText can be set to a legitimate value such as print, handheld, projection, etc. and the browser will still crash and
  • statically defined media (inside a css definition) does not produce a crash.
Attaching to the probes I traced the life-cycle of the said Array<int> down to the crash as well as the differences between various situations where mediaText and th1.align are set. In my limited time I could compare several combinations of execution flow traces of said probes, but ended up with no solid understanding of where the illegal parsing behavior kicks in. Time was not on my side so I chose to resort to an easier solution: functionality amputation. If your fingernail causes you to stumble, cut it off ...

Based on my findings above this means disablement of mediaText setter. But first let's see what mediaText is about. According to W3C it is responsible for getting and setting a collection of media queries. By using media queries, presentations can be tailored to a specific range of output devices without changing the content itself. So basically we're about to interfere with web page layout. If you look at the examples given by W3C, all of them use static media definitions which the patch I'm about to propose does not affect because it will disable only functions exposed to JavaScript that the published PoC relies on. Whether it is worth protecting against a potential remote execution threat in exchange for possible page styling errors is for you to choose (0patch Agent allows disabling of the patch if you cannot live with it). Now let's see how the patch works.

Below is the content of my 0patch source file.  It defines two patchlets for module mshtml.dll enclosed between patchlet_start - patchlet_end directives. Patchlet locations were obtained by tracking every possible setter on a CSSMediaList object accessible from JavaScript. I found two such locations:
  • media.mediaText setter that maps to CFastDOM::CMediaList::Trampoline_Set_mediaText 
  • media.appendMedium method that maps to CFastDOM::CMediaList::Trampoline_appendMedium
I patched both setters so that their function bodies are skipped entirely (using the JUMPOVERBYTES directive) and their result is set to SUCCESS via xor rax,rax.

;target OS: Windows 7 64bit
;

RUN_CMD C:\Program Files\Internet Explorer\iexplore.exe C:\0patch\Patches\CVE-2017-0037\poc.html
MODULE_PATH "C:\Windows\System32\mshtml.dll"
PATCH_ID 265
PATCH_FORMAT_VER 2
VULN_ID 2140
PLATFORM win64


patchlet_start
 PATCHLET_ID 1
 PATCHLET_TYPE 2

 PATCHLET_OFFSET 0x00f79cc0
 JUMPOVERBYTES 211;
 
 code_start
   xor rax,rax
; return success
 code_end
patchlet_end

patchlet_start
 PATCHLET_ID 2
 PATCHLET_TYPE 2

 PATCHLET_OFFSET 0x00fa3f80
 JUMPOVERBYTES 197
 
 code_start
   xor rax,rax ; return success
 code_end
patchlet_end

After compiling this .0pp file with 0patch Builder, the patch gets applied and IE11 browser survives the PoC without a crash.

If you have 0patch Agent installed, patches ZP-265 through ZP-268 should already be present on your machine. If not, you can download a free copy of 0patch Agent to protect yourself from exploits against the presented issue while waiting for Microsoft's official fix. Note that when Microsoft’s update fixes this issue, it will replace the vulnerable mshtml.dll and our patch will automatically stop getting applied as it is strictly tied to the vulnerable version of the DLL. We have deployed this patch for the following platforms: Windows 10 64bit, Windows 8.1 64bit, Windows 7 64bit and Windows 7 32bit.

Patching this 0day was a good opportunity to demonstrate how patching process in our 0patch Team works. We rarely release patches that would replace official vendor fixes. In general we aim to bridge the gap between a release of vendor update and the time this update gets installed on vulnerable machines. Note that in case of the presented patch we're not talking only about 5 days until the next Patch Tuesday -  for many companies the gap is caused by weeks or months of testing before official update batch can be applied to their networks.

So we rather release a simple temporary patch that blocks an attacker than try to create a perfect patch. A much thorougher and better analysis of this bug can and will be done by Microsoft. Browsers are certainly among the most complex applications so with my black box analysis tools and a limited time-frame I don't fool myself that I could get to the bottom of all the weird things an HTML apparatus does. On the other hand Microsoft developers have the source code, the right tools and knowledge to properly fix this bug and probably wont even blink while getting it done.

Feel free to use 0patch Agent with this patch to protect yourself from attacks against CVE-2017-0037 until Microsoft provides an official fix (which we absolutely recommend you apply). Just remember that we're still in beta ;-)