By Steve Povolny · August 05, 2020
This document has been prepared by McAfee Advanced Threat Research in collaboration with JSOF who discovered and responsibly disclosed the vulnerabilities. It is intended to serve as a joint research effort to produce valuable insights for network administrators and security personnel, looking to further understand these vulnerabilities to defend against exploitation. The signatures produced here should be thoroughly considered and vetted in staging environments prior to being used in production and may benefit from specific tuning to the target deployment. There are technical limitations to this work, including the fact that more complex methods of detection might be required to detect these vulnerabilities. For example, multiple layers of encapsulation may obfuscate the exploitation of the flaws and increase the difficulty of detection.
We have also provided packet captures taken from the vulnerability Proof-of-Concepts as artifacts for testing and deployment of either the signatures below or customized signatures based on the detection logic. Signatures and Lua Scripts are located on ATR’s Github page**,** as well as inline and in the appendix of this document respectively.
As of this morning (August 5th), JSOF has presented additional technical detail and exploitation analysis at BlackHat 2020, on the two most critical vulnerabilities in DNS.
The information provided herein is subject to change without notice, and is provided “AS IS”, with all faults, without guarantee or warranty as to the accuracy or applicability of the information to any specific situation or circumstance and for use at your own risk. Additionally, we cannot guarantee any performance or efficacy benchmarks for any of the signatures.
tfDnsExpLabelLength
Leading to Heap Overflow and RCECVE: CVE-2020-11901 (Variant 1) **CVSS:**9 **Protocol(s): **DNS over UDP (and likely DNS over TCP) **Port(s): **53
Vulnerability description:
In the Treck stack, DNS names are calculated via the function tfDnsExpLabelLength
. A bug exists in this function where the computation is performed using an unsigned short, making it possible to overflow the computed value with a specially constructed DNS response packet. Since tfDnsExpLabelLength
computes the full length of a DNS name after it is decompressed, it is possible to induce an overflow using a DNS packet far smaller than 216 bytes. In some code paths, tfGetRawBuffer
is called shortly after tfDnsExpLabelLength
, allocating a buffer on the heap where the DNS name will be stored using the size computed by tfDnsExpLabelLength
, thus leading to a heap overflow and potential RCE.
While newer versions of the Treck stack will stop copying the DNS name into the buffer as soon as a character that isn’t alphanumeric or a hyphen is reached, older versions do not have this restriction and further use predictable transaction IDs for DNS queries, making this vulnerability easier to exploit.
Limitations or special considerations for detection:
Ideally, detection logic for this vulnerability would involve independently computing the uncompressed length of all DNS names contained within incoming DNS responses. Unfortunately, this may be computationally expensive for a device to perform for every incoming DNS response, especially since each one may contain many DNS names. Instead, we must rely on a combination of heuristics.
Furthermore, it is currently unclear whether exercising this vulnerability is possible when using EDNS(0) or DNS over TCP. We recommend assuming it is possible for the purposes of implementing detection logic. During our testing, an inconsistency in how Suricata handled DNS over TCP was discovered – in some cases it was correctly identified as DNS traffic and in other cases, it was not. Consequently, two rules have been created to determine the size of DNS over TCP traffic. The second rule uses the TCP primitive instead of the DNS primitive; however, the second rule will only be evaluated if not flagged by the first rule.
Because the Suricata rule in dns_invalid_size.rules uses the DNS responses’ EDNS UDP length, which may be controlled by the attacker, a second upper limit of 4096 bytes is enforced.
Recommended detection criteria:
False positive conditions (signatures detecting non-malicious traffic):
Networks expecting non-malicious traffic containing DNS names using non-alphanumeric characters or an abnormally large number of DNS compression pointers may generate false positives. Unfortunately, checking for pointers in only the domain name fields is insufficient, as a malicious packet could use a compression pointer that points to an arbitrary offset within said packet, so our rule instead checks every byte of the DNS layer. Consequently, Treck’s overly liberal classification of DNS compression pointers means that our rule will often misclassify unrelated bytes in the DNS payload as pointers.
In our testing, we ran into false positives with domain names containing spaces or things like “https://”. Per the RFCs, characters such as “:” and “/” should not be present in domain names but may show up from time to time in real, non-malicious traffic. The list of acceptable characters should be expanded as needed for the targeted network to avoid excessive false positives. That being said, keeping the list of acceptable characters as small as possible will make it more difficult to sneak in shellcode to leverage one of the Ripple20 DNS vulnerabilities.
False positives on the DNS size rules may occur when DNS over TCP is used if Suricata does not properly classify the packet as a DNS packet – something that has occurred multiple times during our testing. This would cause the second size check to occur, which assumes that all traffic over port 53 is DNS traffic and processes the payload accordingly. As a result, any non-DNS traffic on TCP port 53 may cause false positives in this specific case. It is recommended the port number in the rule be adjusted for any network where a different protocol is expected over port 53.
Fragmentation of DNS traffic over TCP may also introduce false positives. If the streams are not properly reconstructed at the time the rules execute on the DNS payload, byte offsets utilized in the attached Lua scripts could analyze incorrect data. Fragmentation in DNS response packets is not common on a standard network unless MTU values have been set particularly low. Each rule should be evaluated independently prior to use in production based on specific network requirements and conditions.
False negative conditions (signatures failing to detect vulnerability/exploitation):
False negatives are more likely as this detection logic relies on heuristics due to computation of the uncompressed DNS name length being too computationally expensive. Carefully constructed malicious packets may be able to circumvent the suggested pointer limitations and still trigger the vulnerability.
Signature(s):
dns_invalid_size.rules:
alert dns any any ‑> any any (msg:"DNS packet too large"; flow:to_client; flowbits:set,flagged; lua:dns_size.lua; sid:2020119014; rev:1;)
Lua_ script (dns_size.lua) can be found in _Appendix A
alert tcp any 53 -> any any (msg:"DNS over TCP packet too large"; flow:to_client,no_frag; flowbits:isnotset,flagged; lua:dns_tcp_size.lua; sid:2020119015; rev:1;)
Lua_ script (dns_tcp_size.lua) can be found in _Appendix A
dns_invalid_name.rules:
alert dns any any -> any any (flow:to_client; msg:"DNS response contains invalid domain name"; lua:dns_invalid_name.lua; sid:2020119013; rev:1;)
Lua_ script (dns_invalid_name.lua) can be found in _Appendix A
dns_heap_overflow.rules:
# Variant 1
alert dns any any -> any any (flow:to_client; msg:"Potential DNS heap overflow exploit (CVE-2020-11901)"; lua:dns_heap_overflow_variant_1.lua; sid:2020119011; rev:1;)
Lua_ script (dns_heap_overflow_variant_1.lua) can be found in _Appendix A
CVE: CVE-2020-11901 (Variant 2) **CVSS:**9 **Protocol(s):**DNS/UDP (and likely DNS/TCP) **Port(s): **53
Vulnerability description:
In some versions of the Treck stack, a vulnerability exists in the way the stack processes DNS responses containing CNAME records. In such records, the length of the buffer allocated to store the DNS name is taken from the RDLENGTH field, while the data written is the full, decompressed domain name, terminating only at a null byte. As a result, if the size of the decompressed domain name specified in RDATA exceeds the provided RDLENGTH in a CNAME record, the excess is written past the end of the allocated buffer, resulting in a heap overflow and potential RCE.
Limitations or special considerations for detection:
Although exploitation of this vulnerability has been confirmed using malicious DNS over UDP packets, it has not been tested using DNS over TCP and it is unclear if such packets would exercise the same vulnerable code path. Until this can be confirmed, detection logic should assume both vectors are vulnerable.
Recommended detection criteria:
* In this case, the “actual size” corresponds to how vulnerable versions of the Treck stack compute the RDATA length, which involves adding up the size of every label until either null byte, a DNS compression pointer, or the end of the payload is encountered. The Treck stack will follow and decompress the pointer that terminates the domain name, if present, but the script does not as this computation is simply too expensive, as mentioned previously.
False positive conditions (signatures detecting non-malicious traffic):
False positives should be unlikely, but possible in scenarios where network devices send non-malicious traffic where RDLENGTH is not equal to the size of RDATA, thereby breaking RFC 1035.
False negative conditions (signatures failing to detect vulnerability/exploitation):
Since the detection logic does not perform decompression when computing the “actual size” of RDATA, it will fail to detect malicious packets that contain domain names whose length only exceeds RDLENGTH after decompression. Unfortunately, coverage for this case is non-trivial as such packets are actually RFC-compliant. According to RFC 1035, section 4.1.4:
If a domain name is contained in a part of the message subject to a length field (such as the RDATA section of an RR), and compression is used, the length of the compressed name is used in the length calculation, rather than the length of the expanded name.
Besides the computational overhead, enforcing such a check would likely result in very high false positive rates.
Signature(s):
dns_heap_overflow.rules:
# Variant 2
alert dns any any -> any any (flow:to_client; msg:"Potential DNS heap overflow exploit (CVE-2020-11901)"; lua:dns_heap_overflow_variant_2.lua; sid:2020119012; rev:1;)
_Lua script (dns_heap_overflow_variant_2.lua) can be found in _Appendix A
CVE: CVE-2020-11897 **CVSS:**10 **Protocol(s):**IPv6 Port(s): N/A
Vulnerability description:
When processing IPv6 incoming packets, an inconsistency parsing the IPv6 routing header can be triggered where the header length is checked against the total packet and not against the fragment length. This means that if we send fragmented packets with the overall size greater than or equal to the specified routing header length, then we process the routing header under the assumption that we have enough bytes in our current fragment (where we have enough bytes in the overall reassembled packet only). Thus, using routing header type 0 (RH0) we can force read and write into out-of-bounds memory location.
There is also a secondary side effect where we can get an info leak in a source IPv6 address in an ICMP parameter returned from the device.
Limitations or special considerations for detection:
The RFC for RH0 defines the length field as equal to “two times the number of addresses in the header.” For example, if the routing header length is six, then there are three IPv6 addresses expected in the header. Upon reconstruction of the fragmented packets, the reported number of addresses is filled with data from the fragments that follow. This creates “invalid” IPv6 addresses in the header and potentially malforms the next layer of the packet. During exploitation, it would also be likely for the next layer of the packet to be malformed. Although ICMP can be used to perform an information leak, it is possible for the next layer to be any type and therefore vary in length. Verification of the length of this layer could therefore be very expensive and non-deterministic.
Recommended detection criteria:
Notes:
The routing header type 0 was deprecated in IPv6 traffic in RFC 5095 as of December 2007. As a result, it may be feasible simply to detect packets using this criterion. False positives may be possible in this scenario for legacy devices or platforms. Suricata already provides a default rule for this scenario which has been added below. According to the RFC, routers are not supposed to fragment IPv6 packets and must support an MTU of 1280, which would always contain all of the RH0 header, unless an unusual amount of header extensions or an unusually large header is used. If this is followed, then a packet using the RH0 header should never be fragmented across the RH0 extension header bounds and any RH0 packet fragmented in this manner should be treated as potentially malicious. Treating any fragmented RH0 packet as potentially malicious may be sufficient. Furthermore, treating any fragmented RH0 packet with fragments size below a threshold as well as IPv6 packets with multiple extension headers or an unusually large header above a threshold may provide high accuracy detection.
False positive conditions (signatures detecting non-malicious traffic):
If all detection criteria outlined above are used, false positives should be minimal since the reported length of a packet should match its actual length and the next header should never contain malformed data. If only routing header type 0 is checked, false positives are more likely to occur. In the additional provided rule, false positives should be minimal since RH0 is deprecated and the ICMP header should never have invalid checksums or unknown codes.
False negative conditions (signatures failing to detect vulnerability/exploitation):
False negatives may occur if the signature is developed overly specific to the layer following IPv6, for example, ICMP. An attacker could potentially leverage another layer and still exploit the vulnerability without the information leak; however, this would still trigger the default RH0 rule. In the second rule below, false negatives are likely to occur if:
Signature(s):
Ipv6_rh0.rules:
alert ipv6 any any -> any any (msg:"SURICATA RH Type 0"; decode-event:ipv6.rh_type_0; classtype:protocol-command-decode; sid:2200093; rev:2;)
alert ipv6 any any -> any any (msg:"IPv6 RH0 Treck CVE-2020-11897"; decode-event:ipv6.rh_type_0; decode-event:icmpv6.unknown_code; icmpv6-csum:invalid; dsize:>5; sid:2020118971; rev:1;)
CVE: CVE-2020-11896 **CVSS:**10.0 **Protocol(s):**IPv4/UDP Port(s): Any
Vulnerability description:
The Treck TCP/IP stack does not properly handle incoming IPv4-in-IPv4 packets with fragmented payload data. This could lead to remote code execution when sending multiple specially crafted tunneled UDP packets to a vulnerable host.
The vulnerability is a result of an incorrect trimming operation when the advertised total IP length (in the packet header) is strictly less than the data available. When sending tunneled IPv4 packets using multiple fragments with a small total IP length value, the TCP/IP stack would execute the trimming operation. This leads to a heap overflow situation when the packet is copied to a destination packet allocated based on the smaller length. When the tunneled IPv4 packets are UDP packets sent to a listening port, there’s a possibility to trigger this exploit if the UDP receive queue is non-empty. This can result in an exploitable heap overflow situation, leading to remote code execution in the context in which the Treck TCP/IP stack runs.
Recommended detection criteria:
In order to detect an ongoing attack, the following conditions should be met if encapsulation can be unpacked:
* Flag MF = 1 with any offset, or
The fourth condition above is required to activate the code-path which is vulnerable, as it spreads the data to be copied across multiple in-memory buffers. The final detection step is the source of the buffer overflow, as such, triggering on this may be sufficient.
Depending on the limitations of the network inspection device in question, a looser condition could be used, though it may be more prone to false positives.
In order to detect an ongoing attack if encapsulation cannot be unpacked:
* Flag MF = 1 with any value in offset field, or
The final condition shown above is not something that should be seen in a normal network.
Fragmentation, when it occurs, is the result of data overflowing the MTU of a given packet type. This indicates the final fragment should be no larger than any other fragment – and in practice would likely be smaller. The inverse, where the final fragment is somehow larger than previous fragments, indicates that the fragmentation is not the result of MTU overflow, but instead something else. In this case, malicious intent.
As network monitors in common usage are likely to have the ability to unpack encapsulation, only that ruleset is provided.
Limitations or special considerations for detection:
The Treck stack supports (at least) two levels of tunneling. Each tunnel level can be IPv4-in-IPv4, IPv6-in-IPv4, or IPv4-in-IPv6. The above logic is specific to the IPv4-in-IPv4, single level of tunneling case. In cases of deeper nesting, either a recursive check or a full unwrapping of all tunneling layers will be necessary.
False positive conditions (signatures detecting non-malicious traffic):
False positives should be minimal if all detection criteria outlined above are used in the case where the tunneling can be unpacked. In the case where tunneling cannot be unpacked, this is unlikely to trigger many false positives in the presence of standards compliant applications. Fragmentation as seen here is simply not common.
False negative conditions (signatures failing to detect vulnerability/exploitation):
False negatives could occur with deeper levels of nesting, or nesting of IPv6.
Signature(s):
ipv4_tunneling.rules:
alert ip any any -> any any (msg:"IPv4 TUNNELING EXPLOIT (CVE‑2020‑11896)"; ip_proto:4; lua:tunnel_length_check.lua; sid:2020118961; rev:1;)
Lua_ script (tunnel_length_check.Lua) can be found in _Appendix A
Appendix A contains Lua scripts that are required for usage with corresponding Suricata signatures. These scripts are also located on McAfee ATR’s GitHub