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:
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:
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.
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.
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:
Setting Up kernel debugging:
bcdedit /debug on bcdedit /set TESTSIGNING ON bcdedit /dbgsettings net hostip:192.168.29.119 port:54444 key:220.127.116.11 bcdedit /dbgsettings
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“
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”
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:
I was very curious to check the values of a1 a2 and a3, we can use the debugger for this as shown below:
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:
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:
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 frombcrypt.h, which is more or less about encryption settings, such as creating contexts, deleting contexts, setting contexts, configuration, etc.
Next, we go to the Vulnerable function and we hit function CfgAdtFormatPropertyBlock
Now the real fun starts here as shown below:
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.
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.
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:
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.