Wow, I feel real sorry for the FreeBSD guys having to announce a remotely exploitable vulnerability in their Telnet Daemon on Christmas Eve! Let’s just hope that nobody uses Telnet anymore.

The Classic, Oh So Classic, Overflow

The buffer overflow occurs in encrypt.c and really is your textbook example of a buffer overflow. The interesting part is that this code hasn’t been touched in over 3 years! As Colin Percival pointed out in the comments, this bug has been in telnetd for over 20 years!

telnetd.c & state.c

It all starts in telnetd.c which receives input from the network and passes it to state.c which is a little “state machine” that directs the input to the proper method call by pulling a character from the data received and walking through a huge case statement.

In the case of encryption negotiation, the case TELOPT_ENCRYPT is reached and eventually the subcase ENCRYPT_ENC_KEYID is reached. ‘subpointer’ is data supplied by the client minus the 2 bytes used for control flow of the state machine. encrypt_enc_keyid passes the ‘subpointer’ data directly to the encrypt_keyid function

... case ENCRYPT_ENC_KEYID: encrypt_enc_keyid(subpointer, SB_LEN()); break; ...

encrypt.c

encrypt.c defines a key_info struct with a fixed length buffer of 64 bytes to hold the key id passed by the client.

#define MAXKEYLEN 64 static struct key_info { unsigned char keyid[MAXKEYLEN]; int keylen; int dir; int *modep; Encryptions *(*getcrypt)(); } ki[2] = { { { 0 }, 0, DIR_ENCRYPT, &encrypt_mode, findencryption }, { { 0 }, 0, DIR_DECRYPT, &decrypt_mode, finddecryption }, };

However, the encrypt_keyid method happily copies the passed data into the key_info struct using memcpy without any restriction on the length specified by MAXKEYLEN

static void encrypt_keyid(struct key_info *kp, unsigned char *keyid, int len) { ... } else if ((len != kp->keylen) || (memcmp(keyid,kp->keyid,len) != 0)) { /* * Length or contents are different */ kp->keylen = len; memcpy(kp->keyid,keyid, len); if (ep->keyid) (void)(*ep->keyid)(dir, kp->keyid, &kp->keylen); ... }