Core Labs has completed an in-depth analysis of two Microsoft vulnerabilities, CVE-2019-1181 and CVE-2019-1182, which were patched in August 2019. These vulnerabilities are particularly interesting and worth further assessment because they affect OS versions ranging from Windows 7 to Windows 10 1903 (x86, x86-64 and ARM64).
We began our research by choosing Windows 7 SP1 as target, because Windows 7 SP1 has less exploitation mitigations than Windows 10 1903. While working on this research, we made two discoveries:
- If NLA (Network Level Authentication) is enabled, the attacker would first need to authenticate to Remote Desktop Services using a valid account. Therefore, if this is on, they won’t be able to exploit it directly.
- If the target is Windows 7 SP1 or Windows Server 2008 R2 SP1, it will need to have RDP 8.0 or RDP 8.1 installed.
The second issue reduces a lot of the impact for Windows 7 SP1/Windows Server 2008 R2 SP1 targets. The default installation isn’t vulnerable. In order to exploit this server, it is necessary to upgrade RDP to RDP 8.0 or RDP 8.1 (Remote Desktop Protocol (RDP) 8.0 update for Windows 7 and Windows Server 2008 R2). Therefore, we decided to switch its focus to Windows 10 1903, using the latest version of Windows 10 as an opportunity to learn more about Windows exploitation.
Analysis of DejaBlue
We began our research with Windows 7 SP1 in mind, beginning with finding the vulnerable function. The first thing to do when analyzing a patch is a diff between the vulnerable version and the patched version in order to detect changes between them.
This specific analysis was initially completed by another researcher (DejaBlue: Analyzing a RDP Heap Overflow).Briefly, this analyst found that the vulnerable function is DecompressUnchopper::Decompress which is in RDPCoreTS.dll.
We completed the binary diff in Windows 10 1903, finding that the method DecompressUnchopper::Decompress wasn’t in RDPCoreTS.dll, and was instead located in rdpbase.dll.
When analyzing the diff between the vulnerable DecompressUnchopper::Decompress and the patched one, we used rdpbase.dll 10.0.18362.1 (vulnerable) and 10.0.18362.295 (patched).
Figure 1 represents the vulnerable function. By moving a value from [rbx+3] to eax and then adding to it 0x2000, this value will be used by the new operator as a chunk size. The problem is that the attacker can control the value of [rbx+3] and, therefore, generate an Integer Overflow.
Below is the pseudo code/reversed version:
If you pass a value in the range [0xFFFFFFFF-01FFF, 0xFFFFFFFF], you will generate the Integer Overflow. You can take advantage of this by reading where this chunk is used. By taking a look later in the code, you will see the memcpy:
Triggering the Vulnerability
Before calling the vulnerable function, you need some background about the RDP protocol. A simple explanation will be provided below, with a deeper analysis of the vulnerability. For more detailed information about the RDP protocol, Microsoft provides ample technical documentation.
The Connection Sequence:
The Connection Sequence has 10 phases:
- Connection Initiation
- Basic Settings Exchange
- Channel Connection
- RDP Security Commencement
- Secure Settings Exchange
- Optional Connect-Time Auto-Detection
- Optional Multitransport Bootstrapping
- Capabilities Exchange
- Connection Finalization
The client sends a Client X.224 Connection Request PDU indicating which protocols it supports (Standard RDP Security, TLS, CredSSP, RDSTLS, etc). Then, the server responds with a Server X.224 Connection Confirm PDU.
Basic Settings Exchange
In this phase, using the Client MCS Connect Initial PDU with GCC Conference Create Request, you can specify which static virtual channels you will use. Then the server responds with a Server MCS Connect Response PDU with GCC Conference Create Response where it defines the ID of the I/O Channel and the ID’s for each static virtual channel requested.
The client sends a Client MCS Erect Domain Request PDU and a Client MCS Attach User Request PDU. Then, the server sends a Server MCS Attach User Confirm PDU. In this, you can find the user channel ID. Lastly, the client sends a Client MCS Channel Join Request PDU for each static virtual channel requested, I/O channel and user channel.
Then the server responds with a Server MCS Channel Join Confirm PDU.
RDP Security Commencement
In this optional phase, the session keys that are used to encrypt and validate the integrity of RDP traffic are generated.
Secure Settings Exchange
The client sends the username, password and reconnection cookie using Client Info PDU.
Optional Connect-Time Auto-Detection
This phase is optional with a goal of detecting different characteristics of the network.
In this phase, the server sends a Server License Error PDU - Valid Client.
Optional Multitransport Bootstrapping
This optional phase can be used if the server chooses to use multitransport connections.
This is the last phase. The client sends a Client Synchronize PDU, Client Control PDU - Cooperate, Client Control PDU - Request Control, Client Persistent Key List PDU, Client Font List PDU and the server responds with Server Control PDU - Cooperate, Server Control PDU - Granted Control, Server Font Map PDU.
When the connection sequence is done, you can send data over the static virtual channels.
Reaching the Vulnerable Function
If you do some xrefs and backtrace you will find that CRdpDynVCMgr::HandleIncomingDvcData from rdpserverbase.dll uses the class DecompressUnchopper. CRdpDynVC::GetDecompressor, who calls to DecompressRdp8__CreateInstance, who calls DecompressUnchopper::Decompress.
What does this mean? The method CRdpDynVCMgr::HandleIncomingDvcData, as the name says, handles data send over a dynamic virtual channel. The first thing to do is create a dynamic virtual channel and send data over it in order to reach CRdpDynVCMgr::HandleIncomingDvcData method.
This is done with Dynamic Virtual Channels ([MS-RDPEDYC]: Remote Desktop Protocol: Dynamic Channel Virtual Channel Extension), which are an extension of static virtual channels. If you want to use them, create a static virtual channel called “DRDYNVC” in the Basic Settings Exchange phase.
The first step to use a DVC (Dynamic Virtual Channel) is the initialization phase. In this step, the server and the client negotiate the supported version. The server sends a DVC Capabilities Request PDU indicating the supported version and the client responds with DVC Capabilities Response PDU (DYNVC_CAPS_RSP).
When the DVC is initialized, the server opens a DVC. The server sends a DVC Create Request PDU (DYNVC_CREATE_REQ) indicating the channel ID and the name of the channel. Then, the client responds with a DVC Create Response PDU (DYNVC_CREATE_RSP).
After the channel is opened, the client can send/receive data over it.
You can send data over the dynamic virtual channel by using DVC Data PDU (DYNVC_DATA).
Returning to the vulnerability, you now need to send compressed data because the name of the vulnerable method is Decompress.
To send compressed data over the DVC you must send a RDP_SEGMENTED_DATA. This structure has an array of RDP_DATA_SEGMENT. The RDP_DATA_SEGMENT structure has a member called bulkData which is a RDP8_BULK_ENCODED_DATA structure where the compressed data is stored.
After some reversing, we found that to reach the vulnerable function and generate the heap overflow you must use MULTIPART (0xE1) as descriptor in RDP_SEGMENTED_DATA. Also, the [rbx+3] is the member size of RDP_DATA_SEGMENT. Put a value in the range of [0xFFFFFFFF-0x1FFF, 0xFFFFFFFF] to generate the integer overflow and after the heap overflow.
Now, you’re able to create the evil packet and trigger the heap overflow:
What are the next steps?
- Determine which heap our vulnerable chunk is in (Segment/NT)
- Determine which component uses the chunk to make the allocation (LFH/VS/etc.)
- Find a way to make a heap spray
- Find a way to leak memory