A Bug Has No Name: Multiple Heap Buffer Overflows In the Windows DNS Client

Share this…

CVE-2017-11779 fixed by Microsoft in October of 2017, covers multiple memory corruption vulnerabilities in the Windows DNS client. The issues affect computers running Windows 8/ Server 2012 or later, and can be triggered by a malicious DNS response. An attacker can exploit this issue to gain arbitrary code execution in the context of the application that made the DNS request.

This means that if an attacker controls your DNS server (e.g., through a Man-in-the-Middle attack or a malicious coffee-shop hotspot) – they can gain access to your system. This doesn’t only affect web browsers – your computer makes DNS queries in the background all the time, and any query can be responded to in order to trigger this issue.

The below video explains more about this CVE, but if you’re interested in the technical details, keep reading.

Technical TL;DR

In Windows 8/Windows Server 2012, Microsoft extended DNSSEC support to the Windows DNS client, the code for which exists in DNSAPI.dll. One of the DNS Resource Records (RRs) introduced to support DNSSEC was the NSEC3 record, handled by the Nsec3_RecordRead function.

The vulnerabilities in CVE-2017-11779 all relate to how the Nsec3_RecordRead function unsafely parses NSEC3 RRs, resulting in multiple out-of-bounds writes. The responsible DNSAPI DLL is commonly used by the DnsCache service which runs under svchost.exe as the NT AUTHORITY\NETWORK SERVICE user, and provides the DNS caching service for the DNS client on Windows systems. It’s also imported by other applications for making DNS queries.

It is important to note that as the record is malformed, it should not traverse any sane DNS resolvers. Because of this, the issue can only be triggered if the victim(s) are accepting DNS responses directly from the attacker-controlled server. Typically, this would require an active Man-in-the-Middle attack.

The title of this blog post is a reference to the vulnerable DNS RR, NSEC3 (Next Secure Record version 3). NSEC3 records can be used by resolvers to verify the non-existence of a record name as part of DNSSEC validation.

Also, I quite like “Game of Thrones.”

The Bug – Explained

Your computer performs DNS requests when you’re browsing, or streaming music, and even when you’re doing nothing. Background actions such as your computer checking for Windows updates also make these requests. Most of the time whatever program is making the request doesn’t see the response directly, but it rather goes through the DNS caching service, which then saves the response for reuse. This cuts down on the number of DNS requests the system needs to make.

DNS is a plaintext protocol and vulnerable to Man-in-the-Middle attacks. Partially because of this, DNSSEC (Domain Name Security) extensions were created. This introduced several new DNS records to deliver this information to DNS clients and servers. DNSSEC tries to solve some problems, but not the most common ones — and it introduces new problems, too.

Windows added client functionality for DNSSEC in Windows 8 and Server 2012, with the introduction of several new DNS records. This functionality came along with a vulnerability in one of the records used for DNSSEC: NSEC3.  The Windows DNS client doesn’t do enough sanity checking when it processes a DNS response that contains an NSEC3 record. Malformed NSEC3 records can trigger this vulnerability and corrupt the memory of the DNS client. If this is done carefully, it can result in arbitrary code execution on the target system.

Because the record is malformed, it doesn’t make it through the normal DNS system. Servers along the way will drop it because it doesn’t fit the standard for NSEC3 records. This is a good thing, because otherwise this issue would be easier to exploit and have far more serious implications. So, for an attacker to exploit this issue, they need to be between you and the DNS server you’re using. For example, if you’re using coffee shop Wi-Fi and someone is tampering with it, or if they’ve hacked your cable router – they can modify DNS responses that your computer receives.

This vulnerability has some additional upsides for attackers:

  • First, it can result in code execution at different privilege levels. including as the administrative system user. If the DNS cache service crashes, the next DNS response will go directly to the application that made the request. This means that an attacker could crash the DNS caching service, and wait until a DNS query that is known to be related to a sensitive system task, like Windows Update. The attacker could potentially respond to this request with the malicious code execution payload and successfully gain complete control over the victim’s system.
  • Second, an attacker has unlimited attempts to exploit it. The DNS caching service that handles the storage of DNS responses automatically restarts when it crashes, and it won’t notify the user of the crash. So, an attacker can respond to requests coming directly from applications with innocuous responses, to ensure the caching service restarts, and then attack that service repeatedly. This can help an attacker bypass some of the protections Microsoft has built into Windows to protect against memory corruption vulnerabilities.

What is Affected and How Do We Fix This?

These vulnerabilities affect all versions of Windows from Windows 8 / Windows Server 2012 through Windows 10 / Windows Server 2016. Versions of Windows prior to this are not vulnerable.

To remediate this vulnerability, immediately apply the Microsoft security patches released in October 2017.

Technical Details

Three heap buffer overflows in DNSAPI.dll can be triggered by a malicious DNS server, or by a man-in-the-middle attack, by sending a malformed NSEC3 Response Record (“RR”) in reply to a DNS request. All three conditions exist in one basic block.

All addresses in this report refer to DNSAPI.dll version 6.3.9600.18512 (x86, Windows 8.1). These issues have also been confirmed for version 10.0.14393.206 (x64, Windows 10).

Allocation of Destination Buffer

The Nsec3_RecordRead function allocates a destination buffer (“destbuf”) for the NSEC3 response data by calling DNSAPI!DNS_AllocateRecordEx. The allocation size for destbuf is determined by the 16-bit user-controlled data length field, the last generic field in a DNS Resource Record prior to record-specific data. By manipulating the data length field, the size of destbuf can be controlled by an attacker and used to perform out-of-bounds read and out-of-bounds write attacks.

The data length field described above is highlighted in the following Wireshark capture of an NSEC3 Resource Record:

FIGURE 1 – Captured NSEC3 Resource Record with user-supplied data length field highlighted

DNSAPI retrieves this value in the Dns_ReadRecordStructureFromPacket function, and it is consumed in Nsec3_RecordRead when determining buffer allocation size.

Heap Overflow #1 – NSEC3 Salt_Length

The first heap buffer overflow is triggered at DNSAPI!Nsec3_RecordRead+0xB9, where memcpy is provided the user-supplied 8-bit NSEC3 Salt Length as its copy size. The location of the NSEC3 Salt Length  field in an example NSEC3 Resource Record is shown below:

FIGURE 2 – User-controlled NSEC3 Salt Length field

This heap overflow can be used to write out of bounds if the attacker-controlled NSEC3 Salt Length size is greater than the attacker-controlled size of destbuf.

The Nsec3_RecordRead function uses the attacker-controlled value of the NSEC3 Salt Length field as the size argument for memcpy, shown below:

FIGURE 3 – User-supplied value used as size parameter

This memcpy copied 0xff bytes from the attacker’s DNS Resource Record to destbuf.

Heap Overflow #2 – NSEC3 Hash Length

The second heap buffer overflow occurs shortly after the first instance, in the same basic block, at Nsec3_RecordRead+0xD9:

FIGURE 4 – Disassembly showing population of copy size argument

As with the first instance, the call to memcpy takes a user-supplied value for the size argument. The size of the destination buffer is the same user-controlled buffer used in instance 1, destbuf. The location of the NSEC3 Hash Length field in an example NSEC3 Resource Record is shown below:

Heap Overflow #3 – Integer Underflow

The final, and most useful heap buffer overflow does not directly consume a user-supplied length field, but instead performs some arithmetic on other user-supplied fields prior to use in a memcpy operation in the same basic block at Nsec3_RecordRead+0x106. This is shown as two subtractions:

FIGURE 5 – Unsafe subtract operations performed against user-supplied data length

Above, two sub operations are carried out against the saved record length value retrieved from the malicious packet (DNS_RR_Size). The saved record length (saved_record_len) is the user-supplied DNS_RR_Size less 6; this value is intended to indicate the length of the NSEC3 record without the leading header data about hash algorithm and number of iterations.

The following pseudo-code demonstrates the calculation performed:

The issue here lies in the fact that the nsec3_nho_len value is used as an unsigned 16-bit integer by memcpy. By setting the values of the nsec3_hash_len and nsec3_salt_len fields to values cumulatively larger than the value of the saved_record_len field, an integer underflow occurs, resulting in a very large value being passed as the size to memcpy. This can be leveraged as an out-of-bounds read and out-of-bounds write.

A proof of concept can be created to trigger an out-of-bounds read and out-of-bounds write, using the following values:

  • saved_record_len of 0x00f9 (a.k.a DNS_RR_Size of 0xff – 0x6)
  • nsec3_hash_len of 0xf8
  • nsec3_salt_len of 0x6

This combination resulted in the size parameter for the final value being 0xfffb (0x00f9 – 0xf8 – 0x06 = 0xfffb). At the final memcpy at Nsec_RecordRead+0x106, the various arguments had the following states:

dest (esp) size: 0x128

src (esp+0x4) size: 11e0

copy_size (esp+0x8): fffb

Example Exception

In the following case, attacker-controlled data is used for both the source and destination registers, eax and edx:

FIGURE 6 – Example exception resulting from heap corruption

To trigger this exception, the first response set the ‘truncated’ DNS bit, forcing the client to perform a second DNS query over TCP to enable larger payload delivery. With careful heap manipulation, it is possible to overwrite objects in memory that contain function pointers, ultimately resulting in the execution of arbitrary code.

Considerations for Exploitation

These vulnerabilities have several desirable attributes for exploitation: the vulnerability can be triggered without user interaction, it can affect processes running at different privilege levels (including SYSTEM) and the DnsCache service under svchost.exe restarts on failure. This means an attacker can first kill the DnsCache service to have a more deterministic starting state of the heap, exploit the issue multiple times to leak addresses for defeating ASLR, and then use the disclosed addresses when delivering the final exploit.

As a constraint, at this point it is unknown if a malicious payload could successfully traverse a recursive DNS server. To date, tested DNS resolvers do not accept the malformed record.

Wrapping Up

This issue can be exploited on a local network without user interaction, and deserves attention and timely patching.

New functionality always brings new vulnerabilities, and the introduction of DNSSEC to Windows was no exception. The last mile (between your computer and its DNS resolver) will remain a weak point in DNS, so consider the use of a Virtual Private network (VPN) or using your phone as a personal hotspot to reduce the likelihood of an attacker interfering with your DNS traffic.