Penetration Test Report (Cure53): pentest report ntp

Dr.-Ing. Mario Heiderich, Cure53
Rudolf Reusch Str. 33
D 10367 Berlin
cure53.de · mario@cure53.de
Pentest-Report NTP 01.2017
Cure53, Dr.-Ing. M. Heiderich, M. Wege, MSc. N. Krein, BSc. D. Weißer
Index
Introduction
Scope
Test Coverage
Identified Vulnerabilities
NTP-01-002 NTP: Buffer Overflow in ntpq when fetching reslist (Critical)
NTP-01-012 NTP: Authenticated DoS via Malicious Config Option (High)
NTP-01-015 NTPsec: Regression in ctl_putdata() leads to Endless Loop (High)
NTP-01-016 NTP: Denial of Service via Malformed Config (High)
Miscellaneous Issues
NTP-01-001 NTP: Makefile does not enforce Security Flags (Low)
NTP-01-003 NTP: Improper use of snprintf() in mx4200_send() (Low)
NTP-01-004 NTP: Potential Overflows in ctl_put() functions (Medium)
NTP-01-005 NTP: Off-by-one in Oncore GPS Receiver (Low)
NTP-01-006 NTP: Copious amounts of Unused Code (Info)
NTP-01-007 NTP: Data Structure terminated insufficiently (Low)
NTP-01-008 NTP: Stack Buffer Overflow from Command Line (Low)
NTP-01-009 NTP: Privileged execution of User Library code (Low)
NTP-01-010 NTP: ereallocarray()/eallocarray() underused (Info)
NTP-01-011 NTP: ntpq_stripquotes() returns incorrect Value (Low)
NTP-01-013 NTPsec: Inclusion of obsolete NTPclassic-dependent Script (Info)
NTP-01-014 NTP: Buffer Overflow in DPTS Clock (Low)
Conclusion
Cure53, Berlin · 03.02.17 1/23
Dr.-Ing. Mario Heiderich, Cure53
Rudolf Reusch Str. 33
D 10367 Berlin
cure53.de · mario@cure53.de
Introduction
“NTP is a protocol designed to synchronize the clocks of computers over a network.
NTP version 4, a significant revision of the previous NTP standard, is the current
development version. It is formalized by RFCs released by the IETF.”
From http://www.ntp.org/
This report documents the findings of a source code audit of the NTP software. The
project was completed by Cure53 team in January 2017. Four members of the Cure53
participated in this assignment, which required a total of thirty-two days of testing in
order for a satisfactory level of coverage to be reached.
The audit constituted a joint project dedicated to both NTP and NTPsec. The code base
of NTPsec was examined in parallel and the two components of the scope were given
the same amount of attention and scrutiny. While this document primarily pertains to the
NTP element, the relevant results applicable to NTPsec are also briefly recalled. At the
same time, a separate report has been created to discuss the NTPsec issues in detail
and at length. In the latter NTPsec document, the discoveries connected to NTP are
analogically given less space and specificity in reporting.
As for the test’s approach, the investigations were rooted in the so-called white-box
methodology, meaning that the testing team was granted full access to relevant sources
and the like. Prior to initiating the audit, the Cure53 team established solid
communication channels for the two respective software items in scope, liaising with the
development teams of NTP and NTPsec, respectively.
The document proceeds with describing the test’s scope, then discusses coverage and
findings, ultimately delivering conclusions and verdicts about the general level of security
discovered, and the state of the audited code for the two software products in question.
Scope
• NTP 4.2.8.p9
◦ https://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-4.2/ntp-4.2.8p9.tar.gz
Cure53, Berlin · 03.02.17 2/23
Dr.-Ing. Mario Heiderich, Cure53
Rudolf Reusch Str. 33
D 10367 Berlin
cure53.de · mario@cure53.de
Test Coverage
One can consult an overview of the test’s coverage below. The listing shows the
percentage of the coverage reached by the code audit per directory. All directories
belong to the downloaded code base.
Coverage in % SLOC Directory
n/a 88991 sntp
100 68098 ntpd
20 39912 lib
20 12637 libntp
n/a 8791 tests
100 7626 include
100 7578 ports
80 7452 ntpq
n/a 7321 util
n/a 5934 scripts
n/a 5720 libparse
n/a 5148 ntpdc
100 1565 ntpdate
n/a 1520 parseutil
n/a 1422 ntpsnmpd
n/a 950 libjsmn
n/a 934 clockstuff
80 730 kernel
n/a 353 adjtimed
In addition to the directories marked with “n/a”, the obsolete subcomponent autokey was
explicitly left out of scope along with the external libraries (libevent, libisc, libopts), and
the testing framework unity.
Cure53, Berlin · 03.02.17 3/23
Dr.-Ing. Mario Heiderich, Cure53
Rudolf Reusch Str. 33
D 10367 Berlin
cure53.de · mario@cure53.de
Identified Vulnerabilities
The following sections list both vulnerabilities and implementation issues spotted during
the testing period. Note that findings are listed in a chronological order rather than by
their degree of severity and impact. The aforementioned severity rank is simply given in
brackets following the title heading for each vulnerability. Each vulnerability is
additionally given a unique identifier (e.g. NTP-01-001) for the purpose of facilitating any
future follow-up correspondence.
NTP-01-002 NTP: Buffer Overflow in ntpq when fetching reslist (Critical)
Note: This issue affects NTP only and is not present in the NTPsec code.
A stack buffer overflow can be triggered by a malicious server when a client (using ntpq)
requests the restriction list from the server. This is due to a missing length check in the
reslist() function. It occurs whenever the function parses the server’s response and
encounters a flagstr variable of an extensive length. The string will be copied into a
fixed-size buffer, leading to an overflow on the function’s stack-frame.
Although this issue is mitigated by having the ntpq compiled with FORTIFY_SOURCE,
the default compilation flags do not include this option (see NTP-01-001), thus leaving a
considerable percentage of clients vulnerable. Additionally, stack canaries do not entirely
prevent this issue since the overflow allows to overwrite pointers which are used for
reading and writing from and to memory before the stack canary is reached. Having
control over these pointers makes it possible for an attacker to bypass the stack canary
protection.
Affected File:
ntp/ntpq/ntpq-subs.c
Affected Code:
typedef struct reslist_row_tag {
u_int idx;
sockaddr_u addr;
sockaddr_u mask;
u_long hits;
char flagstr[128];
} reslist_row;
static void
reslist(
struct parse * pcmd,
FILE * fp
Cure53, Berlin · 03.02.17 4/23
Dr.-Ing. Mario Heiderich, Cure53
Rudolf Reusch Str. 33
D 10367 Berlin
cure53.de · mario@cure53.de
)
{
[...]
reslist_row row;
[...]
qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars,
qdata, &rstatus, &dsize, &datap);
[...]
ZERO(row);
fields = 0;
ui = 0;
while (nextvar(&dsize, &datap, &tag, &val)) {
[...]
case 'f':
if (1 == sscanf(tag, flags_fmt, &ui)) {
if (NULL == val) {
row.flagstr[0] = '\0';
comprende = TRUE;
} else {
len = strlen(val);
memcpy(row.flagstr, val, len);
row.flagstr[len] = '\0';
comprende = TRUE;
}
}
break;
To verify this issue, the NTP server component was modified to replace every flagstr
variable with a string of 312-bytes in length prior to sending the response to the client.
Modified File:
ntp/ntpd/ntp_control.c
Modified Code:
static void
send_restrict_entry(
restrict_u * pres,
int ipv6,
u_int idx
)
{
[...]
case 3:
snprintf(tag, sizeof(tag), flags_fmt, idx);
match_str = res_match_flags(pres->mflags);
access_str = res_access_flags(pres->flags);
if ('\0' == match_str[0]) {
Cure53, Berlin · 03.02.17 5/23
Dr.-Ing. Mario Heiderich, Cure53
Rudolf Reusch Str. 33
D 10367 Berlin
cure53.de · mario@cure53.de
pch = access_str;
} else {
LIB_GETBUF(buf);
snprintf(buf, LIB_BUFLENGTH, "%s %s",
match_str, access_str);
pch = buf;
}
// TODO: start - patch to simulate malicious server
char payload[1024] = {0};
memset(payload, 'A', 312);
ctl_putunqstr(tag, payload, strlen(payload));
// TODO: original code
// ctl_putunqstr(tag, pch, strlen(pch));
// TODO: end
break;
Once the client connects to the malicious server and requests the restriction list, the
server responds with a modified package carrying the flagstr to trigger the overflow. As
soon as the client tries to parse the response, the overflow is trigger