An exploitable denial of service vulnerability exists in the broadcast mode replay prevention functionality of ntpd. To prevent replay of broadcast mode packets, ntpd rejects broadcast mode packets with non-monotonically increasing transmit timestamps. Remote unauthenticated attackers can send specially crafted broadcast mode NTP packets to cause ntpd to reject all broadcast mode packets from legitimate NTP broadcast servers.
NTP 4.2.8p6
CVSSv2: 5.0 - (AV:N/AC:L/Au:N/C:N/I:N/A:P)
CVSSv3: 5.3 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
In response to the NTP Deja Vu vulnerability (CVE-2015-7973), ntp-4.2.8p6 introduced several new integrity checks on incoming broadcast mode packets. Upon receipt of a broadcast mode packet, before authentication is enforced, ntpd will reject the packet if any of the following conditions hold:
pkt->ppoll < peer->minpoll || pkt->ppoll > peer->maxpoll
The packet was received before a full poll interval has elapsed since the last broadcast packet was received from the packet’s sender. i.e. A server cannot ingress packets more frequently than peer->minpoll
.
The packet transmit timestamp is less than the last seen broadcast packet transmit timestamp from the packet’s sender. i.e. Broadcast packet transmit timestamps must be monotonically increasing.
The following logic is used to ensure that packet transmit timestamps are monotonically increasing:
/* ntp-4.2.8p6 ntpd/ntp_proto.c */
1305 if (MODE_BROADCAST == hismode) {
...
1351 tdiff = p_xmt;
1352 L_SUB(&tdiff, &peer->bxmt);
1353 if (tdiff.l_i < 0) {
1354 msyslog(LOG_INFO, "receive: broadcast packet from %s contains non-monotonic timestamp: %#010x.%08x -> %#010x.%08x",
1355 stoa(&rbufp->recv_srcadr),
1356 peer->bxmt.l_ui, peer->bxmt.l_uf,
1357 p_xmt.l_ui, p_xmt.l_uf
1358 );
1359 ++bail;
1360 }
1361
1362 peer->bxmt = p_xmt;
1363
1364 if (bail) {
1365 peer->timelastrec = current_time;
1366 sys_declined++;
1367 return;
1368 }
1369 }
If the packet transmit timestamp is less than the transmit timestamp on the last received broadcast packet from this association (p_xmt - peer->bxmt < 0
), the packet will be discarded.
Unfortunately, line 1362 updates the saved transmit timestamp for alleged sender of the packet (peer->bxmt
) before the packet is discarded. The update takes place even if the packet is unauthenticated and fails the previous integrity checks.
This leads to a trivial denial of service attack. The attacker:
peer->bxmt
forward so that any legitimate packet will be rejected by the non-monotonic timestamp check.
peer->bxmt
update happens even when a packet fails the poll period checks, there is no penalty for sending packets too frequently.To prevent this vulnerability, peer->bxmt
should only be updated when a packet authenticates correctly. This is the approach taken in the patch below.
There is no workaround for this issue. Because the vulnerable logic is executed before authentication is enforced, authentication and the restrict notrust
ntpd.conf directive have no effect. An attacker can bypass notrust
restrictions by sending incorrectly authenticated packets.
In order to succeed in an attack, the attacker must send at least one spoofed packet per poll period. Therefore observing more than one NTP broadcast packet from the same sender address per poll period indicates a possible attack.
The following patch can be used to fix this vulnerability:
From 097fd4dae9ac4927d7cfa8011fd42f704bd02c45 Mon Sep 17 00:00:00 2001
From: Matthew Van Gundy <[email protected]>
Date: Tue, 26 Jan 2016 15:00:28 -0500
Subject: [PATCH] Fix unauthenticated broadcast mode denial of service (peer->bxmt)
---
include/ntp_fp.h | 1 +
ntpd/ntp_proto.c | 22 ++++++++++++++++------
2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/include/ntp_fp.h b/include/ntp_fp.h
index 7806932..ad7a01d 100644
--- a/include/ntp_fp.h
+++ b/include/ntp_fp.h
@@ -242,6 +242,7 @@ typedef u_int32 u_fp;
#define L_ISGTU(a, b) M_ISGTU((a)->l_ui, (a)->l_uf, (b)->l_ui, (b)->l_uf)
#define L_ISHIS(a, b) M_ISHIS((a)->l_ui, (a)->l_uf, (b)->l_ui, (b)->l_uf)
#define L_ISGEQ(a, b) M_ISGEQ((a)->l_ui, (a)->l_uf, (b)->l_ui, (b)->l_uf)
+#define L_ISGEQU(a, b) L_ISHIS(a, b)
#define L_ISEQU(a, b) M_ISEQU((a)->l_ui, (a)->l_uf, (b)->l_ui, (b)->l_uf)
/*
diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c
index ad45409..ac469ce 100644
--- a/ntpd/ntp_proto.c
+++ b/ntpd/ntp_proto.c
@@ -1305,7 +1305,6 @@ receive(
if (MODE_BROADCAST == hismode) {
u_char poll;
int bail = 0;
- l_fp tdiff;
DPRINTF(2, ("receive: PROCPKT/BROADCAST: prev pkt %ld seconds ago, ppoll: %d, %d secs\n",
(current_time - peer->timelastrec),
@@ -1348,9 +1347,8 @@ receive(
++bail;
}
- tdiff = p_xmt;
- L_SUB(&tdiff, &peer->bxmt);
- if (tdiff.l_i < 0) {
+ /* Use L_ISGEQU() to ensure unsigned comparison */
+ if (!L_ISGEQU(&p_xmt, &peer->bxmt)) {
msyslog(LOG_INFO, "receive: broadcast packet from %s contains non-monotonic timestamp: %#010x.%08x -> %#010x.%08x",
stoa(&rbufp->recv_srcadr),
peer->bxmt.l_ui, peer->bxmt.l_uf,
@@ -1359,8 +1357,6 @@ receive(
++bail;
}
- peer->bxmt = p_xmt;
-
if (bail) {
peer->timelastrec = current_time;
sys_declined++;
@@ -1563,6 +1559,14 @@ receive(
peer->xmt = p_xmt;
/*
+ * Now that we know the packet is correctly authenticated,
+ * update peer->bxmt if needed
+ */
+ if (MODE_BROADCAST == hismode) {
+ peer->bxmt = p_xmt;
+ }
+
+ /*
* Set the peer ppoll to the maximum of the packet ppoll and the
* peer minpoll. If a kiss-o'-death, set the peer minpoll to
* this maximum and advance the headway to give the sender some
@@ -2400,6 +2404,7 @@ peer_clear(
)
{
u_char u;
+ l_fp bxmt = peer->bxmt;
#ifdef AUTOKEY
/*
@@ -2436,6 +2441,11 @@ peer_clear(
peer->flash = peer_unfit(peer);
peer->jitter = LOGTOD(sys_precision);
+ /* Don't throw away our broadcast replay protection */
+ if (peer->hmode == MODE_BCLIENT) {
+ peer->bxmt = bxmt;
+ }
+
/*
* If interleave mode, initialize the alternate origin switch.
*/