Monday, May 15, 2017

0patching the "Worst Windows Remote Code Execution Bug in Recent Memory" CVE-2017-0290


Building up our Skills and Speed for the Future WannaCry Attacks

By Mitja Kolsek, 0patch Team

Like many other stories of the past week, mine begins with this tweet.



Natalie Silvanovich and Tavis Ormandy of Google Project Zero found a pretty nasty bug in Microsoft Malware Protection Engine, allowing an attacker to execute arbitrary code as LocalSystem on any Windows computer running any Microsoft anti-malware product such as Security Essentials or Windows Defender by simply having that computer access a malicious file. Attack vectors were abundant, from emailing the file or sending it via any other channel like Skype or Messenger, to having it hosted on a malicious web site or uploading it to an IIS web server.

Unlike many other stories of the past week, mine is not about how Natalie and Tavis found this bug, how they reported it to Microsoft or how the fact that they found and reported it was made known to the public. Rather, it is about the bug itself, its root cause, and - of course - about writing a micropatch for it.

But first: why would we want to write a micropatch for a vulnerability that would quickly get automatically fixed on all Windows computers anyway? As you may know, Microsoft was super fast in fixing this bug and made an update available literally over the weekend. Furthermore, the Malware Protection Engine is implemented as a dynamic-load library mpengine.dll, and Microsoft designed their anti-malware products smartly enough to not require a computer restart - the old DLL is simply unloaded, and the new one loaded.

So why write a micropatch? Well, not every computer gets updated automatically: while automatic application of updates is configured by default, admins can change that if they want to control what gets applied when. And enterprise admins like to have such control, allowing them to test new code before deploying it to computers throughout their organization. Just imagine the updated mpengine.dll having a flaw that prevented users from accessing legitimate files.

Another reason for writing this micropatch was to learn, as we haven't patched a security product before - and one can expect to stumble upon something new here (and stumble I did, as you will see). The final reason was to teach, to share some knowledge with those of you who want to analyze vulnerabilities yourselves and learn how to write micropatches.


Reproducing CVE-2017-0290

The first step in analyzing a vulnerability is to reproduce its exploitation. The Project Zero report provides a downloadable proof-of-concept file, which has a .zip extension, but is really an HTML-lookalike file that comprises a tiny exploit bit and a lot of random HTML content that makes sure the engine processes the file.

Reproducing on 64-bit Windows 8.1 was trivial - just downloading and saving the file was enough to make the Windows Defender service crash, instantly turning from this:



 to this:





After the crash, the Application Event Log contained an Error event about this crash, revealing the crashing module being mpengine.dll, and the crash location being at offset 0x21745a. (You will find a different crash address in Google's report because they were working on a 32-bit computer.)




Note that I was using mpengine.dll version 1.1.13701.0, which is the last vulnerable version before the fixed 1.1.13704.0. It is always good to do your analysis on the last vulnerable version in order to minimize the difference with the fixed version - you will thank yourself when diffing these versions.


Analyzing CVE-2017-0290

With the bug successfully reproduced, the path was clear towards analysis. Here, the Google report was a great start, as Natalie and Tavis have clearly gained substantial understanding of what goes on in the crash case. The most important detail for me was that it was a type confusion error, specifically with some function expecting a string object but getting a number object (which resulted in calling a string vtable function where there really was no vtable).

This was important because when a bug is a type confusion error, a typical fix is to add the missing check for the correct type. And such a fix is usually easy to recognize when observing the difference between vulnerable and fixed code.

Which brings us to IDA. The image below shows the function that crashed - the exact access violation location was the mov rax, [rcx] instruction (see the red box) at address 0x75A31745A, which is at offset  0x21745a from mpengine.dll's default base address.




When a vendor patch is available, diffing the vulnerable and the patched version usually provides useful information and allows you to understand the bug, and the patch, better. Diffing can be a time-consuming operation though first for the computer and then for you, and with large binaries (mpengine.dll is 12MB) you can get a lot of matched functions, and finding where the code logic is different - as opposed to where the code is different - can be somewhat frustrating.

So I went on to diff the two versions of mpengine.dll, the vulnerable 1.1.13701.0 and the patched 1.1.13704.0. There were 38440 matched functions, which, in scientific terms, is an awful lot. What I could do with these results was compare the above crashing function between the two versions. If I was lucky, the patch would be there and I could go home early.




Nope. Both functions are logically identical, which means that the flaw (and the patch) is somewhere higher on the call stack. At this point one could diff all functions that call this function, but there are about 50 of them - and if all of those turned out to be identical as well, such approach could turn into an exponential mission impossible. (Not to mention that IDA may not see all callers.)
 
Now about the call stack: you will notice that I haven't used a debugger up to this point, and the reason is that Windows Defender is a protected service and as such tries very hard to protect itself from tampering. You cannot attach a debugger to a protected process, even if you're a local administrator. And it's not easy to un-protect a protected service either: while its protected status is defined by the LaunchProtected registry value (in our case under HKLM\SYSTEM\CurrentControlSet\Services\WinDefend), you cannot change that value for Windows Defender while Windows Defender is running as it prevents you from "attacking" it.

Fortunately, we have a way to stop Windows Defender - by crashing it with the PoC. So what I did was crash Windows Defender, rename its LaunchProtected registry value, restarted the computer (the protected status of services is read only at system startup), then configured Windows Error Reporting to generate dump files for crashing processes. (I only created the LocalDumps key and the DumpFolder value containing "C:\dumps" in it.)

After crashing Windows Defender again, I got its mini dump file in C:\dumps, and it contained a full call stack for the access violation. I was only interested in locations from mpengine.dll:

mpengine!FreeSigFiles+0x11ea9a 
mpengine!FreeSigFiles+0x12046f 
mpengine!FreeSigFiles+0x111e81 
mpengine!FreeSigFiles+0x111d9e 
mpengine!FreeSigFiles+0x125eaa 
mpengine!FreeSigFiles+0x3de1d  
mpengine!FreeSigFiles+0x3dbf5  
mpengine!FreeSigFiles+0x125eaa 
mpengine!FreeSigFiles+0x117ade 
mpengine!FreeSigFiles+0x120146 
mpengine!FreeSigFiles+0x113d76 
mpengine!FreeSigFiles+0xcce7f  
mpengine+0x54a99               
mpengine+0x865e1               
mpengine+0x50f3f               
mpengine+0x50d1f               
mpengine+0x8c208               
mpengine+0x8bf47               
mpengine!FreeSigFiles+0x174a3  
mpengine+0x13b7d               
mpengine!FreeSigFiles+0x1535a  
mpengine!_rsignal+0x243        
mpengine!_rsignal+0xe7  
 

The top one we already know - it's the access violation location in the crashing function that we diffed just moments ago. So I proceeded with the second address, FreeSigFiles+0x12046f, and located it in IDA. It was, as expected, after a call to the crashing function. I then took the address of the function containing that address, and viewed the diff with its patched version.




Now we're talking! This looks like a typical added check that exits a function if something is not right. (The patched version is on the left.) The upper red block is added code that takes rdi (the object) and passes it to a call to some function, and if the result of that function is 4, the execution continues as before, otherwise the return value (al) is set to 0 in the lower red block, and the function exits. The function that gets called from the upper red block seems to determine the type of the object and returns its type code. Reviewing other calls to this function I found a very obvious implementation of JavaScript's typeof operator, which confirmed that type code for string is actually 4.

This is clearly the patch I was looking for. It was simple, it did exactly what I was expecting it to do, and it was in the code path of our crash.


Micropatching CVE-2017-0290

My goal at this point was to create a micropatch that would inject the same patch logic into the vulnerable version of mpengine.dll. In a perfect world, I could use literally the same code that I found in the patched version, and inject it in the same place - but in this world a compiler likes to use different registers and different implementation of the same logic in two subsequent builds. So I had to re-implement the patch logic from the original patch.

Let's look at the vulnerable function in IDA.




The above image shows the vulnerable function and a good location for injecting our code. The location is selected so that it allows us to jump from our patchlet code directly to the function epilogue (the lowest block of code).

Here is the patch code for 64-bit mpengine.dll version 1.1.13701.0:


;0patch for CVE-2017-0290 in 64-bit mpengine.dll version 1.1.13701.0

MODULE_PATH "C:\Analysis\CVE-2017-0290\mpengine.dll_64bit_1.1.13701.0\mpengine.dll"
PATCH_ID 271
PATCH_FORMAT_VER 2
VULN_ID 2436
PLATFORM win64


patchlet_start
 PATCHLET_ID 1
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x218E10

 ; We'll need the GetTypeOf function and the location of function epilogue
 PIT mpengine.dll!0x218940,mpengine.dll!0x218E9A

 ; Note that GetTypeOf taints the following registers:
 ; rdx - always
 ; rcx - only in case of an exception
 ; rax - expected, this is the return value
 
 code_start

  push rcx          ; We need to preserve rcx, as it's still used after our patchlet code
                    ; while GetTypeOf taints rdx, we don't need to preserve it
  mov rcx, r9       ; r9 points to the object
  call PIT_0x218940 ; GetTypeOf object
  pop rcx           ; restore rcx
  cmp eax, 4        ; is the object of type string?
  jz OK             ; It is? Very well, continue...

  xor al, al        ; It isn't? Exit this function without doing anything, return 0
  call PIT_ExploitBlocked ; Show "Exploit attempt blocked"
  jmp PIT_0x218E9A  ; Jump to function epilogue
 
  OK:

 code_end
patchlet_end


What this single-patchlet patch, inserted at the shown point in code, does is - just as the original patch - call GetTypeOf on the object (whose address is in register r9) and see if its type code is 4 (string). If it is, it continues execution of original code where it was injected . Otherwise, it sets the return code (register al) to 0 and jumps to function epilogue.

Note that in order to avoid any negative side effects, I had to (1) review the GetTypeOf function to see which registers it may taint and whether that could impact the code after our injected patch (it taints rdx and rcx, but rdx holds nothing valuable at our injection point), and then (2) store rcx on the stack before calling GetTypeOf function because rcx holds some value that is still being used after our injected patch.

I also wrote the same patch for the last vulnerable 32-bit version of mpengine.dll. If you have 0patch Agent installed, patches ZP-271 and ZP-272 should already be downloaded to your computer, waiting for any occurrence of the vulnerable mpengine.dll getting loaded.


The Irony of Protected Services


To restore the original system configuration, I turned Windows Defender back to a protected service, and... shoot, the patch stopped getting applied. It quickly became clear that we can't inject our loader into the protected Windows Defender because only binaries signed by Microsoft are allowed to get loaded. (It's a bit more complex than that but close enough.) This is Windows Defender protecting itself against local malware - even with admin privileges - trying to compromise it.

The irony is that a Windows anti-malware protection prevents our security product from fixing a vulnerability in Windows Defender, while the exploit for the same vulnerability can freely execute arbitrary code in Windows Defender. (Hmm, perhaps we should leverage this exploit to get our code running inside Windows Defender and thereby fix it.) 

So while we're exploring options for extending our reach towards patching protected services, patches ZP-271 and ZP-272 for Malware Protection Engine will only get applied on Windows 7 and Windows Vista, which don't have protected services.  


Experimenting with Micropatches for CVE-2017-0290


If you want to experiment with these micropatches, you'll need two things:
  1. A 32-bit or 64-bit Windows 7 computer running Windows Defender. While you could also do the testing on newer Windows versions, you would have to un-protect the Windows Defender service in order to proceed. 
  2. Vulnerable mpengine.dll. If your Windows Defender doesn't happen to have this exact version (unlikely, due to automatic updates), you can get it here:
    1. 32-bit mpengine.dll version 1.1.13701.0 for 32-bit Windows
    2. 64-bit mpengine.dll version 1.1.13701.0 for 64-bit Windows
First of all, stop the Windows Defender service via elevated Services console.

Then browse to C:\ProgramData\Microsoft\Windows Defender (folder permissions don't originally allow you to open it so Windows will ask you for elevation, after which it will add your account to the folder ACL). Open folder Definition Updates, and notice one or more subfolders with GUID-like names starting with curly braces. Open each of these folders to find the one containing mpengine.dll. Rename the existing mpengine.dll into something else, then save the vulnerable mpengine.dll there.

Start the Windows Defender service.

Download the proof-of-concept file and store it in some empty temporary folder.

Launch the Windows Defender console via the Control Panel, and Custom-Scan the above folder. Notice that Windows Defender service crashes.

Now install 0patch Agent on the computer. If you don't already have it, download a free copy and register it with your free 0patch account.

Finally, restart the Windows Defender service and re-scan the temporary folder. This time, you'll see an "Exploit Attempt Blocked" popup instead of Windows Defender crashing.

If you want to build our patches yourself, you can download their source code and build them using 0patch Agent for Developers.


While this vulnerability has already been automatically fixed on most computers, it turned out to be an interesting learning experience to micropatch it. I hope this post will help future micropatchers jump-start their research.

While I was writing this post, the world got pierced by the WannaCry ransomware worm exploiting a known vulnerability that had an official patch available for Windows operating systems which Microsoft supported at the time. Many hospitals and other critical infrastructure components were taken offline, partly also because they were stuck with unsupported OSs such as Windows XP. They have very rational and complex reasons for being on such outdated systems in 2017, and undoubtedly they will have similar reasons next year and the year after. One of our goals with 0patch is to provide protection for such end-of-support systems while users scramble to update them (and have the same problem almost immediately afterwards). Defending against modern attackers will require rapid response, and this exercise with CVE-2017-0290 - although likely of low value to users - was an example of building up skills and speed. The world will need a lot of 3rd-party patch developers though, so all existing and prospective security researchers are warmly welcome to join us.

@mkolsek
@0patch





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 ;-)

Tuesday, February 28, 2017

0patching a 0-day: Windows gdi32.dll memory disclosure (CVE-2017-0038)

My first 0-day fixed (and I am not even a Microsoft developer)

By Luka Treiber, 0patch Team.

I've been fishing around the web for my next target to patch when something got caught into my net. Then along came Mitja and asked:
M:What's the catch?
-Well, zero...
M:Oh, that's a shame...
-Yeah, but not if it's a zero-day

As you've probably noticed, the last Patch Tuesday didn't make it. Consequently a number of 0-days are getting published, with CVE-2017-0038 being the first one on the list. But don't worry, every cloud has a silver lining. I had some free time last week to look into the matter and as a result I can give you the very first 0patch for a 0-day.

CVE-2017-0038 is a bug in EMF image format parsing logic that does not adequately check image dimensions specified in the image file being parsed against the amount of pixels provided by that file. If image dimensions are large enough the parser is tricked into reading memory contents beyond the memory-mapped EMF file being parsed. An attacker could use this vulnerability to steal sensitive data that an application holds in memory or as an aid in other exploits when ASLR needs to be defeated.

I have to kindly thank Mateusz Jurczyk of Google Project Zero for a terse and accurate report that allowed me to quickly grasp what the bug was about and jump on to 0patching it.

Mateusz's PoC worked as advertised - by launching an instance of IE 11 and dropping poc.emf onto it, this is the result I got:




The image had to be zoomed in to the max in order to see the intimidating heap data leaking through the colorful pixels. And it did leak consistently: reloading the PoC resulted in a different image on each reload.

In order to see the pixel values I put poc.emf onto an HTML canvas and printed the pixel values with JavaScript. Diffing the results after consecutive PoC reloads showed that the only pixel that didn't change was the one in the lower left corner (if you look really closely at the screenshot above you will find a red smudge right there).

In other words:

FF 33 33 FF

This is consistent with the report (one only needs to look closely enough). So the only pixel that originates from the actual data provided by the EMF file is that one (represented by the 4 bytes) - everything else is heap data read from out of bounds of the image pixel data.

Now let's go on to patching. Before we do that we need to understand where the vulnerable code is. Luckily the next thing that the report conveniently reveals is the name of the vulnerable function MRSETDIBITSTODEVICE::bPlay. This is important because the PoC produces no crash or exception that could be caught by a debugger for an in-depth analysis. With that piece of info in hand we can attach WinDbg to iexplore.exe and set a breakpoint

bp MRSETDIBITSTODEVICE::bPlay

According to the report this is where all the trouble is. It says cbBitsSrc is not checked against "the expected number of bitmap bytes" - the number which is calculated from cxSrc and cySrc image dimensions.

Here we need a little help from MSDN:
  • cxSrc
    Width of the source rectangle, in logical units.

  • cySrc
    Height of the source rectangle, in logical units.

  • cbBitsSrc
    Size of source bitmap bits.

After feeding WinDbg with symbols for EMRSETDIBITSTODEVICE type (explanation follows later) the explanation from the report finally got clear.



When processing the PoC EMF file, the EMRSETDIBITSTODEVICE structure (detailed in the lower right pane of the screenshot above) passed to the MRSETDIBITSTODEVICE::Play function as the first argument (rcx register that gets stored to rbx, marked in blue) contains cbBitsSrc set to 4, which does not match the 0x10 * 0x10 image dimensions defined by cxSrc and cySrc (these amount to 256 pixels or 1024 bytes).

So to fix the flaw a check has to be added before any data gets stolen. What principally needs to be done is adding a check whether cbBitsSrc is smaller than cxSrc * cySrc * 4 before any image pixels get copied around. An appropriate spot seems to be right before the first series of checks that validate the EMRSETDIBITSTODEVICE attributes (see cmp - jg pairs in the above code screenshot that end with a jmp to function exit in case any of the checks fail). However, selecting the exact patch location is not just about where to logically fit the modifications that we want to apply. The way patching works is that a jump is made from original to patch code and then back. At the patch location in original code a jmp instruction is written (consuming 5 bytes of original instruction space) that redirects execution to a memory block designated for patch instructions. The original instructions that were replaced by the jmp instruction are relocated to the end of that patch code block followed by a final jmp instruction directing execution back to original code right after the patch location. So there are some additional requirements that need to be met:
  • instructions at the patch location need to be relocatable (e.g., no short jumps);
  • at the patch location there must be either a single instruction or several instructions that belong to the same execution flow (cross-references must not point anywhere in-between them).
With this in mind I selected the exact patch location. In order to write as little patch code as possible I set the patch location at the first branch instruction that jumps to the function exit (avoiding the processing of image data) so I could piggy-back on it. On the code screenshot above, the patch location is marked in red while the repurposed jump instruction glows pink as a piggy. The result of pvClientObjGet call is checked against 0 at the patch location, followed by jne in order to jump to function exit (if rax is 0). The same jne that serves this logic can help our patch code exit the function: we only need to set rax to 0 if cbBitsSrc is smaller than cxSrc * cySrc * 4. 

It might seem desirable to select 000007fe`feeae3b8 as patch location at the first glance, but test rax,rax takes only 3 bytes so patching would also eat into the jne (which is not relocatable) because of the said 5-byte requirement. At the currently selected location, however, patching affects two 3-byte instructions - mov rsi,rax and test rax,rax - which are both relocatable.

Having selected the patch location we can finally write the actual patch. Below is the content of a .0pp patch file ready to be compiled with 0patch Builder that is included in 0patch Agent for Developers - usage of which we already discussed in a previous post.

At the beginning of the code_start section there is the cxSrc * cySrc * 4 calculation that gets stored in rcx. Both multiplications are followed by an overflow check so we don't end up with a product that seems within bounds but was produced by overlong factors. In case invalid image dimensions are detected, a call to a special function of our 0patch Agent is made in order to display an "Exploit Blocked" popup, and most importantly the rax register is set to 0 so that the following test rax,rax instruction can set the jump condition for the pink jne (resulting in the function exiting).

;target platform: Windows 7 x64
;

RUN_CMD C:\Program Files\Internet Explorer\iexplore.exe C:\0patch\Patches\ZP-gdi32\poc.emf

MODULE_PATH "C:\Windows\System32\gdi32.dll"
PATCH_ID 259
PATCH_FORMAT_VER 2
VULN_ID 2135
PLATFORM win64

patchlet_start
 PATCHLET_ID 1
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x0004e3b5

 ;NOTE: We checked that rcx is free to use.
 ; We don't care if rsi gets spoiled by block_it because in that case
 ; it will not be used by anyone any more


 code_start

   imul ecx, dword[rbx+28h], 04h ; cxSrc * 4 (each pixel is represented by 4 bytes)
   jc block_it ; if overflow
   imul ecx, dword[rbx+2ch] ; * cySrc
   jc block_it ; if overflow
   cmp ecx,dword[rbx+3ch] ; cbBitsSrc < cxSrc * cySrc * 4
   jbe skip
  block_it:
   call PIT_ExploitBlocked ; show "Exploit Blocked" popup

   xor rax,rax ; setting jump condition for the original jne after the patch

  skip:

 code_end
patchlet_end

By the way, the EMRSETDIBITSTODEVICE structure member offsets relative to rbx were obtained by dumping type structure using WinDbg's dt command:

0:034> dt symbollib!EMRSETDIBITSTODEVICE
       +0x000 emr              : tagEMR
       +0x008 rclBounds        : _RECTL
       +0x018 xDest            : Int4B
       +0x01c yDest            : Int4B
       +0x020 xSrc             : Int4B
       +0x024 ySrc             : Int4B
       +0x028 cxSrc            : Int4B
       +0x02c cySrc            : Int4B
       +0x030 offBmiSrc        : Uint4B
       +0x034 cbBmiSrc         : Uint4B
       +0x038 offBitsSrc       : Uint4B
       +0x03c cbBitsSrc        : Uint4B
       +0x040 iUsageSrc        : Uint4B
       +0x044 iStartScan       : Uint4B
       +0x048 cScans           : Uint4B

But since the type information is not available in WinDbg by just loading symbols from Microsoft's Symbol Server, the following workaround was needed. I compiled a bare-bone DLL project with only EMRSETDIBITSTODEVICE x; instance declaration as custom source. Then just before launching iexplore.exe I added the DLL to the AppInit_DLLs registry value so it got loaded into the process (thanks to this trick).

After compiling this .0pp file with 0patch Builder, the patch gets applied and instead of the rainbow image an empty image is displayed in the browser and an "Exploit Blocked" popup shows up.

Here is a video I captured that summarizes the described process.


So this is my first 0-day fixed. While not the most severe issue, I get shivers thinking that instead of the rainbow image, a malicious page could steal credentials to my online banking account or grab a photo of me after last night's party from my browser's memory.

If you have 0patch Agent installed, patches ZP-258 through ZP-264 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 gdi32.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.

If you would like to write patches like this yourself, do not hesitate to download our 0patch Agent for Developers and give it a try (we made the .0pp files available for download so you can build them yourself). We'll also happily accept any feedback.

Now onward to writing the next 0-day 0patch.

Friday, February 10, 2017

One Step Closer To Crowdpatching and Patch Bounties

Launching 0patch Builder

By Mitja Kolsek, 0patch Team

Things have been happening fast in the 0patch land lately: in the last few weeks we extended our OS coverage from Windows to Ubuntu and Fedora (still alpha, but major technical obstacles are out of the way), and created a micropatch that seemed to fix a remote execution vulnerability more thoroughly than vendor's original update. And now we're making probably the biggest step since the release of 0patch Agent: we're launching 0patch Builder (for Windows) - a tool that allows anyone to write a micropatch that can then be applied with 0patch Agent.

That's right - security researchers can now write their own "alternative" micropatches to fix known issues in a micro-surgical, low risk manner while admins are testing huge official updates for potential functional problems. Or they can micropatch those 0days they have just found in a popular closed-source product.

Software vendors can experiment with micropatching their own bugs to see how much easier and cheaper this approach can be compared to the status quo of petabytes of code being transferred over the Internet to effectively add a single bounds check on millions of computers (whose millions of users are then advised to take a coffee break as updates are being applied). Not to mention how easy and unnoticeable it can be to "un-apply" a micropatch: instead of installing and uninstalling massive updates, we're finally moving towards switching microscopic patches on and off.

0patch Builder is an essential component of the crowdpatching model we're building. Our vision entails thousands of security researchers, expert patch developers around the World writing micropatches for personal computers, servers, mobile phones, routers, smart TVs, fridges, online cameras, ATMs, light bulbs and smart meters. Many of them working for original vendors who will prefer to outsource patch development in order to keep their own resources on current projects, but some also writing patches for unsupported products and those whose vendors - let's put it this way - don't exactly assign high priority to security. We also envision patch bounties, a natural extension of today's bug bounties: why would vendors only pay researchers for finding bugs in their code, if the same researchers could also fix them? Sure, vendors will still decide whether to accept a patch or not but that will serve as feedback for patch developers to improve their skills and create better and better patches.

In our wildest dreams, an entire 3rd-party patching industry emerges, supported by not only security research but also scientific research on proving correctness of code micro-changes, engineering efforts for bringing micropatching engines to all devices (hey, why not provided directly by CPUs?), integrating micropatching support into development and reverse-engineering environments, automating patch generation from vulnerability-finding tools, and many other things we're not smart enough to imagine at this point.

But enough of this visionary stuff - how do you go about writing  your own micropatches?

First you have to download and install 0patch Agent for Developers, which comprises a slightly modified 0patch Agent and 0patch Builder. Then you write a patch source file as described in 0patch Developer Manual and compile that file with 0patch Builder. Once you're done, your patch will immediately get applied to the module you're patching in already running processes as well as in newly launched ones. We also made it really easy for you to debug your patches by automatically setting breakpoints on their entry points.

Now if you want to go ahead, read the 0patch Developer Manual for detailed instructions, some under-the-hood information and useful guidelines.

Good luck, and welcome to the crowdpatching community! Let's fix some bugs, and then fix some more.

Monday, February 6, 2017

0patch Agent on Linux - Micropatching CVE-2016-9445

By Jure Skofic, 0patch Team.

In December we finally got our hands on a working prototype of our Linux Agent. It's been in the making for quite some time and now that it's in alpha stage, we can finally start 0patching Linux user space code. The Linux agent still lacks some of the fancier functionality our Windows agent supports, but it's ready to be taken for a spin.

Now I can't stress enough how big of a deal this is. With over 65% of web servers running some flavor of Unix, patching security vulns in high availability Linux environments could become much less of a problem. If we take a zero day like Heartbleed for example, the time it takes to deploy an official patch isn't only comprised of the time it takes the vendor to release the fix, but also of the time it takes DevOps to test and deploy it. We aim to shorten this considerably by making patches tiny, easy to review and even easier to deploy, avoiding the need to restart the server. 

When developing and testing the Linux agent on Ubuntu we already created a patch for Heartbleed and while there's still a lot of vulnerable servers out there, the hype train is nearing its last stop. So we needed something new for the grand unveiling and Chris Evans (@scarybeasts) was kind enough to provide us with just the thing. It also gave us an excuse to port the agent to Fedora and increase our Linux flavor support.

This bug was published last November on Chris's blog. The flaw is located in gstreamer's VMnc decoder that handles the VMware screen capture format. It's a pretty straight forward integer overflow and Chris provided a PoC.

I started off by creating a Fedora 25 virtual machine, then tried playing the PoC with totem, a video player native to Gnome which uses the vulnerable version of gstreamer on an unpatched Fedora 25. Playing the PoC with totem resulted in a crash. Chris was kind enough to do a detailed analysis of the bug which helped a lot. He figured out that the integer overflow occurs in vmnc_handle_wmvi_rectangle method in vmncdec.c, which is a part of the gstreamer-plugins-bad package. You can view its source on gstreamers github. As Chris explains the overflow occurs because of user-supplied rect->width and rect->height that are multiplied with bytes_per_pixel to allocate the image data buffer. Both variables are signed 32-bit integers. If high enough values are supplied the result is an integer overflow. The subsequent memory allocation (marked in red on the image below) produces a buffer that is smaller than the image data, which results in a buffer overflow.


I disassembled libgstvmnc.so and located the point in the function vmnc_handle_wmvi_rectangle where the height and width are multiplied and the buffer is allocated (marked in red on the image below).




The imul instruction sets the carry and overflow flags when the significant bit is carried into the upper half of the result, so my first thought was that we could return an error if these flags are set. The patch location is marked in green on the image above. This was my first version of the patch, which is applied at the location of the original imul (marked in green on the image above):

imul 0x30c(%rbx),%edi //Multiply width and height
jc _bad //If CF is set jump to return error
imul 0x314(%rbx),%edi //Multiply with bytes_per_pixel jc _bad //If CF is set jump to return error jmp _good //If no CF is set continue execution _bad: pop %rdi //Return error code: add $0x28,%rsp mov $0xffffffff,%eax //Set %eax to -1 pop %rbx pop %rbp pop %r12 pop %r13 pop %r14 pop %r15 retq //and return _good:

As you can see, we check if the carry flag is set after each imul and return an error if set. After applying the patch, however, totem still crashed. After some debugging I figured out that returning an error in vmnc_handle_wmvi_rectangle wasn't enough. Instead of looking for the cause I took a look at the official patch below.



The official patch can be found in function vmnc_handle_packet. You can see that both width and height are checked if they're larger then 16384 or 0x4000 and an error is returned if they are. Our patch should do the same. I disassembled both the vulnerable and the patched version of vmnc_handle_packet and used Bindiff to look for an appropriate patch location. The image below represents the combined view of both codes. The green code was added by the official patch.




The official patch code is executed only if the packet type equals TYPE_WMVi or 0x574D5669. At my chosen patch location (marked in red - click the image above for a larger view) the %edi register holds the packet type value, while %r8w and %cx hold the width and height. This is the resulting patch:


cmp $0x574d5669,%edi //If the packet type doesn't equal TYPE_WMVi,
jnz _end //continue execution cmp $0x4000,%r8w //If width is greater than 0x4000, ja _bad //jump to return error cmp $0x4000,%cx //If height is smaller than 0x4000, jbe _end //continue execution, else return error _bad: mov $0xffffffff,%eax //Set %eax to -1 add $0x48,%rsp pop %rbx pop %rbp pop %r12 pop %r13 pop %r14 pop %r15 retq //and return _end:

The patch successfully stops the integer overflow and subsequent memory allocation, actually making the file playable again, just as the official patch does. Here's a short video of our Linux 0patch agent and the patch we just developed.



As you can see, totem crashes when trying to open the PoC. After enabling the patch by copying the patch file to the patch store folder, totem no longer crashes and the video is actually playable.

Effort wise, developing this patch took about 8 hours. That includes reversing libgstvmnc.so, trying out a faulty patch candidate, analyzing the official patch and implementing an adequate patch candidate. This was a relatively simple patch but I wanted to showcase our Linux agent and its capabilities which are almost on par with what we're already doing on Windows.

We're continuously working on expanding the OS platform support so we can one day bring 0patch to everyone who needs it.


@0patch

Wednesday, January 25, 2017

Micropatching Remote Code Execution in WebEx Browser Extension (CVE-2017-3823)

This is a short story of writing a micropatch for a widely-used web browser extension.

[Updated on 1/27/17 after Cisco has, again commendably quickly, issued a new WebEx browser extension version to fix a bypass in version 1.0.5.]

Two days ago, Tavis Ormandy of Google's Project Zero published details of a remote code execution bug he had found in Cisco WebEx Browser Extension that allowed any web page to launch arbitrary executable on visitor's computer. A thing like this is a big deal: WebEx has ~20 million users and many of them are enterprise and government users, or in other words, interesting targets for professional criminals and spies.

According to Tavis, Cisco was impressively quick to issue an updated version 1.0.5 after receiving his report. The automatically installed update restricts the affected functionality so that it can only be used by web sites originating via HTTPS from domains webex.com and webex.com.cn. For all other web sites, an attempt to exploit the vulnerability would result in a warning dialog like this:




I tried to get Tavis's exploit to work with the updated extension, but clicking OK on the above dialog didn't work. Delivering the exploit from a fake local www.webex.com server didn't - as expected - show the above warning, but also didn't result in the exploit working. My theory is that Cisco also added some quick check to break this particular exploit, which was a smart thing to do, since cross-site scripting issues are surely about to start springing up on webex.com servers (one is already there). If my theory is correct, someone will find (and possibly publish) a way to get around this quick check and combine it with a cross-site scripting issue somewhere in the webex.com domain. By the way, Pentest-Tools.com finds 544 hosts in the webex.com domain, so the attack surface seems sizeable.

The show is not over either, it seems, as Tavis has apparently found and reported some residual remote code execution issues with the latest WebEx update.

[Update 1/27/17: Tavis's report is now public. Apparently WebEx developers have implemented the DLL white-listing strategy in version 1.0.5 that we mentioned in the original blog post, but Tavis has found a way around it by misusing the installation functionality for replacing one of the white-listed DLLs with Cisco-signed MSVCR100.DLL. Cisco has since published WebEx update 1.0.7 that fixes this bypass.]

So we have a publicly known remote code execution bug with a working POC on millions of computers, an unknown number of not-yet-updated WebEx extensions, an update that seems to be exploitable with cross-site scripting, and a still-secret residual remote code execution bug in the latest update. One could say there's room for improvement.


Writing a Micropatch

The way this WebEx vulnerability is currently fixed doesn't seem good enough to me. (To be fair, it probably also doesn't look good enough to Cisco but they had to do something quick and are probably working on a better long-term solution.) The risk of resurrecting Tavis's exploit with some modification and cross-site scripting, or of simply tricking users to click OK, made me wonder if we could do a better fix with 0patch.

My initial thought was that we couldn't, as browser extensions are essentially JavaScript code and we currently can't patch that. But then my man Edwin van Andel of Zerocopter kind of dared me to 0patch this bug so I dropped everything else and fired up my 0patch virtual machines. Surprisingly, it turned out that most of WebEx is actually native code - the browser code is merely in charge of launching ciscowebexstart.exe and passing the meeting parameters to it. It is this executable that then does the heavy lifting.

Taviso's exploit shows that a WebEx message, which is used for launching a meeting, can also be used for executing any exported function with arbitrary arguments from any DLL in the WebEx folder. We don't know, perhaps even from anywhere on the computer or even from a remote network share.

So what if we patched WebEx so that it would only be possible to call a limited set of functions from a limited set of DLLs, namely those that are needed for WebEx's normal operations. Looking at what happens when a meeting is launched, my debugger and I could only find calls to functions from atmccli.DLL.Well, atmccli.DLL only exports four functions, and they all seem to be in use so why don't we simply limit loading of DLLs to atmccli.DLL?

The challenge at this point was to find the place in the code where the DLL name provided in the (potentially malicious) WebEx message is used for actually loading a DLL, likely using LoadLibrary/Ex. If we found that place, we could perhaps replace the DLL name with "atmccli.DLL".

I started by monitoring ciscowebexstart.exe with Process Monitor to see the call stack where Tavis's calc.exe gets launched. This is what I found.





Apparently, it is atgpcext.dll that calls wsystem from MSVCR100.DLL. So I opened it up in IDA and looked around the addresses in the above call stack. Fortunately, a lot of logging strings are sprinkled around the DLL, making it easier to understand what the code is supposed to be doing.

Subsequent hours comprised of typical connect-the-dots reversing steps, switching from IDA to WinDbg to Process Monitor and back in attempting to understand the process from point A (web site sending WebEx a message) to point B (the chosen DLL function gets invoked).

Eventually the dots were connected and I ended up with a decent patch candidate:


MODULE_PATH "C:\ProgramData\WebEx\WebEx\T30_MC\atgpcext.dll"
PATCH_ID 10000
PATCH_FORMAT_VER 2
VULN_ID 2099
PLATFORM win32

patchlet_start
PATCHLET_ID 1
PATCHLET_TYPE 2

PATCHLET_OFFSET 0x5da9f
JUMPOVERBYTES 5 ; We jump over the original "call sub_21CBA"
N_ORIGINALBYTES 5

code_start

    call over
    dw __utf16__("atmccli.dll"),0
over:
    pop eax

code_end
patchlet_end


Let's see what this patch does. It gets applied to WebEx's atgpcext.dll at offset 0x5da9f, and replaces the call to function which returns a pointer to the DLL name from the message with a pointer to our own fixed string, "atmccli.dll". This replacing is caused by JUMPOVERBYTES 5, which tells 0patch agent not to continue execution of the original code after returning from the patch code, but to jump over (well, obviously) 5 bytes of that code, effectively landing just behind the "call sub_21CBA" 5-byte instruction.

But wait, what is this trickery in the patch code? Do we really make a call to some label, then pop eax from the stack? Yes we do. This is an efficient, and very hacker-worthy solution for getting a pointer to a string if you don't have some extra space to store said string. The call namely pushes the address of the "following instruction" to stack, but what follows is not an instruction but the string. The pop then takes the address of the string from stack, and that's all the patch needs to do. (Note that this is an old trick; if you have any references to its past use, do let me know so I can credit the author.)

Now, the result of this patch getting applied is that whichever DLL one instructs WebEx to use, WebEx will always use its own atmccli.DLL. This obviously breaks Tavis's exploit, as he is trying to load MSVCR100.DLL, and we replace that with atmccli.DLL; there is no _wsystem exported function in atmccli.DLL, so the operation silently fails.

Let's take a look at a video showing how this micropatch blocks the exploit while keeping WebEx functional.




So How Good Is This Micropatch?

Generally speaking, there can be two problems with a security patch: it can fail to adequately remove the security issue, and/or it can cause functional problems.

  1. Adequate removal of the security issue: Frankly, it's hard to say without digging deep into what the four exported functions of atmccli.DLL are capable of. Perhaps one could find that InitControl provides a way to launch arbitrary executables, which would make the patch ineffective. At this point, only WebEx developers know the answer to this, which just underlines my belief that 0patches like these should best be written by original developers - and someday hopefully will be.

    In comparison to Cisco's official fix (update 1.0.5), this patch prevents the exploit even if it originated from one of the webex.com servers, e.g., via cross-site scripting.

    [Update 1/27/17: After the report about exploiting the WebEx version 1.0.5 was made public, we reproduced the second exploit and discovered that our original micropatch - the one for version 1.0.1 - also blocked said exploit without any modification, which we think is kind of cool. This video shows it best.]

  2. Functional problems: In an attempt to simulate a vendor's quick response to a published vulnerability in their product, we simply tested the main WebEx functionality: launching and running a meeting. This seemed to work, but again, WebEx developers would quickly notice if this patch broke some functionality as they are deeply familiar with its working. If they said, for instance, that a few other DLLs are also invoked under normal WebEx operations, we could improve the patch to white-list DLL-function pairs, and still keep things tiny.


The entire process of creating this patch took about 8 hours, including finding and setting up the vulnerable extension, getting the exploit to work, reversing WebEx, trying out a couple of inadequate patch candidates, and wrestling with our not-quite-yet-production-quality 0patch Builder that we use for compiling patches.

It is my assessment that with some help of WebEx developers this micropatch - or likely a much better one - could be made in two hours tops. Their knowledge would significantly shorten the reversing time and would allow for finding the optimal patching strategy that would reliably close the hole without impacting functionality.

And this is how we see the future of vulnerability patching.


@mkolsek
@0patch
  翻译: