| < draft-dashevskyi-dnsrr-antipatterns-03.txt | draft-dashevskyi-dnsrr-antipatterns-04.txt > | |||
|---|---|---|---|---|
| Independent Submission S. Dashevskyi | Independent Submission S. Dashevskyi | |||
| Internet-Draft D. dos Santos | Internet-Draft D. dos Santos | |||
| Intended status: Informational J. Wetzels | Intended status: Informational J. Wetzels | |||
| Expires: October 21, 2022 A. Amri | Expires: November 6, 2022 A. Amri | |||
| Forescout Technologies | Forescout Technologies | |||
| April 21, 2022 | May 6, 2022 | |||
| Common implementation anti-patterns related | Common implementation anti-patterns related | |||
| to Domain Name System (DNS) resource record (RR) processing | to Domain Name System (DNS) resource record (RR) processing | |||
| draft-dashevskyi-dnsrr-antipatterns-03 | draft-dashevskyi-dnsrr-antipatterns-04 | |||
| 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) response 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. | |||
| skipping to change at line 36 ¶ | skipping to change at line 36 ¶ | |||
| Internet-Drafts are working documents of the Internet Engineering | Internet-Drafts are working documents of the Internet Engineering | |||
| Task Force (IETF). Note that other groups may also distribute | Task Force (IETF). Note that other groups may also distribute | |||
| working documents as Internet-Drafts. The list of current Internet- | working documents as Internet-Drafts. The list of current Internet- | |||
| Drafts is at https://datatracker.ietf.org/drafts/current/. | Drafts is at https://datatracker.ietf.org/drafts/current/. | |||
| Internet-Drafts are draft documents valid for a maximum of six months | Internet-Drafts are draft documents valid for a maximum of six months | |||
| and may be updated, replaced, or obsoleted by other documents at any | and may be updated, replaced, or obsoleted by other documents at any | |||
| time. It is inappropriate to use Internet-Drafts as reference | time. It is inappropriate to use Internet-Drafts as reference | |||
| material or to cite them other than as "work in progress." | material or to cite them other than as "work in progress." | |||
| This Internet-Draft will expire on October 21, 2022. | This Internet-Draft will expire on November 6, 2022. | |||
| 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 | (http://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 | |||
| skipping to change at line 178 ¶ | skipping to change at line 178 ¶ | |||
| [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 a | |||
| 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"). The destination buffer for the domain name | ("dns_payload"). The destination buffer for the domain name | |||
| ("name_buffer") is typically limited to 255 bytes as per | ("name_buffer") is typically limited to 255 bytes as per | |||
| [RFC1035] and can be allocated either in the stack or in the heap | [RFC1035] and can be allocated 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 at Snippet 1 reads the domain name | |||
| label-by-label from a RR until it reaches the NULL octet (0x00) that | label-by-label from a 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 octet | |||
| ("label_len_octet") is a compression pointer, the code extracts the | ("label_len_octet") is a compression pointer, the code extracts the | |||
| value of the compression offset and uses it to "jump" to another | 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 code | |||
| will move on to the next label length octet. | 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 | |||
| skipping to change at line 206 ¶ | skipping to change at line 206 ¶ | |||
| the maximum size of DNS packets that can be sent over the UDP | the maximum size of DNS packets that can be sent over the UDP | |||
| protocol is limited to 512 octets. | protocol is limited to 512 octets. | |||
| The pseudocode at Snippet 1 violates these constraints, as it will | The pseudocode at Snippet 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 out of the | |||
| bounds of a DNS packet. For instance, the compression pointer of | bounds of a DNS packet. For instance, the compression pointer of | |||
| "0xffff" will produce the offset of 16383 octets, which is most | "0xffff" will produce the 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 | |||
| original DNS packet. Supplying such offset values will most likely | original DNS packet. Supplying such offset values will most likely | |||
| cause memory corruption issues and may lead to Denial-of-Service | cause memory corruption issues and may lead to Denial-of-Service | |||
| conditions (e.g., a NULL pointer dereference after "label_len_octet" | conditions (e.g., a Null pointer dereference after "label_len_octet" | |||
| is set to an invalid address in memory). As an additional example, | is set to an invalid address in memory). As an additional example, | |||
| see [CVE-2020-25767], [CVE-2020-24339], and [CVE-2020-24335]. | see [CVE-2020-25767], [CVE-2020-24339], and [CVE-2020-24335]. | |||
| The pseudocode at Snippet 1 allows for jumping from a compression | The pseudocode at Snippet 1 allows for jumping from a compression | |||
| pointer to another compression pointer and it does not restrict the | pointer to another compression pointer and it does not restrict the | |||
| number of such jumps. That is, if a label length octet which is | number of such jumps. That is, if a label length octet which is | |||
| currently being parsed is a compression pointer, the code will | currently being parsed is a compression pointer, the code will | |||
| perform a jump to another label, and if that other label is a | perform a jump to another label, and if that other label is a | |||
| compression pointer as well, the code will perform another jump, and | compression pointer as well, the code will perform another jump, and | |||
| so forth until it reaches an decompressed label. This may lead to | so forth until it reaches an decompressed label. This may lead to | |||
| skipping to change at line 332 ¶ | skipping to change at line 332 ¶ | |||
| consider "0x41" to be a valid compression pointer, and the packet | consider "0x41" to be a valid compression pointer, and the packet | |||
| may pass the validation steps. | may pass the validation steps. | |||
| This might give an additional leverage for attackers in constructing | This might give an additional leverage for attackers in 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 a RR (an octet with | |||
| the 2 highest bits set to 1) must resolve to an octet within a DNS | the 2 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 the 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) the | |||
| support for nested compression pointers (pointers that point to a | support for nested compression pointers (pointers that point to a | |||
| compressed name) should be discouraged: there is very little to be | compressed name) should be discouraged: there is very little to be | |||
| gained in terms of performance versus the high possibility of | gained in terms of performance versus the high possibility of | |||
| introducing errors, such as the ones discussed above. | introducing errors, such as the ones discussed above. | |||
| skipping to change at line 416 ¶ | skipping to change at line 416 ¶ | |||
| or stack metadata in a controlled manner. | or stack metadata 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 | |||
| the value of more than 0 and less than 64 ([RFC1035]). If this is | the value of more than 0 and less than 64 ([RFC1035]). If this is | |||
| not the case, an invalid value has been provided within the packet, | not the case, an invalid value has been provided within the packet, | |||
| or a value at an invalid position might be interpreted as a domain | or a value at an invalid position might be interpreted as a domain | |||
| name length due to other errors in the packet (e.g., misplaced NULL | name 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 NULL 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 within the maximum name | |||
| length (255 octets). | length (255 octets). | |||
| 4. NULL-terminator Placement Validation | 4. Null-terminator Placement Validation | |||
| A domain name must end with a NULL ("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 at Snippets 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 | |||
| NULL octet placed at the proper position within a RR are not | NUL octet placed at the proper position within a 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 Snippets 1 and 4 will continue | |||
| to copy octets into the name buffer, until a NULL octet is | to copy octets into the name buffer, until a NUL octet is | |||
| encountered. This octet can be placed at an arbitrary position | encountered. This octet can be placed at an arbitrary position | |||
| within a RR, or not placed at all. | within a RR, or not placed at all. | |||
| Consider a pseudocode function shown on Snippet 5. The function | Consider a pseudocode function shown on Snippet 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) { | |||
| skipping to change at line 462 ¶ | skipping to change at line 462 ¶ | |||
| 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 | Snippet 5 - A broken implementation of a function that returns the | |||
| length of a domain name | 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 NULL ("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-terminated, the return value "strlen(3)" may be also controlled | Null-terminated, the return value "strlen(3)" may be also controlled | |||
| by attackers. Through the value of "name_len" attackers may control | by 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 other operations depending on the | |||
| implementation specifics. | implementation specifics. | |||
| The absence of explicit checks for the NULL octet placement may also | The absence of explicit checks for the NUL octet placement may also | |||
| facilitate controlled memory reads and writes. An example of | 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 in the code Snippet 5, developers should use | |||
| the function "strnlen(3)" that reads at most X characters(the second | the function "strnlen(3)" that reads at most X characters(the second | |||
| argument of the function), and ensure that X is not larger than the | argument of the function), and ensure that X is not larger than the | |||
| buffer allocated for the name. | 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 of | |||
| End of changes. 18 change blocks. | ||||
| 18 lines changed or deleted | 18 lines changed or added | |||
This html diff was produced by rfcdiff 1.48. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ | ||||