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


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"
VULN_ID 2135


 ;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


   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
   call PIT_ExploitBlocked ; show "Exploit Blocked" popup

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



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.