| rfc9267.original | rfc9267.txt | |||
|---|---|---|---|---|
| Independent Submission S. Dashevskyi | Independent Submission S. Dashevskyi | |||
| Internet-Draft D. dos Santos | Request for Comments: 9267 D. dos Santos | |||
| Intended status: Informational J. Wetzels | Category: Informational J. Wetzels | |||
| Expires: November 18, 2022 A. Amri | ISSN: 2070-1721 A. Amri | |||
| Forescout Technologies | Forescout Technologies | |||
| May 18, 2022 | July 2022 | |||
| Common implementation anti-patterns related | Common Implementation Anti-Patterns Related to Domain Name System (DNS) | |||
| to Domain Name System (DNS) resource record (RR) processing | Resource Record (RR) Processing | |||
| draft-dashevskyi-dnsrr-antipatterns-06 | ||||
| Abstract | Abstract | |||
| This memo describes common vulnerabilities related to Domain Name | This memo describes common vulnerabilities related to Domain Name | |||
| System (DNS) response record (RR) processing as seen in several DNS | System (DNS) resource record (RR) processing as seen in several DNS | |||
| client implementations. These vulnerabilities may lead to successful | client implementations. These vulnerabilities may lead to successful | |||
| Denial-of-Service and Remote Code Execution attacks against the | Denial-of-Service and Remote Code Execution attacks against the | |||
| affected software. Where applicable, violations of RFC 1035 are | affected software. Where applicable, violations of RFC 1035 are | |||
| mentioned. | mentioned. | |||
| Status of This Memo | Status of This Memo | |||
| This Internet-Draft is submitted in full conformance with the | This document is not an Internet Standards Track specification; it is | |||
| provisions of BCP 78 and BCP 79. | published for informational purposes. | |||
| Internet-Drafts are working documents of the Internet Engineering | ||||
| Task Force (IETF). Note that other groups may also distribute | ||||
| working documents as Internet-Drafts. The list of current Internet- | ||||
| Drafts is at https://datatracker.ietf.org/drafts/current/. | ||||
| Internet-Drafts are draft documents valid for a maximum of six months | This is a contribution to the RFC Series, independently of any other | |||
| and may be updated, replaced, or obsoleted by other documents at any | RFC stream. The RFC Editor has chosen to publish this document at | |||
| time. It is inappropriate to use Internet-Drafts as reference | its discretion and makes no statement about its value for | |||
| material or to cite them other than as "work in progress." | implementation or deployment. Documents approved for publication by | |||
| the RFC Editor are not candidates for any level of Internet Standard; | ||||
| see Section 2 of RFC 7841. | ||||
| This Internet-Draft will expire on November 18, 2022. | Information about the current status of this document, any errata, | |||
| and how to provide feedback on it may be obtained at | ||||
| https://www.rfc-editor.org/info/rfc9267. | ||||
| Copyright Notice | Copyright Notice | |||
| Copyright (c) 2022 IETF Trust and the persons identified as the | Copyright (c) 2022 IETF Trust and the persons identified as the | |||
| document authors. All rights reserved. | document authors. All rights reserved. | |||
| This document is subject to BCP 78 and the IETF Trust's Legal | This document is subject to BCP 78 and the IETF Trust's Legal | |||
| Provisions Relating to IETF Documents | Provisions Relating to IETF Documents | |||
| (http://trustee.ietf.org/license-info) in effect on the date of | (https://trustee.ietf.org/license-info) in effect on the date of | |||
| publication of this document. Please review these documents | publication of this document. Please review these documents | |||
| carefully, as they describe your rights and restrictions with respect | carefully, as they describe your rights and restrictions with respect | |||
| to this document. | to this document. | |||
| Table of Contents | Table of Contents | |||
| 1. Introduction | 1. Introduction | |||
| 2. Compression Pointer and Offset Validation | 2. Compression Pointer and Offset Validation | |||
| 3. Label and Name Length Validation | 3. Label and Name Length Validation | |||
| 4. Null-terminator Placement Validation | 4. Null-Terminator Placement Validation | |||
| 5. Response Data Length Validation | 5. Response Data Length Validation | |||
| 6. Record Count Validation | 6. Record Count Validation | |||
| 7. Security Considerations | 7. Security Considerations | |||
| 8. IANA Considerations | 8. IANA Considerations | |||
| 9. References | 9. References | |||
| 9.1. Normative References | 9.1. Normative References | |||
| 9.2. Informative References | 9.2. Informative References | |||
| Acknowledgements | Acknowledgements | |||
| Authors' Addresses | Authors' Addresses | |||
| 1. Introduction | 1. Introduction | |||
| Recently, there have been major vulnerabilities on DNS | Major vulnerabilities in DNS implementations recently became evident | |||
| implementations that raised attention to this protocol as an | and raised attention to this protocol as an important attack vector, | |||
| important attack vector, such as [SIGRED], [SADDNS], and | as discussed in [SIGRED], [SADDNS], and [DNSPOOQ], the latter being a | |||
| [DNSPOOQ] - a set of 7 critical issues affecting the DNS | set of 7 critical issues affecting the DNS forwarder "dnsmasq". | |||
| forwarder "dnsmasq". | ||||
| The authors of this memo have analyzed the DNS client implementations | The authors of this memo have analyzed the DNS client implementations | |||
| of several major TCP/IP protocol stacks and found a set of | of several major TCP/IP protocol stacks and found a set of | |||
| vulnerabilities that share common implementation flaws | vulnerabilities that share common implementation flaws (anti- | |||
| (anti-patterns). These flaws are related to processing DNS RRs | patterns). These flaws are related to processing DNS resource | |||
| (discussed in [RFC1035]) and may lead to critical security | records (RRs) (discussed in [RFC1035]) and may lead to critical | |||
| vulnerabilities. | security vulnerabilities. | |||
| While implementation flaws may differ from one software project to | While implementation flaws may differ from one software project to | |||
| another, these anti-patterns are highly likely to span across | another, these anti-patterns are highly likely to span multiple | |||
| multiple implementations. In fact, one of the first CVEs related to | implementations. In fact, one of the first "Common Vulnerabilities | |||
| one of the anti-patterns [CVE-2000-0333] dates back to the year 2000. | and Exposures" (CVE) documents related to one of the anti-patterns | |||
| The observations are not limited to DNS client implementations. | [CVE-2000-0333] dates back to the year 2000. The observations are | |||
| Any software that processes DNS RRs may be affected, such as | not limited to DNS client implementations. Any software that | |||
| firewalls, intrusion detection systems, or general purpose DNS packet | processes DNS RRs may be affected, such as firewalls, intrusion | |||
| dissectors (e.g., [CVE-2017-9345] in Wireshark). Similar issues may | detection systems, or general-purpose DNS packet dissectors (e.g., | |||
| also occur in DNS-over-HTTPS [RFC8484] and DNS-over-TLS [RFC7858] | the DNS dissector in Wireshark; see [CVE-2017-9345]). Similar issues | |||
| implementations. However, any implementation that deals with the DNS | may also occur in DNS-over-HTTPS [RFC8484] and DNS-over-TLS [RFC7858] | |||
| wire format is subject to the considerations discussed in this draft. | implementations. However, any implementation that deals with the DNS | |||
| wire format is subject to the considerations discussed in this | ||||
| document. | ||||
| [COMP-DRAFT] and [RFC5625] briefly mention some of these | [DNS-COMPRESSION] and [RFC5625] briefly mention some of these anti- | |||
| anti-patterns, but the main purpose of this memo is to provide | patterns, but the main purpose of this memo is to provide technical | |||
| technical details behind these anti-patterns, so that the common | details behind these anti-patterns, so that the common mistakes can | |||
| mistakes can be eradicated. | be eradicated. | |||
| We provide general recommendations on mitigating the anti-patterns. | We provide general recommendations on mitigating the anti-patterns. | |||
| We also suggest that all implementations should drop | We also suggest that all implementations should drop malicious/ | |||
| malicious/malformed DNS replies and log them (optionally). | malformed DNS replies and (optionally) log them. | |||
| 2. Compression Pointer and Offset Validation | 2. Compression Pointer and Offset Validation | |||
| [RFC1035] defines the DNS message compression scheme that can be used | [RFC1035] defines the DNS message compression scheme that can be used | |||
| to reduce the size of messages. When it is used, an entire domain | to reduce the size of messages. When it is used, an entire domain | |||
| name or several name labels are replaced with a (compression) pointer | name or several name labels are replaced with a (compression) pointer | |||
| to a prior occurrence of the same name. | to a prior occurrence of the same name. | |||
| The compression pointer is a combination of two octets: the two most | The compression pointer is a combination of two octets: the two most | |||
| significant bits are set to 1, and the remaining 14 bits are the | significant bits are set to 1, and the remaining 14 bits are the | |||
| OFFSET field. This field specifies the offset from the beginning of | OFFSET field. This field specifies the offset from the beginning of | |||
| the DNS header, at which another domain name or label is located: | the DNS header, at which another domain name or label is located: | |||
| +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |||
| | 1 1| OFFSET | | | 1 1| OFFSET | | |||
| +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | |||
| The message compression scheme explicitly allows a domain name to be | The message compression scheme explicitly allows a domain name to be | |||
| represented as: (1) a sequence of unpacked labels ending with a zero | represented as one of the following: (1) a sequence of unpacked | |||
| octet; (2) a pointer; (3) a sequence of labels ending with a pointer. | labels ending with a zero octet, (2) a pointer, or (3) a sequence of | |||
| labels ending with a pointer. | ||||
| However, [RFC1035] does not explicitly state that blindly following | However, [RFC1035] does not explicitly state that blindly following | |||
| compression pointers of any kind can be harmful [COMP-DRAFT], as we | compression pointers of any kind can be harmful [DNS-COMPRESSION], as | |||
| could not have had any assumptions about various implementations | we could not have had any assumptions about various implementations | |||
| that would follow. | that would follow. | |||
| Yet, any DNS packet parser that attempts to decompress domain names | Yet, any DNS packet parser that attempts to decompress domain names | |||
| without validating the value of OFFSET is likely susceptible to | without validating the value of OFFSET is likely susceptible to | |||
| memory corruption bugs and buffer overruns. These bugs allow for easy | memory corruption bugs and buffer overruns. These bugs make it | |||
| Denial-of-Service attacks, and may result in successful Remote Code | easier to perform Denial-of-Service attacks and may result in | |||
| Execution attacks. | successful Remote Code Execution attacks. | |||
| Pseudocode that illustrates a typical example of a broken domain name | Pseudocode that illustrates a typical example of a broken domain name | |||
| parsing implementation is shown below (Snippet 1): | parsing implementation is shown below (Figure 1): | |||
| 1:decompress_domain_name(*name, *dns_payload) { | 1: decompress_domain_name(*name, *dns_payload) { | |||
| 2: | 2: | |||
| 3: name_buffer[255]; | 3: name_buffer[255]; | |||
| 4: copy_offset = 0; | 4: copy_offset = 0; | |||
| 5: | 5: | |||
| 6: label_len_octet = name; | 6: label_len_octet = name; | |||
| 7: dest_octet = name_buffer; | 7: dest_octet = name_buffer; | |||
| 8: | 8: | |||
| 9: while (*label_len_octet != 0x00) { | 9: while (*label_len_octet != 0x00) { | |||
| 10: | 10: | |||
| 11: if (is_compression_pointer(*label_len_octet)) { | 11: if (is_compression_pointer(*label_len_octet)) { | |||
| 12: ptr_offset = get_offset(label_len_octet, | 12: ptr_offset = get_offset(label_len_octet, | |||
| label_len_octet+1); | label_len_octet+1); | |||
| 13: label_len_octet = dns_payload + ptr_offset + 1; | 13: label_len_octet = dns_payload + ptr_offset + 1; | |||
| 14: } | 14: } | |||
| 15: | 15: | |||
| 16: else { | 16: else { | |||
| 17: length = *label_len_octet; | 17: length = *label_len_octet; | |||
| 18: copy(dest_octet + copy_offset, | 18: copy(dest_octet + copy_offset, | |||
| label_len_octet+1, *length); | label_len_octet+1, *length); | |||
| 19: | 19: | |||
| 20: copy_offset += length; | 20: copy_offset += length; | |||
| 21: label_len_octet += length + 1; | 21: label_len_octet += length + 1; | |||
| 22: } | 22: } | |||
| 23: | 23: | |||
| 24: } | 24: } | |||
| 25:} | 25: } | |||
| Snippet 1 - A broken implementation of a function | ||||
| that is used for decompressing DNS domain names (pseudocode) | Figure 1: A Broken Implementation of a Function That Is Used for | |||
| Decompressing DNS Domain Names (Pseudocode) | ||||
| Such implementations typically have a dedicated function for | Such implementations typically have a dedicated function for | |||
| decompressing domain names (for example, see [CVE-2020-24338] and | decompressing domain names (for example, see [CVE-2020-24338] and | |||
| [CVE-2020-27738]). Among other parameters, these functions may | [CVE-2020-27738]). Among other parameters, these functions may | |||
| accept a pointer to the beginning of the first name label within a | accept a pointer to the beginning of the first name label within an | |||
| RR ("name") and a pointer to the beginning of the DNS payload to be | RR ("name") and a pointer to the beginning of the DNS payload to be | |||
| used as a starting point for the compression pointer | used as a starting point for the compression pointer ("dns_payload"). | |||
| ("dns_payload"). The destination buffer for the domain name | The destination buffer for the domain name ("name_buffer") is | |||
| ("name_buffer") is typically limited to 255 bytes as per | typically limited to 255 bytes as per [RFC1035] and can be allocated | |||
| [RFC1035] and can be allocated either in the stack or in the heap | either in the stack or in the heap memory region. | |||
| memory region. | ||||
| The code of the function at Snippet 1 reads the domain name | The code of the function in Figure 1 reads the domain name label by | |||
| label-by-label from a RR until it reaches the NUL octet ("0x00") that | label from an RR until it reaches the NUL octet ("0x00") that | |||
| signifies the end of a domain name. If the current label length octet | signifies the end of a domain name. If the current label length | |||
| ("label_len_octet") is a compression pointer, the code extracts the | octet ("label_len_octet") is a compression pointer, the code extracts | |||
| value of the compression offset and uses it to "jump" to another | the value of the compression offset and uses it to "jump" to another | |||
| label length octet. If the current label length octet is not a | label length octet. If the current label length octet is not a | |||
| compression pointer, the label bytes will be copied into the name | compression pointer, the label bytes will be copied into the name | |||
| buffer, and the number of bytes copied will correspond to the value | buffer, and the number of bytes copied will correspond to the value | |||
| of the current label length octet. After the copy operation, the code | of the current label length octet. After the copy operation, the | |||
| will move on to the next label length octet. | code will move on to the next label length octet. | |||
| The first issue with this implementation is due to unchecked | The first issue with this implementation is due to unchecked | |||
| compression offset values. The second issue is due to the absence of | compression offset values. The second issue is due to the absence of | |||
| checks that ensure that a pointer will eventually arrive at an | checks that ensure that a pointer will eventually arrive at a | |||
| decompressed domain label. We describe these issues in more detail | decompressed domain label. We describe these issues in more detail | |||
| below. | below. | |||
| [RFC1035] states that "... [compression pointer is] a pointer to a | [RFC1035] states that a compression pointer is "a pointer to a prior | |||
| prior occurrence of the same name". Also, according to [RFC1035], | occurance [sic] of the same name." Also, according to [RFC1035], the | |||
| the maximum size of DNS packets that can be sent over the UDP | maximum size of DNS packets that can be sent over UDP is limited to | |||
| protocol is limited to 512 octets. | 512 octets. | |||
| The pseudocode at Snippet 1 violates these constraints, as it will | The pseudocode in Figure 1 violates these constraints, as it will | |||
| accept a compression pointer that forces the code to read out of the | accept a compression pointer that forces the code to read outside the | |||
| bounds of a DNS packet. For instance, the compression pointer of | bounds of a DNS packet. For instance, a compression pointer set to | |||
| "0xffff" will produce the offset of 16383 octets, which is most | "0xffff" will produce an offset of 16383 octets, which is most | |||
| definitely pointing to a label length octet somewhere past the | definitely pointing to a label length octet somewhere past the bounds | |||
| original DNS packet. Supplying such offset values will most likely | of the original DNS packet. Supplying such offset values will most | |||
| cause memory corruption issues and may lead to Denial-of-Service | likely cause memory corruption issues and may lead to Denial-of- | |||
| conditions (e.g., a Null pointer dereference after "label_len_octet" | Service conditions (e.g., a Null pointer dereference after | |||
| is set to an invalid address in memory). As an additional example, | "label_len_octet" is set to an invalid address in memory). For | |||
| see [CVE-2020-25767], [CVE-2020-24339], and [CVE-2020-24335]. | additional examples, see [CVE-2020-25767], [CVE-2020-24339], and | |||
| [CVE-2020-24335]. | ||||
| The pseudocode at Snippet 1 allows for jumping from a compression | The pseudocode in Figure 1 allows jumping from a compression pointer | |||
| pointer to another compression pointer and it does not restrict the | to another compression pointer and does not restrict the number of | |||
| number of such jumps. That is, if a label length octet which is | such jumps. That is, if a label length octet that is currently being | |||
| currently being parsed is a compression pointer, the code will | parsed is a compression pointer, the code will perform a jump to | |||
| perform a jump to another label, and if that other label is a | another label, and if that other label is a compression pointer as | |||
| compression pointer as well, the code will perform another jump, and | well, the code will perform another jump, and so forth until it | |||
| so forth until it reaches an decompressed label. This may lead to | reaches a decompressed label. This may lead to unforeseen side | |||
| unforeseen side-effects that result in security issues. | effects that result in security issues. | |||
| Consider the excerpt from a DNS packet illustrated below: | Consider the DNS packet excerpt illustrated below: | |||
| +----+----+----+----+----+----+----+----+----+----+----+----+ | +----+----+----+----+----+----+----+----+----+----+----+----+ | |||
| +0x00 | ID | FLAGS | QCOUNT | ANCOUNT | NSCOUNT | ARCOUNT | | +0x00 | ID | FLAGS | QDCOUNT | ANCOUNT | NSCOUNT | ARCOUNT | | |||
| +----+----+----+----+----+----+----+----+----+----+----+----+ | +----+----+----+----+----+----+----+----+----+----+----+----+ | |||
| ->+0x0c |0xc0|0x0c| TYPE | CLASS |0x04| t | e | s | t |0x03| | ->+0x0c |0xc0|0x0c| TYPE | CLASS |0x04| t | e | s | t |0x03| | |||
| | +----+--|-+----+----+----+----+----+----+----+----+----+----+ | | +----+--|-+----+----+----+----+----+----+----+----+----+----+ | |||
| | +0x18 | c | o| | m |0x00| TYPE | CLASS | ................ | | | +0x18 | c | o| | m |0x00| TYPE | CLASS | ................ | | |||
| | +----+--|-+----+----+----+----+----+----+----+----+----+----+ | | +----+--|-+----+----+----+----+----+----+----+----+----+----+ | |||
| | | | | | | |||
| ---------------- | ----------------- | |||
| The packet begins with a DNS header at the offset +0x00, and its DNS | The packet begins with a DNS header at offset +0x00, and its DNS | |||
| payload contains several RRs. The first RR begins at the offset of | payload contains several RRs. The first RR begins at an offset of 12 | |||
| 12 octets (+0xc0) and its first label length octet is set to the | octets (+0x0c); its first label length octet is set to the value | |||
| value "0xc0", which indicates that it is a compression pointer. The | "0xc0", which indicates that it is a compression pointer. The | |||
| compression pointer offset is computed from the two octets "0xc00c" | compression pointer offset is computed from the two octets "0xc00c" | |||
| and it is equal to 12. Since the broken implementation at Snippet 1 | and is equal to 12. Since the broken implementation in Figure 1 | |||
| follows this offset value blindly, the pointer will jump back to | follows this offset value blindly, the pointer will jump back to the | |||
| the first octet of the first RR (+0xc0) over and over again. The | first octet of the first RR (+0x0c) over and over again. The code in | |||
| code at Snippet 1 will enter an infinite loop state, since it will | Figure 1 will enter an infinite-loop state, since it will never leave | |||
| never leave the "TRUE" branch of the "while" loop. | the "TRUE" branch of the "while" loop. | |||
| Apart from achieving infinite loops, the implementation flaws at | Apart from achieving infinite loops, the implementation flaws in | |||
| Snippet 1 make it possible to achieve various pointer loops that have | Figure 1 make it possible to achieve various pointer loops that have | |||
| other effects. For instance, consider the DNS packet excerpt shown | other undesirable effects. For instance, consider the DNS packet | |||
| below: | excerpt shown below: | |||
| +----+----+----+----+----+----+----+----+----+----+----+----+ | +----+----+----+----+----+----+----+----+----+----+----+----+ | |||
| +0x00 | ID | FLAGS | QCOUNT | ANCOUNT | NSCOUNT | ARCOUNT | | +0x00 | ID | FLAGS | QDCOUNT | ANCOUNT | NSCOUNT | ARCOUNT | | |||
| +----+----+----+----+----+----+----+----+----+----+----+----+ | +----+----+----+----+----+----+----+----+----+----+----+----+ | |||
| ->+0x0c |0x04| t | e | s | t |0xc0|0x0c| ...................... | | ->+0x0c |0x04| t | e | s | t |0xc0|0x0c| ...................... | | |||
| | +----+----+----+----+----+----+--|-+----+----+----+----+----+ | | +----+----+----+----+----+----+--|-+----+----+----+----+----+ | |||
| | | | | | | |||
| ----------------------------------------- | ------------------------------------------ | |||
| With such a domain name, the implementation at Snippet 1 will first | With such a domain name, the implementation in Figure 1 will first | |||
| copy the domain label at the offset "0xc0" ("test"), then it will | copy the domain label at offset "0xc0" ("test"); it will then fetch | |||
| fetch the next label length octet, which is a compression pointer | the next label length octet, which happens to be a compression | |||
| ("0xc0"). The compression pointer offset is computed from the two | pointer ("0xc0"). The compression pointer offset is computed from | |||
| octets "0xc00c" and is equal to 12 octets. The code will jump back | the two octets "0xc00c" and is equal to 12 octets. The code will | |||
| at the offset "0xc0" where the first label "test" is located. The | jump back to offset "0xc0" where the first label "test" is located. | |||
| code will again copy the "test" label, and jump back to it, | The code will again copy the "test" label and then jump back to it, | |||
| following the compression pointer, over and over again. | following the compression pointer, over and over again. | |||
| Snippet 1 does not contain any logic that restricts multiple jumps | Figure 1 does not contain any logic that restricts multiple jumps | |||
| from the same compression pointer and does not ensure that no more | from the same compression pointer and does not ensure that no more | |||
| than 255 octets are copied into the name buffer ("name_buffer"). In | than 255 octets are copied into the name buffer ("name_buffer"). In | |||
| fact, the code will continue to write the label "test" into it, | fact, | |||
| overwriting the name buffer and the stack of the heap metadata. In | ||||
| fact, attackers would have a significant degree of freedom in | ||||
| constructing shell-code, since they can create arbitrary copy chains | ||||
| with various combinations of labels and compression pointers. | ||||
| Therefore, blindly following compression pointers may not only lead | * the code will continue to write the label "test" into it, | |||
| to Denial-of-Service as pointed by [COMP-DRAFT], but also to | overwriting the name buffer and the stack of the heap metadata. | |||
| successful Remote Code Execution attacks, as there may be other | ||||
| implementation issues present within the corresponding code. | ||||
| Some implementations may not follow [RFC1035], which states: "the | * attackers would have a significant degree of freedom in | |||
| first two bits [of a compression pointer octet] are ones; this allows | constructing shell code, since they can create arbitrary copy | |||
| a pointer to be distinguished from a label, the label must begin | chains with various combinations of labels and compression | |||
| with two zero bits because labels are restricted to 63 octets or less | pointers. | |||
| (the 10 and 01 combinations are reserved for future use)". Snippets 2 | ||||
| and 3 show pseudocode that implements two functions that check | Therefore, blindly following compression pointers may lead not only | |||
| whether a given octet is a compression pointer: correct and incorrect | to Denial-of-Service conditions, as pointed out by [DNS-COMPRESSION], | |||
| implementations respectively. | but also to successful Remote Code Execution attacks, as there may be | |||
| other implementation issues present within the corresponding code. | ||||
| Some implementations may not follow [RFC1035], which states: | ||||
| | The first two bits are ones. This allows a pointer to be | ||||
| | distinguished from a label, since the label must begin with two | ||||
| | zero bits because labels are restricted to 63 octets or less. | ||||
| | (The 10 and 01 combinations are reserved for future use.) | ||||
| Figures 2 and 3 show pseudocode that implements two functions that | ||||
| check whether a given octet is a compression pointer; Figure 2 shows | ||||
| a correct implementation, and Figure 3 shows an incorrect (broken) | ||||
| implementation. | ||||
| 1: unsigned char is_compression_pointer(*octet) { | 1: unsigned char is_compression_pointer(*octet) { | |||
| 2: if ((*octet & 0xc0) == 0xc0) | 2: if ((*octet & 0xc0) == 0xc0) | |||
| 3: return true; | 3: return true; | |||
| 4: } else { | 4: } else { | |||
| 5: return false; | 5: return false; | |||
| 6: } | 6: } | |||
| 7: } | 7: } | |||
| Snippet 2 - Correct compression pointer check | ||||
| Figure 2: Correct Compression Pointer Check | ||||
| 1: unsigned char is_compression_pointer(*octet) { | 1: unsigned char is_compression_pointer(*octet) { | |||
| 2: if (*octet & 0xc0) { | 2: if (*octet & 0xc0) { | |||
| 3: return true; | 3: return true; | |||
| 4: } else { | 4: } else { | |||
| 5: return false; | 5: return false; | |||
| 6: } | 6: } | |||
| 7: } | 7: } | |||
| Snippet 3 - Broken compression pointer check | ||||
| The correct implementation (Snippet 2) ensures that the two most | Figure 3: Broken Compression Pointer Check | |||
| The correct implementation (Figure 2) ensures that the two most | ||||
| significant bits of an octet are both set, while the broken | significant bits of an octet are both set, while the broken | |||
| implementation (Snippet 3) would consider an octet with only one of | implementation (Figure 3) would consider an octet with only one of | |||
| the two bits set as a compression pointer. This is likely an | the two bits set to be a compression pointer. This is likely an | |||
| implementation mistake rather than an intended violation of | implementation mistake rather than an intended violation of | |||
| [RFC1035], because there are no benefits in supporting such | [RFC1035], because there are no benefits in supporting such | |||
| compression pointer values. The implementations related to | compression pointer values. The implementations related to | |||
| [CVE-2020-24338] and [CVE-2020-24335] had a broken | [CVE-2020-24338] and [CVE-2020-24335] had a broken compression | |||
| compression pointer check illustrated on Snippet 3. | pointer check, similar to the code shown in Figure 3. | |||
| While incorrect implementations alone do not lead to vulnerabilities, | While incorrect implementations alone do not lead to vulnerabilities, | |||
| they may have unforeseen side-effects when combined with other | they may have unforeseen side effects when combined with other | |||
| vulnerabilities. For instance, the first octet of the value "0x4130" | vulnerabilities. For instance, the first octet of the value "0x4130" | |||
| may be incorrectly interpreted as a label length by a broken | may be incorrectly interpreted as a label length by a broken | |||
| implementation. Such label length (65) is invalid, and is larger | implementation. Such a label length (65) is invalid and is larger | |||
| than 63 (as per [RFC1035]), and a packet that has this value should | than 63 (as per [RFC1035]); a packet that has this value should be | |||
| be discarded. However, the function shown on Snippet 3 will | discarded. However, the function shown in Figure 3 will consider | |||
| consider "0x41" to be a valid compression pointer, and the packet | "0x41" to be a valid compression pointer, and the packet may pass the | |||
| may pass the validation steps. | validation steps. | |||
| This might give an additional leverage for attackers in constructing | This might give attackers additional leverage for constructing | |||
| payloads and circumventing the existing DNS packet validation | payloads and circumventing the existing DNS packet validation | |||
| mechanisms. | mechanisms. | |||
| The first occurrence of a compression pointer in a RR (an octet with | The first occurrence of a compression pointer in an RR (an octet with | |||
| the 2 highest bits set to 1) must resolve to an octet within a DNS | the two highest bits set to 1) must resolve to an octet within a DNS | |||
| record with the value that is greater than 0 (i.e., it must not be a | record with a value that is greater than 0 (i.e., it must not be a | |||
| Null-terminator) and less than 64. The offset at which this octet is | Null-terminator) and less than 64. The offset at which this octet is | |||
| located must be smaller than the offset at which the compression | located must be smaller than the offset at which the compression | |||
| pointer is located - once an implementation makes sure of that, | pointer is located; once an implementation makes sure of that, | |||
| compression pointer loops can never occur. | compression pointer loops can never occur. | |||
| In small DNS implementations (e.g., embedded TCP/IP stacks) the | In small DNS implementations (e.g., embedded TCP/IP stacks), support | |||
| support for nested compression pointers (pointers that point to a | for nested compression pointers (pointers that point to a compressed | |||
| compressed name) should be discouraged: there is very little to be | name) should be discouraged: there is very little to be gained in | |||
| gained in terms of performance versus the high possibility of | terms of performance versus the high probability of introducing | |||
| introducing errors, such as the ones discussed above. | errors such as those discussed above. | |||
| The code that implements domain name parsing should check the offset | The code that implements domain name parsing should check the offset | |||
| not only with respect to the bounds of a packet, but also its | with respect to not only the bounds of a packet but also its position | |||
| position with respect to the compression pointer in question. A | with respect to the compression pointer in question. A compression | |||
| compression pointer must not be "followed" more than once. We have | pointer must not be "followed" more than once. We have seen several | |||
| seen several implementations using a check that ensures that | implementations using a check that ensures that a compression pointer | |||
| a compression pointer is not followed more than several times. A | is not followed more than several times. A better alternative may be | |||
| better alternative may be to ensure that the target of a compression | to ensure that the target of a compression pointer is always located | |||
| pointer is always located before the location of the pointer in the | before the location of the pointer in the packet. | |||
| packet. | ||||
| 3. Label and Name Length Validation | 3. Label and Name Length Validation | |||
| [RFC1035] restricts the length of name labels to 63 octets, and | [RFC1035] restricts the length of name labels to 63 octets and | |||
| lengths of domain names to 255 octets (i.e., label octets and label | lengths of domain names to 255 octets (i.e., label octets and label | |||
| length octets). Some implementations do not explicitly enforce these | length octets). Some implementations do not explicitly enforce these | |||
| restrictions. | restrictions. | |||
| Consider the function "copy_domain_name()" shown on Snippet 4 below. | Consider the function "copy_domain_name()" shown in Figure 4 below. | |||
| The function is a variant of the "decompress_domain_name()" function | The function is a variant of the "decompress_domain_name()" function | |||
| (Snippet 1), with the difference that it does not support compressed | (Figure 1), with the difference that it does not support compressed | |||
| labels, and copies only decompressed labels into the name buffer. | labels and only copies decompressed labels into the name buffer. | |||
| 1:copy_domain_name(*name, *dns_payload) { | 1: copy_domain_name(*name, *dns_payload) { | |||
| 2: | 2: | |||
| 3: name_buffer[255]; | 3: name_buffer[255]; | |||
| 4: copy_offset = 0; | 4: copy_offset = 0; | |||
| 5: | 5: | |||
| 6: label_len_octet = name; | 6: label_len_octet = name; | |||
| 7: dest_octet = name_buffer; | 7: dest_octet = name_buffer; | |||
| 8: | 8: | |||
| 9: while (*label_len_octet != 0x00) { | 9: while (*label_len_octet != 0x00) { | |||
| 10: | 10: | |||
| 11: if (is_compression_pointer(*label_len_octet)) { | 11: if (is_compression_pointer(*label_len_octet)) { | |||
| 12: length = 2; | 12: length = 2; | |||
| 13: label_len_octet += length + 1; | 13: label_len_octet += length + 1; | |||
| 14: } | 14: } | |||
| 15: | 15: | |||
| 16: else { | 16: else { | |||
| 17: length = *label_len_octet; | 17: length = *label_len_octet; | |||
| 18: copy(dest_octet + copy_offset, | 18: copy(dest_octet + copy_offset, | |||
| label_len_octet+1, *length); | label_len_octet+1, *length); | |||
| 19: | 19: | |||
| 20: copy_offset += length; | 20: copy_offset += length; | |||
| 21: label_len_octet += length + 1; | 21: label_len_octet += length + 1; | |||
| 22: } | 22: } | |||
| 23: | 23: | |||
| 24: } | 24: } | |||
| 25:} | 25: } | |||
| Snippet 4 - A broken implementation of a function | ||||
| that is used for copying non-compressed domain names | Figure 4: A Broken Implementation of a Function That Is Used for | |||
| Copying Non-compressed Domain Names | ||||
| This implementation does not explicitly check for the value of the | This implementation does not explicitly check for the value of the | |||
| label length octet: this value can be up to 255 octets, and a single | label length octet: this value can be up to 255 octets, and a single | |||
| label can fill the name buffer. Depending on the memory layout of the | label can fill the name buffer. Depending on the memory layout of | |||
| target, how the name buffer is allocated, and the size of the | the target, how the name buffer is allocated, and the size of the | |||
| malformed packet, it is possible to trigger various memory corruption | malformed packet, it is possible to trigger various memory corruption | |||
| issues. | issues. | |||
| Both Snippets 1 and 4 restrict the size of the name buffer to 255 | Both Figures 1 and 4 restrict the size of the name buffer to 255 | |||
| octets, however there are no restrictions on the actual number of | octets; however, there are no restrictions on the actual number of | |||
| octets that will be copied into this buffer. In this particular case, | octets that will be copied into this buffer. In this particular | |||
| a subsequent copy operation (if another label is present in the | case, a subsequent copy operation (if another label is present in the | |||
| packet) will write past the name buffer, allowing to overwrite heap | packet) will write past the name buffer, allowing heap or stack | |||
| or stack metadata in a controlled manner. | metadata to be overwritten in a controlled manner. | |||
| Similar examples of vulnerable implementations can be found in the | Similar examples of vulnerable implementations can be found in the | |||
| code relevant to [CVE-2020-25110], [CVE-2020-15795], and | code relevant to [CVE-2020-25110], [CVE-2020-15795], and | |||
| [CVE-2020-27009]. | [CVE-2020-27009]. | |||
| As a general recommendation, a domain label length octet must have | As a general recommendation, a domain label length octet must have a | |||
| the value of more than 0 and less than 64 ([RFC1035]). If this is | value of more than 0 and less than 64 [RFC1035]. If this is not the | |||
| not the case, an invalid value has been provided within the packet, | case, an invalid value has been provided within the packet, or a | |||
| or a value at an invalid position might be interpreted as a domain | value at an invalid position might be interpreted as a domain name | |||
| name length due to other errors in the packet (e.g., misplaced Null- | length due to other errors in the packet (e.g., misplaced Null- | |||
| terminator or invalid compression pointer). | terminator or invalid compression pointer). | |||
| The number of domain label characters must correspond to the value of | The number of domain label characters must correspond to the value of | |||
| the domain label octet. To avoid possible errors when interpreting | the domain label octet. To avoid possible errors when interpreting | |||
| the characters of a domain label, developers may consider | the characters of a domain label, developers may consider | |||
| recommendations for the preferred domain name syntax outlined in | recommendations for the preferred domain name syntax outlined in | |||
| [RFC1035]. | [RFC1035]. | |||
| The domain name length must not be more than 255 octets, including | The domain name length must not be more than 255 octets, including | |||
| the size of decompressed domain names. The NUL octet ("0x00") must | the size of decompressed domain names. The NUL octet ("0x00") must | |||
| be present at the end of the domain name, and within the maximum name | be present at the end of the domain name and must be within the | |||
| length (255 octets). | maximum name length (255 octets). | |||
| 4. Null-terminator Placement Validation | 4. Null-Terminator Placement Validation | |||
| A domain name must end with a NUL ("0x00") octet, as per [RFC1035]. | A domain name must end with a NUL ("0x00") octet, as per [RFC1035]. | |||
| The implementations shown at Snippets 1 and 4 assume that this is the | The implementations shown in Figures 1 and 4 assume that this is the | |||
| case for the RRs that they process, however names that do not have a | case for the RRs that they process; however, names that do not have a | |||
| NUL octet placed at the proper position within a RR are not | NUL octet placed at the proper position within an RR are not | |||
| discarded. | discarded. | |||
| This issue is closely related to the absence of label and name length | This issue is closely related to the absence of label and name length | |||
| checks. For example, the logic behind Snippets 1 and 4 will continue | checks. For example, the logic behind Figures 1 and 4 will continue | |||
| to copy octets into the name buffer, until a NUL octet is | to copy octets into the name buffer until a NUL octet is encountered. | |||
| encountered. This octet can be placed at an arbitrary position | This octet can be placed at an arbitrary position within an RR or not | |||
| within a RR, or not placed at all. | placed at all. | |||
| Consider a pseudocode function shown on Snippet 5. The function | Consider the pseudocode function shown in Figure 5. The function | |||
| returns the length of a domain name ("name") in octets to be used | returns the length of a domain name ("name") in octets to be used | |||
| elsewhere (e.g., to allocate a name buffer of a certain size): for | elsewhere (e.g., to allocate a name buffer of a certain size): for | |||
| compressed domain names the function returns 2, for decompressed | compressed domain names, the function returns 2; for decompressed | |||
| names it returns their true length using the "strlen(3)" function. | names, it returns their true length using the "strlen(3)" function. | |||
| 1: get_name_length(*name) { | 1: get_name_length(*name) { | |||
| 2: | 2: | |||
| 3: if (is_compression_pointer(name)) | 3: if (is_compression_pointer(name)) | |||
| 4: return 2; | 4: return 2; | |||
| 5: | 5: | |||
| 6: name_len = strlen(name) + 1; | 6: name_len = strlen(name) + 1; | |||
| 7: return name_len; | 7: return name_len; | |||
| 8: } | 8: } | |||
| Snippet 5 - A broken implementation of a function that returns the | ||||
| length of a domain name | Figure 5: A Broken Implementation of a Function That Returns the | |||
| Length of a Domain Name | ||||
| "strlen(3)" is a standard C library function that returns the length | "strlen(3)" is a standard C library function that returns the length | |||
| of a given sequence of characters terminated by the NUL ("0x00") | of a given sequence of characters terminated by the NUL ("0x00") | |||
| octet. Since this function also expects names to be explicitly | octet. Since this function also expects names to be explicitly Null- | |||
| Null-terminated, the return value "strlen(3)" may be also controlled | terminated, the return value "strlen(3)" may also be controlled by | |||
| by attackers. Through the value of "name_len" attackers may control | attackers. Through the value of "name_len", attackers may control | |||
| the allocation of internal buffers, or specify the number by octets | the allocation of internal buffers or specify the number by octets | |||
| copied into these buffers, or other operations depending on the | copied into these buffers, or they may perform other operations, | |||
| implementation specifics. | depending on the implementation specifics. | |||
| The absence of explicit checks for the NUL octet placement may also | The absence of explicit checks for placement of the NUL octet may | |||
| facilitate controlled memory reads and writes. An example of | also facilitate controlled memory reads and writes. An example of | |||
| vulnerable implementations can be found in the code relevant to | vulnerable implementations can be found in the code relevant to | |||
| [CVE-2020-25107], [CVE-2020-17440], [CVE-2020-24383], and | [CVE-2020-25107], [CVE-2020-17440], [CVE-2020-24383], and | |||
| [CVE-2020-27736]. | [CVE-2020-27736]. | |||
| As a general recommendation for mitigating such issues, developers | As a general recommendation for mitigating such issues, developers | |||
| should never trust user data to be Null-terminated. For example, to | should never trust user data to be Null-terminated. For example, to | |||
| fix/mitigate the issue in the code Snippet 5, developers should use | fix/mitigate the issue shown in the code in Figure 5, developers | |||
| the function "strnlen(3)" that reads at most X characters(the second | should use the function "strnlen(3)", which reads at most X | |||
| argument of the function), and ensure that X is not larger than the | characters (the second argument of the function), and ensure that X | |||
| buffer allocated for the name. | is not larger than the buffer allocated for the name. | |||
| 5. Response Data Length Validation | 5. Response Data Length Validation | |||
| As stated in [RFC1035], every RR contains a variable length string of | As stated in [RFC1035], every RR contains a variable-length string of | |||
| octets that contains the retrieved resource data (RDATA) (e.g., an IP | octets that contains the retrieved resource data (RDATA) (e.g., an IP | |||
| address that corresponds to a domain name in question). The length of | address that corresponds to a domain name in question). The length | |||
| the RDATA field is regulated by the resource data length field | of the RDATA field is regulated by the resource data length field | |||
| (RDLENGTH), that is also present in an RR. | (RDLENGTH), which is also present in an RR. | |||
| Implementations that process RRs may not check for the validity of | Implementations that process RRs may not check for the validity of | |||
| the RDLENGTH field value, when retrieving RDATA. Failing to do so may | the RDLENGTH field value when retrieving RDATA. Failing to do so may | |||
| lead to out-of-bound read issues (similarly to the label and name | lead to out-of-bound read issues, whose impact may vary | |||
| length validation issues discussed in Section 3), whose impact may | significantly, depending on the implementation specifics. We have | |||
| vary significantly depending on the implementation specifics. We have | ||||
| observed instances of Denial-of-Service conditions and information | observed instances of Denial-of-Service conditions and information | |||
| leaks. | leaks. | |||
| Therefore, the value of the data length byte in response DNS records | Therefore, the value of the data length byte in response DNS records | |||
| (RDLENGTH) must reflect the number of bytes available in the field | (RDLENGTH) must reflect the number of bytes available in the field | |||
| that describes the resource (RDATA). The format of RDATA must | that describes the resource (RDATA). The format of RDATA must | |||
| conform to the TYPE and CLASS fields of the RR. | conform to the TYPE and CLASS fields of the RR. | |||
| Examples of vulnerable implementations can be found in the code | Examples of vulnerable implementations can be found in the code | |||
| relevant to [CVE-2020-25108], [CVE-2020-24336], and [CVE-2020-27009]. | relevant to [CVE-2020-25108], [CVE-2020-24336], and [CVE-2020-27009]. | |||
| 6. Record Count Validation | 6. Record Count Validation | |||
| According to [RFC1035], the DNS header contains four two-octet | According to [RFC1035], the DNS header contains four two-octet fields | |||
| fields that specify the amount of question records (QDCOUNT), answer | that specify the amount of question records (QDCOUNT), answer records | |||
| records (ANCOUNT), authority records (NSCOUNT), and additional | (ANCOUNT), authority records (NSCOUNT), and additional records | |||
| records (ARCOUNT). | (ARCOUNT). | |||
| Figure 6 illustrates a recurring implementation anti-pattern for a | ||||
| function that processes DNS RRs. The function | ||||
| "process_dns_records()" extracts the value of ANCOUNT ("num_answers") | ||||
| and the pointer to the DNS data payload ("data_ptr"). The function | ||||
| processes answer records in a loop, decrementing the "num_answers" | ||||
| value after processing each record until the value of "num_answers" | ||||
| becomes zero. For simplicity, we assume that there is only one | ||||
| domain name per answer. Inside the loop, the code calculates the | ||||
| domain name length ("name_length") and adjusts the data payload | ||||
| pointer ("data_ptr") by the offset that corresponds to "name_length + | ||||
| 1", so that the pointer lands on the first answer record. Next, the | ||||
| answer record is retrieved and processed, and the "num_answers" value | ||||
| is decremented. | ||||
| 1: process_dns_records(dns_header, ...) { | 1: process_dns_records(dns_header, ...) { | |||
| // ... | // ... | |||
| 2: num_answers = dns_header->ancount | 2: num_answers = dns_header->ancount | |||
| 3: data_ptr = dns_header->data | 3: data_ptr = dns_header->data | |||
| 4: | 4: | |||
| 5: while (num_answers > 0) { | 5: while (num_answers > 0) { | |||
| 6: name_length = get_name_length(data_ptr); | 6: name_length = get_name_length(data_ptr); | |||
| 7: data_ptr += name_length + 1; | 7: data_ptr += name_length + 1; | |||
| 8: | 8: | |||
| 9: answer = (struct dns_answer_record *)data_ptr; | 9: answer = (struct dns_answer_record *)data_ptr; | |||
| 10: | 10: | |||
| 11: // process the answer record | 11: // process the answer record | |||
| 12: | 12: | |||
| 13: --num_answers; | 13: --num_answers; | |||
| 14: } | 14: } | |||
| // ... | // ... | |||
| 15: } | 15: } | |||
| Snippet 6 - A broken implementation of a RR processing function | ||||
| Snippet 6 illustrates a recurring implementation anti-pattern for a | Figure 6: A Broken Implementation of a Function That Processes RRs | |||
| function that processes DNS RRs. The function "process_dns_records()" | ||||
| extracts the value of ANCOUNT ("num_answers") and the pointer to the | ||||
| DNS data payload ("data_ptr"). The function processes answer records | ||||
| in a loop decrementing the "num_answers" value after processing each | ||||
| record, until the value of "num_answers" becomes zero. For | ||||
| simplicity, we assume that there is only one domain name per answer. | ||||
| Inside the loop, the code calculates the domain name length | ||||
| "name_length", and adjusts the data payload pointer "data_ptr" by the | ||||
| offset that corresponds to "name_length + 1", so that the pointer | ||||
| lands on the first answer record. Next, the answer record is | ||||
| retrieved and processed, and the "num_answers" value is decremented. | ||||
| If the ANCOUNT number retrieved from the header | If the ANCOUNT number retrieved from the header | |||
| ("dns_header->ancount") is not checked against the amount of data | ("dns_header->ancount") is not checked against the amount of data | |||
| available in the packet and it is, e.g., larger than the number of | available in the packet and it is, for example, larger than the | |||
| answer records available, the data pointer "data_ptr" will read out | number of answer records available, the data pointer ("data_ptr") | |||
| of the bounds of the packet. This may result in Denial-of-Service | will read outside the bounds of the packet. This may result in | |||
| conditions. | Denial-of-Service conditions. | |||
| In this section, we used an example of processing answer records. | In this section, we used an example of processing answer records. | |||
| However, the same logic is often reused for implementing the | However, the same logic is often reused for implementing the | |||
| processing of other types of records: e.g., the number of Question | processing of other types of records, e.g., the number of question | |||
| (QCOUNT), Authority (NSCOUNT), and Additional (ARCOUNT) records. The | (QDCOUNT), authority (NSCOUNT), and additional (ARCOUNT) records. | |||
| number of these records specified must correspond to the actual data | The specified numbers of these records must correspond to the actual | |||
| present within the packet. Therefore, all record count fields must | data present within the packet. Therefore, all record count fields | |||
| be checked before fully parsing the contents of a packet. | must be checked before fully parsing the contents of a packet. | |||
| Specifically, Section 6.3 of[RFC5625] recommends that such malformed | Specifically, Section 6.3 of [RFC5625] recommends that such malformed | |||
| DNS packets should be dropped, and (optionally) logged. | DNS packets should be dropped and (optionally) logged. | |||
| Examples of vulnerable implementations can be found in the code | Examples of vulnerable implementations can be found in the code | |||
| relevant to [CVE-2020-25109], [CVE-2020-24340],[CVE-2020-24334], and | relevant to [CVE-2020-25109], [CVE-2020-24340], [CVE-2020-24334], and | |||
| [CVE-2020-27737]. | [CVE-2020-27737]. | |||
| 7. Security Considerations | 7. Security Considerations | |||
| Security issues are discussed throughout this memo. The document | Security issues are discussed throughout this memo; it discusses | |||
| discusses implementation flaws (anti-patterns) that affect the | implementation flaws (anti-patterns) that affect the functionality of | |||
| functionality of processing DNS RRs. The presence of such | processing DNS RRs. The presence of such anti-patterns leads to bugs | |||
| anti-patterns leads to bugs causing buffer overflows, | that cause buffer overflows, read-out-of-bounds, and infinite-loop | |||
| read-out-of-bounds, and infinite loop issues. These issues have the | issues. These issues have the following security impacts: | |||
| following security impact: Information Leak, Denial-of-Service, and | information leaks, Denial-of-Service attacks, and Remote Code | |||
| Remote Code Execution. | Execution attacks. | |||
| The document lists general recommendation for the developers of DNS | This document lists general recommendations for the developers of DNS | |||
| record parsing functionality that allow to prevent such | record parsing functionality that allow those developers to prevent | |||
| implementation flaws, e.g., by rigorously checking the data received | such implementation flaws, e.g., by rigorously checking the data | |||
| over the wire before processing it. | received over the wire before processing it. | |||
| 8. IANA Considerations | 8. IANA Considerations | |||
| This document introduces no new IANA considerations. Please see | This document has no IANA actions. Please see [RFC6895] for a | |||
| [RFC6895] for a complete review of the IANA considerations | complete review of the IANA considerations introduced by DNS. | |||
| introduced by DNS. | ||||
| 9. References | 9. References | |||
| 9.1 Normative References | 9.1. Normative References | |||
| [RFC1035] | [RFC1035] Mockapetris, P., "Domain names - implementation and | |||
| Mockapetris, P., "Domain names - implementation and | specification", STD 13, RFC 1035, DOI 10.17487/RFC1035, | |||
| specification", RFC 1035, November 1987, | November 1987, <https://www.rfc-editor.org/info/rfc1035>. | |||
| <https://www.rfc-editor.org/info/rfc1035>. | ||||
| [RFC5625] | [RFC5625] Bellis, R., "DNS Proxy Implementation Guidelines", | |||
| Bellis, R., "DNS Proxy Implementation Guidelines", RFC | BCP 152, RFC 5625, DOI 10.17487/RFC5625, August 2009, | |||
| 5625, August 2009, | <https://www.rfc-editor.org/info/rfc5625>. | |||
| <https://www.rfc-editor.org/info/rfc5625>. | ||||
| 9.2 Informative References | 9.2. Informative References | |||
| [SIGRED] | [CVE-2000-0333] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-1350: | Common Vulnerabilities and Exposures, "CVE-2000-0333: A | |||
| A remote code execution vulnerability in Windows Domain | denial-of-service vulnerability in tcpdump, Ethereal, and | |||
| Name System servers", July 2020, <https://cve.mitre.org/ | other sniffer packages via malformed DNS packets", 2000, | |||
| cgi-bin/cvename.cgi?name=CVE-2020-1350>. | <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| 2000-0333>. | ||||
| [SADDNS] | [CVE-2017-9345] | |||
| Man, K., Qian, Z., Wang, Z., Zheng, X., Huang, Y., Duan, | Common Vulnerabilities and Exposures, "CVE-2017-9345: An | |||
| H., "DNS Cache Poisoning Attack Reloaded: Revolutions | infinite loop in the DNS dissector of Wireshark", 2017, | |||
| with Side Channels", November 2020, Proc. of ACM CCS'20, | <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| <https://dl.acm.org/doi/pdf/10.1145/3372297.3417280>. | 2017-9345>. | |||
| [DNSPOOQ] | [CVE-2020-15795] | |||
| Kol, M., Oberman, S., "DNSpooq: Cache Poisoning and RCE | Common Vulnerabilities and Exposures, "CVE-2020-15795: A | |||
| in popular DNS Forwarder dnsmasq", January 2021, technical | denial-of-service and remote code execution vulnerability | |||
| report, <https://www.jsof-tech.com/wp-content/uploads/ | DNS domain name label parsing functionality of Nucleus | |||
| 2021/01/DNSpooq-Technical-WP.pdf>. | NET", 2021, <https://cve.mitre.org/cgi-bin/ | |||
| cvename.cgi?name=CVE-2020-15795>. | ||||
| [CVE-2000-0333] | [CVE-2020-17440] | |||
| Common Vulnerabilities and Exposures, "CVE-2000-0333: | Common Vulnerabilities and Exposures, "CVE-2020-17440 A | |||
| A denial-of-service vulnerability in tcpdump, Ethereal, | denial-of-service vulnerability in the DNS name parsing | |||
| and other sniffer packages via malformed DNS packets", | implementation of uIP", 2020, <https://cve.mitre.org/cgi- | |||
| 2000, <https://cve.mitre.org/cgi-bin/cvename.cgi?name= | bin/cvename.cgi?name=CVE-2020-17440>. | |||
| CVE-2000-0333>. | ||||
| [CVE-2020-24338] | [CVE-2020-24334] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-24338: | Common Vulnerabilities and Exposures, "CVE-2020-24334: An | |||
| A denial-of-service and remote code execution | out-of-bounds read and denial-of-service vulnerability in | |||
| vulnerability in the DNS domain name record | the DNS response parsing functionality of uIP", 2020, | |||
| decompression functionality of picoTCP", December 2020, | <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| <https://cve.mitre.org/cgi-bin/cvename.cgi?name= | 2020-24334>. | |||
| CVE-2020-24338> | ||||
| [CVE-2020-27738] | [CVE-2020-24335] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-27738: | Common Vulnerabilities and Exposures, "CVE-2020-24335: A | |||
| A denial-of-service and remote code execution | memory corruption vulnerability in domain name parsing | |||
| vulnerability DNS domain name record decompression | routines of uIP", 2020, <https://cve.mitre.org/cgi-bin/ | |||
| functionality of Nucleus NET", April 2021, | cvename.cgi?name=CVE-2020-24335>. | |||
| <https://cve.mitre.org/cgi-bin/cvename.cgi?name= | ||||
| CVE-2020-27738>. | ||||
| [CVE-2020-25767] | [CVE-2020-24336] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-25767: | Common Vulnerabilities and Exposures, "CVE-2020-24336: A | |||
| An out-of-bounds read and denial-of-service vulnerability | buffer overflow vulnerability in the DNS implementation of | |||
| in the DNS name parsing routine of HCC Embedded | Contiki and Contiki-NG", 2020, <https://cve.mitre.org/cgi- | |||
| NicheStack", August 2021, <https://cve.mitre.org/ | bin/cvename.cgi?name=CVE-2020-24336>. | |||
| cgi-bin/cvename.cgi?name=CVE-2020-25767>. | ||||
| [CVE-2020-24339] | [CVE-2020-24338] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-24339: | Common Vulnerabilities and Exposures, "CVE-2020-24338: A | |||
| An out-of-bounds read and denial-of-service | denial-of-service and remote code execution vulnerability | |||
| vulnerability in the DNS domain name record | in the DNS domain name record decompression functionality | |||
| decompression functionality of picoTCP", December 2020, | of picoTCP", 2020, <https://cve.mitre.org/cgi-bin/ | |||
| https://cve.mitre.org/cgi-bin/cvename.cgi?name= | cvename.cgi?name=CVE-2020-24338>. | |||
| CVE-2020-24339>. | ||||
| [CVE-2020-24335] | [CVE-2020-24339] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-24335: | Common Vulnerabilities and Exposures, "CVE-2020-24339: An | |||
| A memory corruption vulnerability in domain name parsing | out-of-bounds read and denial-of-service vulnerability in | |||
| routines of uIP", December 2020, <https://cve.mitre.org/ | the DNS domain name record decompression functionality of | |||
| cgi-bin/cvename.cgi?name=CVE-2020-24335>. | picoTCP", 2020, <https://cve.mitre.org/cgi-bin/ | |||
| cvename.cgi?name=CVE-2020-24339>. | ||||
| [CVE-2020-25110] | [CVE-2020-24340] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-25110: | Common Vulnerabilities and Exposures, "CVE-2020-24340: An | |||
| A denial-of-service and remote code execution | out-of-bounds read and denial-of-service vulnerability in | |||
| vulnerability in the DNS implementation of Ethernut | the DNS response parsing functionality of picoTCP", 2020, | |||
| Nut/OS", December 2020, <https://cve.mitre.org/cgi-bin/ | <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| cvename.cgi?name=CVE-2020-25110>. | 2020-24340>. | |||
| [CVE-2020-15795] | [CVE-2020-24383] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-15795: | Common Vulnerabilities and Exposures, "CVE-2020-24383: An | |||
| A denial-of-service and remote code execution | information leak and denial-of-service vulnerability while | |||
| vulnerability DNS domain name label parsing | parsing mDNS resource records in FNET", 2020, | |||
| functionality of Nucleus NET", April 2021, | <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| <https://cve.mitre.org/cgi-bin/cvename.cgi?name= | 2020-24383>. | |||
| CVE-2020-15795>. | ||||
| [CVE-2020-27009] | [CVE-2020-25107] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-27009: | Common Vulnerabilities and Exposures, "CVE-2020-25107: A | |||
| A denial-of-service and remote code execution | denial-of-service and remote code execution vulnerability | |||
| vulnerability DNS domain name record decompression | in the DNS implementation of Ethernut Nut/OS", 2020, | |||
| functionality of Nucleus NET", April 2021, | <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| <https://cve.mitre.org/cgi-bin/cvename.cgi?name= | 2020-25107>. | |||
| CVE-2020-27009>. | ||||
| [CVE-2020-25107] | [CVE-2020-25108] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-25107: | Common Vulnerabilities and Exposures, "CVE-2020-25108: A | |||
| A denial-of-service and remote code execution | denial-of-service and remote code execution vulnerability | |||
| vulnerability in the DNS implementation of Ethernut | in the DNS implementation of Ethernut Nut/OS", 2020, | |||
| Nut/OS", December 2020, <https://cve.mitre.org/cgi-bin/ | <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| cvename.cgi?name=CVE-2020-25107>. | 2020-25108>. | |||
| [CVE-2020-17440] | [CVE-2020-25109] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-17440 | Common Vulnerabilities and Exposures, "CVE-2020-25109: A | |||
| A denial-of-service vulnerability in the DNS name | denial-of-service and remote code execution vulnerability | |||
| parsing implementation of uIP", December 2020, | in the DNS implementation of Ethernut Nut/OS", 2020, | |||
| <https://cve.mitre.org/cgi-bin/cvename.cgi?name= | <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| CVE-2020-17440>. | 2020-25109>. | |||
| [CVE-2020-24383] | [CVE-2020-25110] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-24383: | Common Vulnerabilities and Exposures, "CVE-2020-25110: A | |||
| An information leak and denial-of-service vulnerability | denial-of-service and remote code execution vulnerability | |||
| while parsing mDNS resource records in FNET", December | in the DNS implementation of Ethernut Nut/OS", 2020, | |||
| 2020, <https://cve.mitre.org/cgi-bin/cvename.cgi?name= | <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| CVE-2020-24383>. | 2020-25110>. | |||
| [CVE-2020-27736] | [CVE-2020-25767] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-27736: | Common Vulnerabilities and Exposures, "CVE-2020-25767: An | |||
| An information leak and denial-of-service vulnerability | out-of-bounds read and denial-of-service vulnerability in | |||
| in the DNS name parsing functionality of Nucleus NET", | the DNS name parsing routine of HCC Embedded NicheStack", | |||
| April 2021, <https://cve.mitre.org/cgi-bin/cvename.cgi? | 2021, <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| name=CVE-2020-27736>. | 2020-25767>. | |||
| [CVE-2020-25108] | [CVE-2020-27009] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-25108: | Common Vulnerabilities and Exposures, "CVE-2020-27009: A | |||
| A denial-of-service and remote code execution | denial-of-service and remote code execution vulnerability | |||
| vulnerability in the DNS implementation of Ethernut | DNS domain name record decompression functionality of | |||
| Nut/OS", December 2020, <https://cve.mitre.org/cgi-bin/ | Nucleus NET", 2021, <https://cve.mitre.org/cgi-bin/ | |||
| cvename.cgi?name=CVE-2020-25108>. | cvename.cgi?name=CVE-2020-27009>. | |||
| [CVE-2020-24336] | [CVE-2020-27736] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-24336: | Common Vulnerabilities and Exposures, "CVE-2020-27736: An | |||
| A buffer overflow vulnerability in the DNS | information leak and denial-of-service vulnerability in | |||
| implementation of Contiki and Contiki-NG", December | the DNS name parsing functionality of Nucleus NET", 2021, | |||
| 2020, <https://cve.mitre.org/cgi-bin/cvename.cgi?name= | <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| CVE-2020-24336>. | 2020-27736>. | |||
| [CVE-2020-25109] | [CVE-2020-27737] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-25109: | Common Vulnerabilities and Exposures, "CVE-2020-27737: An | |||
| A denial-of-service and remote code execution | information leak and denial-of-service vulnerability in | |||
| vulnerability in the DNS implementation of Ethernut | the DNS response parsing functionality of Nucleus NET", | |||
| Nut/OS", December 2020, <https://cve.mitre.org/cgi-bin/ | 2021, <https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE- | |||
| cvename.cgi?name=CVE-2020-25109>. | 2020-27737>. | |||
| [CVE-2020-24340] | [CVE-2020-27738] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-24340: | Common Vulnerabilities and Exposures, "CVE-2020-27738: A | |||
| An out-of-bounds read and denial-of-service | denial-of-service and remote code execution vulnerability | |||
| vulnerability in the DNS response parsing functionality | DNS domain name record decompression functionality of | |||
| of picoTCP", December 2020, <https://cve.mitre.org/ | Nucleus NET", 2021, <https://cve.mitre.org/cgi-bin/ | |||
| cgi-bin/cvename.cgi?name=CVE-2020-24340>. | cvename.cgi?name=CVE-2020-27738>. | |||
| [CVE-2020-24334] | [DNS-COMPRESSION] | |||
| Common Vulnerabilities and Exposures, "CVE-2020-24334: | Koch, P., "A New Scheme for the Compression of Domain | |||
| An out-of-bounds read and denial-of-service | Names", Work in Progress, Internet-Draft, draft-ietf- | |||
| vulnerability in the DNS response parsing functionality | dnsind-local-compression-05, 30 June 1999, | |||
| of uIP", December 2020, <https://cve.mitre.org/cgi-bin/ | <https://datatracker.ietf.org/doc/html/draft-ietf-dnsind- | |||
| cvename.cgi?name=CVE-2020-24334>. | local-compression-05>. | |||
| [CVE-2020-27737] | [DNSPOOQ] Kol, M. and S. Oberman, "DNSpooq: Cache Poisoning and RCE | |||
| Common Vulnerabilities and Exposures, "CVE-2020-27737: | in Popular DNS Forwarder dnsmasq", JSOF Technical Report, | |||
| An information leak and denial-of-service vulnerability | January 2021, <https://www.jsof-tech.com/wp- | |||
| in the DNS response parsing functionality of Nucleus | content/uploads/2021/01/DNSpooq-Technical-WP.pdf>. | |||
| NET", April 2021, <https://cve.mitre.org/cgi-bin/ | ||||
| cvename.cgi?name=CVE-2020-27737>. | ||||
| [CVE-2017-9345] | [RFC6895] Eastlake 3rd, D., "Domain Name System (DNS) IANA | |||
| Common Vulnerabilities and Exposures, "CVE-2017-9345: | Considerations", BCP 42, RFC 6895, DOI 10.17487/RFC6895, | |||
| An infinite loop in the DNS dissector of Wireshark", | April 2013, <https://www.rfc-editor.org/info/rfc6895>. | |||
| 2017, <https://cve.mitre.org/cgi-bin/cvename.cgi?name= | ||||
| CVE-2017-9345>. | ||||
| [COMP-DRAFT] | [RFC7858] Hu, Z., Zhu, L., Heidemann, J., Mankin, A., Wessels, D., | |||
| Koch, P., "A New Scheme for the Compression of | and P. Hoffman, "Specification for DNS over Transport | |||
| Domain Names", Internet-Draft, draft-ietf-dnsind-local- | Layer Security (TLS)", RFC 7858, DOI 10.17487/RFC7858, May | |||
| compression-05, June 1999, Work in progress, | 2016, <https://www.rfc-editor.org/info/rfc7858>. | |||
| <https://tools.ietf.org/html/draft-ietf-dnsind-local- | ||||
| compression-05>. | ||||
| [RFC6895] | [RFC8484] Hoffman, P. and P. McManus, "DNS Queries over HTTPS | |||
| Eastlake 3rd, D., "Domain Name System (DNS) IANA | (DoH)", RFC 8484, DOI 10.17487/RFC8484, October 2018, | |||
| Considerations", RFC 6895, April 2013, | <https://www.rfc-editor.org/info/rfc8484>. | |||
| <https://www.rfc-editor.org/info/rfc6982>. | ||||
| [RFC8484] | [SADDNS] Man, K., Qian, Z., Wang, Z., Zheng, X., Huang, Y., and H. | |||
| Hoffman, P., McManus, P., "DNS Queries over HTTPS | Duan, "DNS Cache Poisoning Attack Reloaded: Revolutions | |||
| (DoH)", RFC 8484, October 2018, | with Side Channels", Proc. 2020 ACM SIGSAC Conference on | |||
| <https://www.rfc-editor.org/info/rfc8484>. | Computer and Communications Security, CCS '20, | |||
| DOI 10.1145/3372297.3417280, November 2020, | ||||
| <https://dl.acm.org/doi/pdf/10.1145/3372297.3417280>. | ||||
| [RFC7858] | [SIGRED] Common Vulnerabilities and Exposures, "CVE-2020-1350: A | |||
| Hu, Z. et al, "Specification for DNS over Transport | remote code execution vulnerability in Windows Domain Name | |||
| Layer Security (TLS)", RFC 7858, May 2016, | System servers", 2020, <https://cve.mitre.org/cgi-bin/ | |||
| <https://www.rfc-editor.org/info/rfc7858>. | cvename.cgi?name=CVE-2020-1350>. | |||
| Acknowledgements | Acknowledgements | |||
| We would like to thank Shlomi Oberman, who has greatly contributed to | We would like to thank Shlomi Oberman, who has greatly contributed to | |||
| the research that led to the creation of this document. | the research that led to the creation of this document. | |||
| Authors' Addresses | Authors' Addresses | |||
| Stanislav Dashevskyi | Stanislav Dashevskyi | |||
| Forescout Technologies | Forescout Technologies | |||
| John F. Kennedylaan, 2 | John F. Kennedylaan, 2 | |||
| Eindhoven, 5612AB | 5612AB Eindhoven | |||
| The Netherlands | Netherlands | |||
| Email: stanislav.dashevskyi@forescout.com | Email: stanislav.dashevskyi@forescout.com | |||
| Daniel dos Santos | Daniel dos Santos | |||
| Forescout Technologies | Forescout Technologies | |||
| John F. Kennedylaan, 2 | John F. Kennedylaan, 2 | |||
| Eindhoven, 5612AB | 5612AB Eindhoven | |||
| The Netherlands | Netherlands | |||
| Email: daniel.dossantos@forescout.com | Email: daniel.dossantos@forescout.com | |||
| Jos Wetzels | Jos Wetzels | |||
| Forescout Technologies | Forescout Technologies | |||
| John F. Kennedylaan, 2 | John F. Kennedylaan, 2 | |||
| Eindhoven, 5612AB | 5612AB Eindhoven | |||
| The Netherlands | Netherlands | |||
| Email: jos.wetzels@forescout.com | Email: jos.wetzels@forescout.com | |||
| Amine Amri | Amine Amri | |||
| Forescout Technologies | Forescout Technologies | |||
| John F. Kennedylaan, 2 | John F. Kennedylaan, 2 | |||
| Eindhoven, 5612AB | 5612AB Eindhoven | |||
| The Netherlands | Netherlands | |||
| Email: amine.amri@forescout.com | Email: amine.amri@forescout.com | |||
| End of changes. 148 change blocks. | ||||
| 505 lines changed or deleted | 501 lines changed or added | |||
This html diff was produced by rfcdiff 1.48. | ||||