Author：SungLin@Knownsec 404 Team
Time: April 2, 2020
Chinese version:https://paper.seebug.org/1164/

0x00 Background

On March 12, 2020, Microsoft confirmed that a critical vulnerability affecting the SMBv3 protocol exists in the latest version of Windows 10, and assigned it with CVE-2020-0796, which could allow an attacker to remotely execute the code on the SMB server or client. On March 13 they announced the poc that can cause BSOD, and on March 30, the poc that can promote local privileges was released . Here we analyze the poc that promotes local privileges.

0x01 Exploit principle

The vulnerability exists in the srv2.sys driver. Because SMB does not properly handle compressed data packets, the function Srv2DecompressData is called when processing the decompressed data packets. The compressed data size of the compressed data header, OriginalCompressedSegmentSize and Offset, is not checked for legality, which results in the addition of a small amount of memory. SmbCompressionDecompress can be used later for data processing. Using this smaller piece of memory can cause copy overflow or out-of-bounds access. When executing a local program, you can obtain the current offset address of the token + 0x40 of the local program that is sent to the SMB server by compressing the data. After that, the offset address is in the kernel memory that is copied when the data is decompressed, and the token is modified in the kernel through a carefully constructed memory layout to enhance the permissions.

0x02 Get Token

Let's analyze the code first. After the POC program establishes a connection with smb, it will first obtain the Token of this program by calling the function OpenProcessToken. The obtained Token offset address will be sent to the SMB server through compressed data to be modified in the kernel driver. Token is the offset address of the handle of the process in the kernel. TOKEN is a kernel memory structure used to describe the security context of the process, including process token privilege, login ID, session ID, token type, etc.

Following is the Token offset address obtained by my test.

0x03 Compressed Data

Next, poc will call RtCompressBuffer to compress a piece of data. By sending this compressed data to the SMB server, the SMB server will use this token offset in the kernel, and this piece of data is 'A' * 0x1108 + (ktoken + 0x40).

The length of the compressed data is 0x13. After this compressed data is removed except for the header of the compressed data segment, the compressed data will be connected with two identical values 0x1FF2FF00BC, and these two values will be the key to elevation.

0x04 debugging

Let's debug it first, because here is an integer overflow vulnerability. In the function srv2! Srv2DecompressData, an integer overflow will be caused by the multiplication 0xffff ffff * 0x10 = 0xf, and a smaller memory will be allocated in srvnet! SrvNetAllocateBuffer.

After entering srvnet! SmbCompressionDecompress and nt! RtlDecompressBufferEx2 to continue decompression, then entering the function nt! PoSetHiberRange, and then starting the decompression operation, adding OriginalMemory = 0xffff ffff to the memory address of the UnCompressBuffer storage data allocated by the integer overflow just started Get an address far larger than the limit, it will cause copy overflow.

But the size of the data we need to copy at the end is 0x1108, so there is still no overflow, because the real allocated data size is 0x1278, when entering the pool memory allocation through srvnet! SrvNetAllocateBuffer, finally enter srvnet! SrvNetAllocateBufferFromPool to call nt! ExAllocatePoolWithTag to allocate pool memory.

Although the copy did not overflow, it did overwrite other variables in this memory, including the return value of srv2! Srv2DecompressDatade. The UnCompressBuffer_addressis fixed at 0x60, and the return value relative to the UnCompressBuffer_address offset is fixed at 0x1150, which means that the offset to store the address of the UnCompressBuffer relative to the return value is 0x10f0, and the address to store the offset data is 0x1168, relative to the storage decompression Data address offset is 0x1108.

There is a question why it is a fixed value, because the OriginalSize = 0xffff ffff, offset = 0x10 passed in this time, the multiplication integer overflow is 0xf, and in srvnet! SrvNetAllocateBuffer, the size of the passed in 0xf is judged, which is less At 0x1100, a fixed value of 0x1100 will be passed in as the memory allocation value of the subsequent structure space for the corresponding operation, and when the value is greater than 0x1100, the size passed in will be used.

Then return to the decompressed data. The size of the decompressed data is 0x13. The decompression will be performed normally. Copy 0x1108of "A", the offset address of the 8-byte token + 0x40 will be copied to the back of "A".

After decompression and copying the decompressed data to the address that was initially allocated, exit the decompression function normally, and then call memcpy for the next data copy. The key point is that rcx now becomes the address of token + 0x40of the local program!!!

After the decompression, the distribution of memory data is 0x1100 ('A') + Token = 0x1108, and then the function srvnet! SrvNetAllocateBuffer is called to return the memory address we need, and the address of v8 is just the initial memory offset 0x10f0, so v8 + 0x18 = 0x1108, the size of the copy is controllable, and the offset size passed in is 0x10. Finally, memcpy is called to copy the source address to the compressed data0x1FF2FF00BC to the destination address 0xffff9b893fdc46f0 (token + 0x40), the last 16 Bytes will be overwritten, the value of the token is successfully modified.

0x05 Elevation

The value that is overwritten is two identical 0x1FF2FF00BC. Why use two identical values to overwrite the offset of token + 0x40? This is one of the methods for operating the token in the windows kernel to enhance the authority. Generally, there are two methods.

The first method is to directly overwrite the Token. The second method is to modify the Token. Here, the Token is modified.

In windbg, you can run the kd> dt _token command to view its structure.

So modify the value of _SEP_TOKEN_PRIVILEGES to enable or disable it, and change the values of Present and Enabled to all privileges of the SYSTEM process token 0x1FF2FF00BC, and then set the permission to:

This successfully elevated the permissions in the kernel, and then execute any code by injecting regular shellcode into the windows process "winlogon.exe":

Then it performed the action of the calculator as follows: