Lucene search

K
googleprojectzeroGoogleProjectZeroGOOGLEPROJECTZERO:C2A64C2133DFD2ACB457C2DD2790CBF7
HistoryApr 02, 2020 - 12:00 a.m.

TFW you-get-really-excited-you-patch-diffed-a-0day-used-in-the-wild-but-then-find-out-it-is-the-wrong-vuln

2020-04-0200:00:00
googleprojectzero.blogspot.com
154

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

9.3 High

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:M/Au:N/C:C/I:C/A:C

0.974 High

EPSS

Percentile

99.9%

Posted by Maddie Stone, Project Zero

INTRODUCTION

I’m really interested in 0-days exploited in the wild and what we, the security community, can learn about them to make 0-day hard. I explained some of Project Zero’s ideas and goals around in-the-wild 0-days in a November blog post.


On December’s Patch Tuesday, I was immediately intrigued by CVE-2019-1458, a Win32k Escalation of Privilege (EoP), said to be exploited in the wild and discovered by Anton Ivanov and Alexey Kulaev of Kaspersky Lab. Later that day, Kaspersky published a blog post on the exploit. The blog post included details about the exploit, but only included partial details on the vulnerability. My end goal was to do variant analysis on the vulnerability, but without full and accurate details about the vulnerability, I needed to do a root cause analysis first. I tried to get my hands on the exploit sample, but I wasn’t able to source a copy.


Without the exploit, I had to use binary patch diffing in order to complete root cause analysis. Patch diffing is an often overlooked part of the perpetual vulnerability disclosure debate, as vulnerabilities become public knowledge as soon as a software update is released, not when they are announced in release notes. Skilled researchers can quickly determine the vulnerability that was fixed by comparing changes in the codebase between old and new versions. If the vulnerability is not publicly disclosed before or at the same time that the patch is released, then this could mean that the researchers who undertake the patch diffing effort could have more information than the defenders deploying the patches.


While my patch diffing adventure did not turn out with me analyzing the bug I intended (more on that to come!), I do think my experience can provide us in the community with a data point. It’s rarely possible to reference hard timelines for how quickly sophisticated individuals can do this type of patch-diffing work, so we can use this as a test. I acknowledge that I have significant experience in reverse engineering, however I had no previous experience at all doing research on a Windows platform, and no knowledge of how the operating system worked. It took me three work weeks from setting up my first VM to having a working crash proof-of-concept for a vulnerability. This can be used as a data point (likely a high upper bound) for the amount of time it takes for individuals to understand a vulnerability via patch diffing and to create a working proof-of-concept crasher, since most individuals will have prior experience with Windows.


But as I alluded to above, it turns out I analyzed and wrote a crash POC for not CVE-2019-1458, but actually CVE-2019-1433. I wrote this whole blog post back in January, went through internal reviews, then sent the blog post to Microsoft to preview (we provide vendors with 24 hour previews of blog posts). That’s when I learned I’d analyzed CVE-2019-1433, not CVE-2019-1458. At the beginning of March, Piotr Florczyk published a detailed root cause analysis and POC for the “real” CVE-2019-1458 bug. With the “real” root cause analysis for CVE-2019-1458 now available, I decided that maybe this blog post could still be helpful to share what my process was to analyze Windows for the first time and where I went wrong.


This blog post will share my attempt to complete a root cause analysis of CVE-2019-1458 through binary patch diffing, from the perspective of someone doing research on Windows for the first time. This includes the process I used, a technical description of the “wrong”, but still quite interesting bug I analyzed, and some thoughts on what I learned through this work, such as where I went wrong. This includes the root cause analysis for CVE-2019-1433, that I originally thought was the vulnerability for the in the wild exploit. As far as I know, the vulnerability detailed in this blog post was not exploited in the wild.

MY PROCESS

When the vulnerability was disclosed on December’s Patch Tuesday, I was immediately interested in the vulnerability. As a part of my new role on Project Zero where I’m leading efforts to study 0-days used in the wild, I was really interested in learning Windows. I had never done research on a Windows platform and didn’t know anything about Windows programming or the kernel. This vulnerability seemed like a great opportunity to start since:

  1. Complete details about the specific vulnerability weren’t available,

  2. It affected both Windows 7 and Windows 10, and

  3. The vulnerability is in win32k which is a core component of the Windows kernel.


I spent a few days trying to get a copy of the exploit, but wasn’t able to. Therefore I decided that binary patch-diffing would be my best option for figuring out the vulnerability. I was very intrigued by this vulnerability because it affected Windows 10 in addition to Windows 7. However, James Forshaw advised me to patch diff the Windows 7 win32k.sys files rather than the Windows 10 versions. He suggested this for a few reasons:

  1. The signal to noise ratio is going to be much higher for Windows 7 rather than Windows 10. This “noise” includes things like Control Flow Guard, more inline instrumentation calls, and “weirder” compiler settings.

  2. On Windows 10, win32k is broken up into a few different files: win32k.sys, win32kfull.sys, win32kbase.sys, rather than a single monolithic file.

  3. Kaspersky’s blog post stated that not all Windows 10 builds were affected.


I got to work creating a Windows 7 testing environment. I created a Windows 7 SP1 x64 VM and then started the long process of patching it up until September 2019 (the last available update prior to the December 2019 update where the vulnerability was supposedly fixed). This took about a day and a half as I worked to find the right order to apply the different updates.


Turns out that me thinking that September 2019 was the last available update prior to December 2019 would be one of the biggest reasons that I patch-diffed the wrong bug. I thought that September 2019 was the latest because it was the only update shown to me, besides December 2019, when I clicked “Check for Updates” within the VM. Because I was new to Windows, I didn’t realize that not all updates may be listed in the Windows Update window or that updates could also be downloaded from the Microsoft Update Catalog. When Microsoft told me that I had analyzed the wrong vulnerability, that’s when I realized my mistake. CVE-2019-1433, the vulnerability I analyzed, was patched in November 2019, not December 2019. If I had patch-diffed November to December, rather than September to December, I wouldn’t have gotten mixed up.


Once the Windows 7 VM had been updated to Sept 2019, I made a copy of its C:\Windows\System32\win32k.sys file and snapshotted the VM. I then updated it to the most recent patch, December 2019, where the vulnerability in question was fixed. I then snapshotted the VM again and saved off the copy of win32k.sys. These two copies of win32k.sys are the two files I diffed in my patch diffing analysis.


Win32k is a core kernel driver that is responsible for the windows that are shown as a part of the GUI. In later versions of Windows, it’s broken up into multiple files rather than the single file that it is on Windows 7. Having only previously worked on the Linux/Android and RTOS kernels, the GUI aspects took a little bit of time to wrap my head around.


On James Foreshaw’s recommendation, I cloned my VM so that one VM would run WinDbg and debug the other VM. This allows for kernel debugging.


Now that I had a copy of the supposed patched and supposed vulnerable versions of win32k.sys, it’s time to start patch diffing.

PATCH DIFFING WINDOWS 7 WIN32K.SYS

I decided to use BinDiff to patch diff the two versions of win32k. In October 2019, I did a comparison on the different binary diffing tools available [video, slides], and for me, BinDiff worked best “out of the box” so I decided to at least start with that again.


I loaded both files into IDA and then ran BinDiff between the two versions of win32k. To my pleasant surprise, there were only 23 functions total in the whole file/driver that had changed from one version to another. In addition, there were only two new functions added in the December 2019 file that didn’t exist in September. This felt like a good sign: 23 functions seemed like even in the worst case, I could look at all of them to try and find the patched vulnerability. (Between the November and December 2019 updates only 5 functions had changed, which suggests the diffing process could have been even faster.)

Original BinDiff Matched Functions of win32k.sys without Symbols


When I started the diff, I didn’t realize that the Microsoft Symbol Server was a thing that existed. I learned about the Symbol Server and was told that I could easily get the symbols for a file by running the following command in WinDbg: x win32k!*. I still hadn’t realized that IDA Pro had the capability to automatically get the symbols for you from a PDB file, even if you aren’t running IDA on a Windows computer. So after running the WinDBG command, I copied all of the output to a file, rebased my IDA Pro databases to the same base address and then would manually rename functions as I was reversing based on the symbols and addresses in the text file. About a week into this escapade, I learned how to modify the IDA configuration file to have my IDA Pro instance, running on Linux, connect to my Windows VM to get the symbols.

BinDiff Matched Function of win32k.sys with Symbols

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

9.3 High

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:M/Au:N/C:C/I:C/A:C

0.974 High

EPSS

Percentile

99.9%