Talos Vulnerability Report

TALOS-2015-0065

Network Time Protocol Password Length Memory Corruption Vulnerability

October 21, 2015
CVE Number

CVE-2015-7854

Description

A potential buffer overflow vulnerability exists in the password management functionality of ntp. A specially crafted key file could cause a buffer overflow potentially resulting in memory being modified. An attacker could provide a malicious password to trigger this vulnerability.

Tested Versions

ntp 4.2.8p2

Product URLs

http://www.ntp.org

Details

The function MD5auth_setkey() is called in three places:

- line 179 in authreadkeys.c
- line 202 in authreadkeys.c
- line 32 in authusekey.c

void MD5authsetkey(keyidt keyno, int keytype, const uchar *key, sizet len)

The function takes 4 arguments, the keyno, keytype, key and len. If a keyno doesn’t exist, then a new key will be allocated based on len. If keyno does exist, the new key will be copied over the old key, using the new length as a size for the strlcpy or memcpy function. The 2 calls in authreadkeys.c have size checks (20 and 32 bytes respectively):

178     if (len <= 20) {    /* Bug 2537 */
                MD5auth_setkey(keyno, keytype, (u_char *)token, len);
            } else {
                char    hex[] = "0123456789abcdef";
                u_char  temp;
                char    *ptr;
                size_t  jlim;

    186         jlim = min(len, 2 * sizeof(keystr));
                …
    202         MD5auth_setkey(keyno, keytype, keystr, jlim / 2);

In the code above, keystr is a 32 byte unsigned character array.

The one in authusekey does not have size checks:

28  len = strlen((const char *)str);
    if (0 == len)
        return 0;

32  MD5auth_setkey(keyno, keytype, str, len);

However it is called twice from the functions sendrequest and passwd in ntpd.c (lines 895 and 1785) and ntpq.c (lines 1217 and 2453). The code to call authusekey is identical in both files. Both functions retrieve the key via a call to getpass which requires console access and only use it for the infoauthkeyid. Below is the code from ntpd.c for the functions sendrequest and passwd respectively:

889 if (!authistrusted(info_auth_keyid)) {
            pass = getpass_keytype(info_auth_keytype);
            if ('\0' == pass[0]) {
                fprintf(stderr, "Invalid password\n");
                return 1;
            }
            authusekey(info_auth_keyid, info_auth_keytype,
                   (u_char *)pass);
            authtrust(info_auth_keyid, 1);
    898 }

    1776    if (pcmd->nargs >= 1)
            pass = pcmd->argval[0].string;
        else {
            pass = getpass_keytype(info_auth_keytype);
            if ('\0' == *pass) {
                fprintf(fp, "Password unchanged\n");
                return;
            }
        }
        authusekey(info_auth_keyid, info_auth_keytype, (u_char *)pass);
    1786    authtrust(info_auth_keyid, 1);

Below is the vulnerable code in the MD5auth_setkey function:

530 /*
         * See if we already have the key.  If so just stick in the
         * new value.
         */
        bucket = &key_hash[KEYHASH(keyno)];
        for (sk = *bucket; sk != NULL; sk = sk->hlink) {
            if (keyno == sk->keyid) {
                sk->type = (u_short)keytype;
                secretsize = len;
                sk->secretsize = (u_short)secretsize;
    #ifndef DISABLE_BUG1243_FIX
                memcpy(sk->secret, key, secretsize);
    #else
                strlcpy((char *)sk->secret, (const char *)key,
                    secretsize);
    #endif

If a key is set to 20 bytes and later replaced by a key that is 32 bytes or larger, no extra size checks will be performed, the key will simply be copied over the old key, potentially resulting in a heap-based buffer overflow. This allows an attacker to first provide a shorter key and then a longer one. This can be exploited either through the console using the getpass functionality or via the remote configuration facility: specifying a password file where a specific key is set to a short password and then replacing it with one up to 32 bytes in length, causing a buffer overflow.

Credit

Yves Younan and Aleksander Nikolich of Cisco Talos