Windows Kernel Drive Vulnerability Research:  CVE-2020-17087

Windows LPE CNG.sys CVE-2020-17087: root cause + patch analysis + reduction of crashing sample to be a 1 liner
Windows Kernel Drive Vulnerability Research:  CVE-2020-17087

The original report from Google:

(https://bugs.chromium.org/p/project-zero/issues/detail?id=2104)

Windows Kernel

The original crash sample contains many lines:

(https://bugs.chromium.org/p/projectzero/issues/attachmentText?aid=472684)

original crash

This is the end result of my work for “cve-2020-17087” (1 liner):

We use a function called BCryptSetContextFunctionProperty (later in the blog, we will take a detailed look at the function). For now, we google the function and go through the MSDN (https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptsetcontextfunctionproperty)
 This function sets the value of a named property for a cryptographic function in an existing CNG context.
Windows Kernel
windows CVE

Let’s take a closer look at “cbValue” (contains the size, in bytes, of the pbValue buffer) and “pbValue” (the address of a buffer that contains the new property value), because we can set to write this function and try to put something malicious in cbValue and crash the system (BSOD).

Now, let’s check the bug analysis shown below:

bugcheck

Now, we will look at the call stack. Starting with BCryptSetContextFunctionsProperty, control is passed to the kernel code in NtDeviceIoControlFile, jumps into KsecDispatch, then passes to CNG, and then crashes at the vulnerable function, only this time it was my data that caused the crash as shown below:

Windows Kernel Drive Vulnerability

Introduction:

On October 22, 2020, Google’s security researchers published a security report on bug.chromium.org titled “Issue 2104: Windows Kernel cng.sys pool-based buffer overflow,” roughly meaning in CNG A security vulnerability was found in .sys and CNG.sys, a Windows driver whose IOCTL 0x390400 made a function vulnerable to 16-bit integer overflow. The main security problem function is cng!CfgAdtpFormatPropertyBlock, and this function is not exported. The vulnerability exists in a Microsoft driver called CNG.sys

What is cng.sys:

Cryptography API: Next Generation (CNG) is the long-term replacement for CryptoAPI. CNG is designed to be extensible at many levels and cryptography agnostic in behavior. CNG provides a set of APIs that can be used to easily encrypt and decrypt content to authorization principals across multiple computers and can also be used for importing and exporting keys such as (BCryptImportKey, BCryptExportKey)

The file cng.sys is located in the C:WindowsSystem32drivers folder.

Windows Vulnerability

Overview of pool overflow vulnerability in cng.sys (original exploit):

This vulnerability is an integer overflow and buffer vulnerability in the Windows kernel encryption module (cng.sys). In the function cng!CfgAdtpFormatPropertyBlock, after multiplying the second parameter (srclen) is multiplied by 6, an overflow occurs, resulting in a subsequent application buffer. If it is too small, the kernel accesses invalid memory, causing BSOD.
windows Vulnerability

This bug was posted by j00ru and provided a lot of useful information that we can use to track down this bug further. Specifically, the information he provided us includes: call stack, code snippets, and part of the !analyze command. Shown below is the call stack information we will use:

  • cng!CngDispatch
  • cng!CngDeviceControl
  • cng!ConfigIoHandler_Safeguarded
  • cng!ConfigFunctionIoHandler
  • cng!_ConfigurationFunctionIoHandler
  • cng!BCryptSetContextFunctionProperty
  • cng!CfgAdtReportFunctionPropertyOperation
  • cng!CfgAdtpFormatPropertyBlock

Setting Up kernel debugging:

bcdedit /debug on
bcdedit /set TESTSIGNING ON
bcdedit /dbgsettings net hostip:192.168.29.119 port:54444 key:1.2.3.4
bcdedit /dbgsettings

Step 1:

Vulnerability

Step 2:

Vulnerability

Step 3:

Vulnerability

Analysis and Debugging:

The First thing I always try to check is analysis crash with a debugger !analyze -v . To get a better understanding, we try to analyze in windbg.
First, we load the drivers for debugging.
sxe ld cng.sys
Kernel Vulnerability

I will try to explain the value step by step:

The first step is to analyze the stack frame from the crash analysis. We can set a breakpoint on the first point cng!CngDispatch so that when we can reach cng!CfgAdtpFormatPropertyBlock. for that we need some kind of Advance Debugging.
This is the next-generation encryption driver for Windows.
PoC Context we will use “ba e1 /p eprocess
Windows Analysis
Analysis

cng!CngDispatch

Two parameters PDEVICE_OBJECT(a1) and PIRP(a2)
Before calling the CngDeviceControl function, the main a2 IRP data is processed to obtain the In/Out buffer address and length, as well as the IOCTL code and RequestMode, which are sent to CngDeviceControl as six parameters respectively. Once the breakpoint is triggered and we are currently in CngDispatch, we can use the call tracing technique to dock to cng!CngDeviceControl, which is the next item in the call stack.

After judging a large number of IOCTL codes I finally tracked down IOCTL code that was used in POC as shown below:
debug

I was very curious to check the values of a1 a2 and a3, we can use the debugger for this as shown below:
Windows Kernel

Now, we know that a1 and a3 are pointers to the input buffer, and a2 seems to indicate the variable length, whose value is 0x2aab+0x1000. Now, I will move ahead to cng!ConfigIoHandler_Safeguarded It also performs a series of checks and allocates two blocks of memory with BCryptAlloc, and copies the contents of the input buffer into a newly allocated piece of memory.

This particular function –  IoUnpack_SG_ParamBlock_Header as shown below, looked interesting:
Analysis_debugg

While looking back to PoC I found this strange number value 0x1a2b3c4d It turned out to be an important value because when it is replaced, the exploit will not work and will not reach the vulnerable code path.
The next step is CNG! The ConfigFunctionIoHandler function, which takes six arguments as shown below, which I can also identify by the debugger:
Analysis_debugg

Let’s Look for the first register RCX which is 10400 set as the second DWORD of the PoC, R8 is set to size+1000, and other both RDX and R9 are some kinds of allocating buffers that store our data.

Now, I attempt to dissect why the RCX=10400 is very important in PoC Because if the value of this register is not 0x10400, the path changes and we cannot reach the code path. Also, the other functions in the switch statement are of little use to us, at least from an exploit perspective.

Let’s try to check some functions from bcrypt.h, which is more or less about encryption settings, such as creating contexts, deleting contexts, setting contexts, configuration, etc.

Later on, I was looking ConfigurationFunctionIoHandler for some kind of function related to that so I got a condition as shown below (https://support.industry.siemens.com/forum/WW/en/posts/what-io-field-can-be-used-to-display-a-dw-number-16-bits/67549) When loWord equals 0x10400 or 0x400 (1024), it can directly take us to (BCryptSetContextFunctionProperty)
Analysis_debugg

Next, we go to the Vulnerable function and we hit function CfgAdtFormatPropertyBlock

Now the real fun starts here as shown below:

Windows Analysis
Vulnerable

This is the vulnerable function that J00ru speaks of.
So, let’s look at this line ” v8 = BCryptAlloc((unsigned __int16) (6 * a2));” for eg 6 * 0x2aab = 2the result 6* size of is forcibly converted to an unsigned 16-bit integer. When multiplied by 6, the overflow will be 2. The function will call ExAllocatePoolWithTag or SkAllocatePool to dynamically apply for pool space in the type NonPagedPoolNx pool. However, the number of do-while loops is the size (ie 0x2ab), writing 6 bytes to the pool space each time, eventually leading to out-of-bounds writing and triggering BSOD0x10000.

Patch Analysis:

I checked the changes before and after the function, and found that after the patch, the function calls the cng!RtlUShortMult function to process the incoming size.
Vulnerable

This is cng!RtlUShortMult function is very short and simple, and can judge the overflow of the result of size* 6 (compared with 0xffff) as shown below:
patch analysis

In my next blog post, we will take a close look at an LPE exploit with a Chrome RCE exploit.

Looking for more security research blog posts? click here

VS-Labs: Security Research

We Solve Complex Technical Challenges VerSprite’s Research and Development division (a.k.a VS-Labs) is comprised of individuals who are passionate about diving into the internals of various technologies. Our clients rely on VerSprite’s unique offerings of zero-day vulnerability research and exploit development to protect their assets from various threat actors. From advanced technical security training to our research for hire B.O.S.S offering, we help organizations solve their most complex technical challenges.