Lucene search

K
talosTalos IntelligenceTALOS-2016-0071
HistoryJan 19, 2016 - 12:00 a.m.

Network Time Protocol Skeleton Key: Symmetric Authentication Impersonation Vulnerability

2016-01-1900:00:00
Talos Intelligence
www.talosintelligence.com
261

8.1 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

HIGH

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

6.8 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:M/Au:N/C:P/I:P/A:P

0.015 Low

EPSS

Percentile

86.6%

CERT VU#357792

Summary

Symmetric key encryption requires a single trusted key to be specified for each server configuration. A key specified only for one server should only work to authenticate that server, other trusted keys should be refused.

Instead we observe that when symmetric key authentication is verified, there is no check that the key used is the key specified for the address, any trusted key can be used as long as the keyid references another key the systems share and that key is used to compute the MAC.

This has three implications for the client server model. A client that has multiple servers configured each with different keys could be attacked by one of its servers spoofing every other server using its own key. Even worse a server can be attacked by any of its authenticated clients in a similar manner.

Finally, being able to use any key to authenticate a packet for a client or server means that if any key in the trustedkeys list uses a weak digest algorithim (MD5), then an attacker can abuse that method instead of being restricted by the stronger keys configured.

NOTE: Code locations referenced below refer to the NTP reference implementation from http://www.ntp.org.

There is no clear location in the code where this defect occurs, since it exists due to an omission. Verifying the key used matches the proper server’s key could be done in ntp_proto.c around line 803 (in 4.8.2p3) where authdecrypt is called, or it might make sense to build it into the libntp code such as the authdecrypt function itself.

Be aware that this issue could affect other ntpd modes of operation such as broadcast or active/passive peering.

Tested Versions

NTP 4.2.8p3
NTPsec a5fb34b9cc89b92a8fef2f459004865c93bb7f92
chrony 2.2

Product URLs

(http://www.ntp.org/)[http://www.ntp.org/] (https://www.ntpsec.org/)[https://www.ntpsec.org/] (http://chrony.tuxfamily.org/)[http://chrony.tuxfamily.org/]

CVSS Score

CVSSv2: 3.6 - AV:N/AC:H/Au:S/C:N/I:P/A:P
CVSSv3: 4.2 - CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:L

Details

ntpd does not ensure that the key used to verify the authenticity of a packet actually belongs to the alleged sender of the packet.

The intended binding between keys (via keyids) and peers is indicated to ntpd through the configuration file. For example:

server ntp-server key 1

indicates that keyid 1 is a symmetric key shared with ntp-server. Packets bound for ntp-server should be authenticated under keyid 1 and, to prevent impersonation, packets from ntp-server should be authenticated using keyid 1.

Unfortunately, when receiving a packet, allegedly from ntp-sever, ntpd does not require the packet to authenticate under keyid 1. ntpd only ensures that the packet authenticates under some trustedkey known to ntpd. This allows any authenticated peer to impersonate any other authenticated peer.

We confirmed this vulnerability with the following setup. (The tests below were performed with NTP 4.2.8p3. 4.2.8p4 appears to introduce regressions which break LOCAL refclocks and symmetric associations.)

  • ntp-server - simulated stratum 1 server to provide time to clients

      # /etc/ntp.conf
    

    keys /etc/ntp.keys
    trustedkey 1
    server 127.127.1.1 prefer
    fudge 127.127.1.1 stratum 0

    /etc/ntp.keys:

    1 MD5 youllneverguess
    2 MD5 you_really_wont

  • ntp-client - authenticates ntp-server with keyid 1 and ntp-client2 with keyid 2

      # /etc/ntp.conf
    

    keys /etc/ntp.keys
    trustedkey 1 2
    server ntp-server key 1 minpoll 2 maxpoll 2 iburst
    peer ntp-client2 key 2 minpoll 2 maxpoll 2 noselect

    /etc/ntp.keys

    1 MD5 youllneverguess
    2 MD5 you_really_wont

  • ntp-client2 - authenticates ntp-server with keyid 1, uses keyid 1 to talk to ntp-client

      # /etc/ntp.conf
    

    keys /etc/ntp.keys
    trustedkey 1 2
    server ntp-server key 1 minpoll 2 maxpoll 2 iburst
    peer ntp-client key 1 minpoll 2 maxpoll 2

    /etc/ntp.keys

    1 MD5 youllneverguess
    2 MD5 you_really_wont

  • attacker1 (192.168.33.9) - conducts a man-in-the-middle attack using ntp-client2’s peer key (keyid 2) to impersonate ntp-server to ntp-client and shift time

attacker1 uses an ARP-spoofing attack to intercept and replay all packets between ntp-client and ntp-server. When it receives a server mode packet, it modifies the sent and recv time to be 100 years in the future. It then authenticates the packet with ntp-client2’s key (keyid 2). In this specific attack, the timestamps overflow so 100 years in the future from 2015 is 1979.

ntp-client accepts the forged replies as though they were coming from ntp-server and, eventually, steps its clock.

Do The Sender and Recipient Have to Agree on the Keyid?

According to the ntpd documentation html/authentic.html:

> The servers and clients involved must agree on the key ID, key type and key to authenticate NTP packets.

Though it doesn’t indicate precisely what “agreement” means or why, this conflicts with RFC 5905 which states:

> keyid: Symmetric key ID for the 128-bit MD5 key used to generate and verify the MAC. The client and server or peer can use different values, but they must map to the same key.

This statement is problematic and only partially true. It is problematic because it does not address the binding of symmetric keys (specified by keyid) to the peers that they authenticate, allowing for impersonation between peers. It is only partially true because, in client-server modes, the server will use the keyid from the client mode packet to authenticate the incoming client mode packet as well as the outgoing server mode packet. Thus, in a benign scenario, clients and server must agree both on keyid and the key value. In symmetric modes, the statement is partially true because each peer uses the keyid specified in its peer association when generating an outgoing packet. However, in all cases, the keyid specified in an incoming packet will be used to authenticate that packet. Therefore, even in symmetric modes, if the two peers use different keyids A and B for a given symmetric association, each peer must map both keyids to the same key — the sender will use A (respectively B) to generate the authenticator for the outgoing packet and, therefore, the recipient will use A (respectively B) to authenticate the incoming packet. This is illustrated by the non-normative example code from RFC 5905:

This is also borne out by the ntpd source code. When ntpd receives an incoming packet with an authenticator, it verifies the authentication under the key specified by the keyid from the incoming packet:

  • receive() reads the keyid from the packet:

      skeyid = ntohl(((u_int32 *)pkt)[authlen / 4]);
    
  • receive() then calls authdecrypt() with that keyid:

      if (!authdecrypt(skeyid, (u_int32 *)pkt, authlen,
      has_mac))
      is_authentic = AUTH_ERROR;
    

    else
    is_authentic = AUTH_OK;

  • authdecrypt() calls authhavekey() which looks up the key by keyid:

      if (id == sk->keyid) {
      if (0 == sk->type) {
    
  • And finally, MD5authdecrypt() is called to compute and verify the digest using the key value found via the packet’s key id

  • But, there is never a check which verifies that the keyid from the incoming packet (skeyid) matches the keyid configured for the peer association in question (peer->keyid)

Test Case: Equal keyids Refer to Differing Key Material

To verify that the sender and recipient can’t merely use different keyids to refer to the same key material unless both have been configured to map both keyids to the same key, we configured ntp-client2 with the same keys as ntp-server and ntp-client, but we swapped the keyids on ntp-client2. This confirmed that, though ntp-client2 was using the same key material as the other two nodes, all packets from ntp-client2 would fail authentication because the other nodes did not also have the same key material mapped to the keyids sent by ntp-client2.

  • ntp-client can sync with ntp-server just fine

      # /etc/ntp.keys
    

    1 MD5 youllneverguess
    2 MD5 you_really_wont

    /etc/ntp.conf

    keys /etc/ntp.keys
    trustedkey 1 2

    server ntp-server key 1 minpoll 2 maxpoll 2 iburst
    peer ntp-client2 key 2 minpoll 2 maxpoll 2

    ntp-client$ ntpq -c as

    ind assid status conf reach auth condition last_event cnt

    1 43544  f65a   yes   yes   ok   sys.peer    sys_peer  5  # ntp-server
    2 43545  e01a   yes    no   ok     reject    sys_peer  1  # ntp-client2
    

ntp-client detects that ntp-client2’s packets fail authentication. However, because ntp-client2 is also rejecting ntp-client’s packets due to bad auth, the origin timestamp doesn’t match and that check fails first before an auth check is performed. Note the auth 2 (AUTH_ERROR) on line 2 of the log snippet below.

      Nov  3 19:23:37 ntp-client ntpd: (ntp_proto.c): calling authdecrypt
  Nov  3 19:23:37 ntp-client ntpd: receive: at 698 192.168.33.11<-192.168.33.13 mode 1 keyid 00000001 len 68 auth 2
  Nov  3 19:23:37 ntp-client ntpd: (ntp_proto.c): big switch
  Nov  3 19:23:37 ntp-client ntpd: (ntp_proto.c): AM_PROCPKT, regular packet
  Nov  3 19:23:37 ntp-client ntpd: (ntp_proto.c): done with big switch
  Nov  3 19:23:37 ntp-client ntpd: (ntp_proto.c): flip is 0, not interleaved
  Nov  3 19:23:37 ntp-client ntpd: (ntp_proto.c): met bogus condition (p_org is not qual to peer->aorg), test2
  • ntp-client2 uses the same keys but different ids.

      # /etc/ntp.keys
    

    2 MD5 youllneverguess
    1 MD5 you_really_wont

    /etc/ntp.conf

    keys /etc/ntp.keys
    trustedkey 1 2

    server ntp-server key 2 minpoll 2 maxpoll 2 iburst
    peer ntp-client key 1 minpoll 2 maxpoll 2

We can see that ntp-client2 is rejecting both the server and the peer due to bad auth.

      ntp-client2$ ntpq -c as

  ind assid status  conf reach auth condition  last_event cnt
  ===========================================================
    1 64340  c01c   yes    no   bad    reject              1  # ntp-server
    2 64341  c01c   yes    no   bad    reject              1  # ntp-client

We can see this when ntp-client2 receives a packet from ntp-client:

      Nov  3 19:40:29 ntp-client2 ntpd: receive: at 1438 192.168.33.13<-192.168.33.11 mode 1 keyid 00000002 len 68 auth 2
  Nov  3 19:40:29 ntp-client2 ntpd: (ntp_proto.c): big switch
  Nov  3 19:40:29 ntp-client2 ntpd: (ntp_proto.c): AM_PROCPKT, regular packet
  Nov  3 19:40:29 ntp-client2 ntpd: (ntp_proto.c): done with big switch
  Nov  3 19:40:29 ntp-client2 ntpd: (ntp_proto.c): flip is 0, not interleaved
  Nov  3 19:40:29 ntp-client2 ntpd: (ntp_proto.c): digest failed
  Nov  3 19:40:29 ntp-client2 ntpd: event at 1438 192.168.33.11 c01c 8c bad_auth digest

Test Case: Using Different keyids That Map To The Same Key Material

This tests confirms that, as long as the sender and the recipient have the same key material configured for a given keyid, the recipient won’t enforce the binding between the keyid configured for an association and the keyid used to authenticate an incoming packet under that association.

We configure the same keys on both hosts

# /etc/ntp.keys on both hosts
1 MD5 youllneverguess
2 MD5 you_really_wont

But ntp-client uses keyid 2 to communicate with ntp-client2.

# ntp-client /etc/ntp.conf
keys /etc/ntp.keys
trustedkey 1 2

server ntp-server iburst key 1 minpoll 2 maxpoll 2
peer ntp-client2 key 2

And ntp-client2 uses keyid 1 when sending packets to ntp-client.

# ntp-client2 /etc/ntp.conf
keys /etc/ntp.keys
trustedkey 1 2

server ntp-server iburst key 1 minpoll 2 maxpoll 2
peer ntp-client key 1

As can be seen here, both ntp-client and ntp-client2 accept the packets from their peer despite being configured to use different keyids.

ntp-client$ ntpq -c as
ind assid status  conf reach auth condition  last_event cnt
===========================================================
  1 55125  f65a   yes   yes   ok   sys.peer    sys_peer  5  # ntp-server
  2 55126  f03a   yes   yes   ok     reject    sys_peer  3  # ntp-client2


ntp-client2$ ntpq -c as
ind assid status  conf reach auth condition  last_event cnt
===========================================================
  1 53439  f63a   yes   yes   ok   sys.peer    sys_peer  3  # ntp-server
  2 53440  f024   yes   yes   ok     reject   reachable  2  # ntp-client

Possible Fix

The following (untested) patch ensures that the keyid used to authenticate the packet is the same as the keyid configured for the corresponding peer association. If not, it sets is_authentic to AUTH_ERROR and skips the call to authdecrypt().

diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c
index 2a15d72..01959f0 100644
--- a/ntpd/ntp_proto.c
+++ b/ntpd/ntp_proto.c
@@ -840,6 +840,11 @@ receive(
 		}
 #endif	/* AUTOKEY */

+ 		/* Ensure that the packet actually came from the expected peer */
+ 		if(skeyid <= NTP_MAXKEY && peer && skeyid != peer->keyid) {
+  			is_authentic = AUTH_ERROR;
+  			// TODO: log impersonation attempt
+
 		/*
 		 * Compute the cryptosum. Note a clogging attack may
 		 * succeed in bloating the key cache. If an autokey,
@@ -847,11 +852,13 @@ receive(
 		 * again. If the packet is authentic, it can mobilize an
 		 * association. Note that there is no key zero.
 		 */
-		if (!authdecrypt(skeyid, (u_int32 *)pkt, authlen,
+  		} else if (!authdecrypt(skeyid, (u_int32 *)pkt, authlen,
 		    has_mac))
 			is_authentic = AUTH_ERROR;
 		else
 			is_authentic = AUTH_OK;
+
+
 #ifdef AUTOKEY
 		if (crypto_flags && skeyid > NTP_MAXKEY)
 			authtrust(skeyid, 0);

Timeline

2015-10-07 - Vendor Disclosure
2016-01-19 - Public Release

8.1 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

HIGH

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

6.8 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:M/Au:N/C:P/I:P/A:P

0.015 Low

EPSS

Percentile

86.6%