Talos Vulnerability Report

TALOS-2016-0071

January 19, 2016

CVE Number

CVE-2015-7974, CVE-2016-1567

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.

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.

CVSS Score

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.)

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:

According to receive() (https://tools.ietf.org/html/rfc5905#page-78)
the keyid in the received packet is used to look up the key to
authenticate the packet.

The keyid in the received packet is used to set the peer keyid for
manycast peers (https://tools.ietf.org/html/rfc5905#page-78),
symmetric peers (https://tools.ietf.org/html/rfc5905#page-79), and
broadcast peers.

The keyid in the received packet is also used to choose the
keyid and key to authenticate the transmitted packet in
fast_xmit() (https://tools.ietf.org/html/rfc5905#page-88)

In peer_xmit() (https://tools.ietf.org/html/rfc5905#page-108)
the keyid of the peer is used to authenticate the transmitted
packet. It appears to omit setting the keyid on the transmitted
packet. But, for preemptable associations, the peer keyid came
from a received packet. For configured associations, it's the
value from the configuration file.

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:

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 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.

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.

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().