Exploits & Vulnerabilities
CVE-2019-0725: An Analysis of Its Exploitability
It’s worth noting that DHCP-related vulnerabilities are drawing more attention in Patch Tuesdays this year. An example is CVE-2019-0725, which doesn’t require user interaction, and affects all versions of Windows Server. How bad is it exactly?
May’s Patch Tuesday saw what is likely to be one of the most prominent vulnerabilities this year with the “wormable” Windows Terminal Services vulnerability (CVE-2019-0708). However, there’s another remote code execution (RCE) vulnerability that would be hard to ignore: CVE-2019-0725, an RCE vulnerability in Windows Dynamic Host Configuration Protocol (DHCP) Server. It’s worth noting that DHCP-related vulnerabilities are drawing more attention in Patch Tuesdays this year. An example is a different RCE flaw (CVE-2019-0626) that was patched in the DHCP server last February.
CVE-2019-0725 doesn’t require user interaction, and affects all versions of Windows Server. How bad — and exploitable — is CVE-2019-0725, exactly?
Microsoft’s CVSS 3.0 rating for CVE-2019-0725 has a base score of 8.1. A successful attack would allow system-level code execution, as shown by the high Confidentiality, Integrity, and Availability impact ratings. The severity of the vulnerability across all versions of Windows Server is rated as critical. Additionally, there are no privileges required to mount a successful attack.
However, its Attack Complexity rating is high, which means there is likely a major factor of the vulnerability that is not fully under an attacker’s control. In this case, that rating is due at least in part to the fact that the vulnerability is a use-after-free caused by a race condition, a flaw caused by unexpected timing of events that affect other actions.
How exactly does this race condition work? Let’s quickly go through how an address is generally assigned via DHCP.
A client looking to have an IP address assigned will first send a DISCOVER message, generally to a broadcast address (FF:FF:FF:FF:FF:FF at the hardware layer and 255.255.255.255 at the IP layer). If there is a DHCP server in the same broadcast domain, and it has IP addresses available to allocate, it will respond with an OFFER message. It contains details of the IP address that the client should use, as well as other information such as DNS servers.
The DHCP client then sends a REQUEST message, which contains additional information about the client and confirms the IP address that the client is requesting (typically the address sent by the server in the OFFER message). If the server accepts the REQUEST message from the client, it sends a DHCP ACK to notify the client that it can now use the assigned IP address. With this information in mind, let’s take a look at the vulnerability itself in some detail.
Triggering the Race Condition and Use After Free
The DHCP server is implemented in dhcpssvc.dll and run via svchost.exe. Incoming DHCP messages are handled by the function ProcessMessage(). It first calls a function to extract the DHCP Options from the incoming message. This takes place early because DHCP Options contains information such as the requested IP address, host name, and most importantly, the DHCP message type (e.g., DISCOVER or REQUEST). ProcessMessage() will call a handler function depending on the DHCP message type. In the case of a DISCOVER message, the function called is ProcessDhcpDiscover(), which is the problematic function with respect to the vulnerability. The DHCP server is implemented in dhcpssvc.dll and run via svchost.exe.
Figure 1. ProcessMessage() checking the DHCP message type
The Windows DHCP Server keeps track of IP address leases that are “pending”. This means the IP address has been internally assigned to the client but not necessarily offered or accepted by the client. In order to keep track of these pending leases, the DHCP server uses a list of references to PendingCtxt structures. These PendingCtxt structures are contained in heap-based buffers and consist of information such as the client hardware address, lease, renewal, and rebinding times, the IP address allocated to the client, and a flag designating whether an OFFER has been sent for this particular lease. When ProcessDhcpDiscover() is first called, it checks to see if it has an existing PendingCtxt. It does this by using the client hardware address, contained in the DHCP header, as a parameter to the function DhcpFindPendingCtxt().
If no current PendingCtxt is found for a particular client, an address is allocated from the pool of available addresses or an address previously assigned to this client. The function DhcpProcessDiscoverForValidatedAddress() is then called to perform the remaining tasks required to construct the OFFER message that will be sent in response to DISCOVER. DhcpProcessDiscoverForValidatedAddress() retrieves the configured lease information such as lease, renewal, and rebinding times configured on the server. That information, along with the offered IP address and subnet mask, is then passed to the function DhcpAddPendingCtxt(). The function allocates a heap-based buffer for a new PendingCtxt structure, which is filled with the pending lease information, then added to the list of all pending leases.
Figure 2. Allocation and filling of PendingCtxt structure in DhcpAddPendingCtxt()
Once the PendingCtxt structure has been added, the OFFER message is constructed in the function DhcpRespondToDiscover() and sent to the client.
Because PendingCtxt structures may, in theory, be accessed by more than one server thread at any point in time, access to the structures is generally contained within the DhcpGlobalInprogressCritSect critical section. This is used to limit unexpected behavior due to shared access of the same resource by only allowing one thread or process to run inside the critical section at any given time. In ProcessDhcpDiscover(), the DhcpGlobalInprogressCritSect critical section is entered before DhcpFindPendingCtxt() is called. The thread then leaves the critical section if there is no PendingCtxt, or after some information in the existing PendingCtxt structure is verified.
Figure 3. Entering of critical section for initial operations on an existing PendingCtxt structure
However, after the thread leaves this initial critical section, there is one additional direct access of the PendingCtxt structure referenced by the address located in the RBX register. This access checks the value of the “OFFER flag” to see if an OFFER has already been sent to this client. Although this direct access is protected by another critical section, there is a tiny window between leaving the previous critical section and entering the new critical section (highlighted in Figure 4).
Figure 4. Instructions between critical sections protecting access to the PendingCtxt structure
Due to the unpredictable nature of thread scheduling, there is no guarantee that the PendingCtxt structure will still exist. A thread that removes the PendingCtxt structure may be chosen to run after ProcessDhcpDiscover() has left the first critical section, but before the function enters the next critical section. This race condition can result in a use after free when the PendingCtxt structure is accessed again.
There are several things that may cause the PendingCtxt structure to be freed, a task performed by the function DhcpDeletePendingCtxt(). A PendingCtxt may simply expire and be cleaned up, something an attacker has no control over. However, sending a REQUEST message with a requested IP address that the server cannot allocate, or a RELEASE message, will cause DhcpDeletePendingCtxt() to be called and the previously allocated heap buffer to be freed.
Figure 5. DhcpProcessRelease() looking for an existing PendingCtxt and deleting it if present
Caveats and Exploitability
An attacker can trigger the use after free by sending at least two DISCOVER messages. One can be sent to create the initial PendingCtxt, and another to cause the lookup and access of the created PendingCtxt. The attacker must also send a perfectly timed RELEASE or crafted REQUEST message.
In reality, it is effectively impossible to trigger such a race condition on the first try. An attacker would likely have to concurrently send a large number of DISCOVER and RELEASE or REQUEST messages. Our tests showed that it took 10 seconds to several minutes to trigger the race condition. A highly unusual number of DHCP messages were also sent from our client the entire time.
While triggering the vulnerability may seem easy, actually gaining code execution as a result of use after free is incredibly challenging, if at all possible. Attackers will need to create a large volume of traffic on the network to have a chance. However, because triggering the vulnerability may lead to a crash of the DHCP server service, an attacker may use this denial-of-service capability in order to run a rogue DHCP server that can be used to perform attacks such as DNS cache poisoning. As with any critical vulnerability, it’s recommended that organizations patch and update their Microsoft DHCP servers to further lessen their attack surface.