CERT-FI Vulnerability Advisory on GnuTLS
Advisory Reference FICORA #130447
Release Date 19 May 2008 12:00 UTC
Last Revision 19 May 2008
Version Number 1.0
Vulnerabilities were discovered by Ossi Herrala and Jukka Taimisto from the CROSS project
at Codenomicon Ltd., and reported directly to the vendor through CERT-FI.
What is Affected?
The vulnerabilities described in this advisory affect GnuTLS prior to version 2.2.4.
The impact from vulnerabilities can expose Denial-of-Service (DoS) and buffer overflow
conditions. It may even be possible for an attacker to execute code on the affected system.
Three programming errors leading to segmentation fault were found in the gnutls-serv
program, triggered by the following TLS messages:
1. Sending TLS record containing multiple Client Hello handshake messages.
2. Sending TLS 1.0 Client Hello message which contains specifically crafted server name extension.
3. Doing complete TLS 1.0 handshake followed by a encrypted Client-Hello message with a Record Length of 8.
1. The problem occurs when gnutls-serv receives TLS message which contains multiple Client Hello
messages. The program reads the first Client Hello and then proceeds to send Server Hello,
Certificate, Certificate Request and Server Hello Done messages. After sending these, it apperently
reads next Client Hello from the message sent earlier and crashes to segmentation fault caused by
a NULL pointer.
The error occurs when _gnutls_recv_client_kx_message() (in lib/gnutls_kx.c) is called. This function
calls _gnutls_recv_handshake() which in turn reads the next Client Hello packet and ends up
returning 0 and sets the data pointer to NULL. _gnutls_recv_client_kx_message() does not check
the variable data for NULL before calling the key exchange handling function, in this case
_gnutls_proc_rsa_client_kx() (in lib/auth_rsa.c). _gnutls_proc_rsa_client_kx() function does not
check the data pointer for NULL and proceeds to call _gnutls_read_uint16() with a NULL pointer,
causing a segmentation fault.
2. The problem occurs when gnutls-serv receives Client Hello message which contains Server
name extension where the length of the server name list is set to 18 and the data of the Server
Same list consists of 18 zero octets. After receiving this Client Hello message gnutls-serv continues
to function normally, but crashes after Finished messages when it tries to pack the session data in
the function pack_security_parameters() (in lib/gnutls_session_pack.c).
However the bug causing the crash is in the extension handling, in
_gnutls_server_name_recv_params() (lib/ext_server_name.c). The function tries to calculate the
number of Server Names in the packet (lines 70-80), but does not check if the length of Server
Name is 0. This causes the function to interpret the 18 zero octets as containing 6 Server Names.
The more serious bug follows when, on line 83, the number of server names is saved to
session->security_parameters.extensions.server_names_size. Then the number of Server Sames
is checked against the maximum number of Server Names supported (3) but the value saved to
the struct is never corrected. Hence after the extensions are interpreted, the
session->security_parameters.extensions.server_names_size contains the illegal value 6.
This causes the crash later on when in pack_security_parameters() the
session->security_parameters.extensions.server_names_size is used in the loop where the
Server Names are copied (gnutls_session_pack.c, lines 1074 - 1090). Since there is only space
for 3 Server Names, the loop iterates well over the boundary and results in a segmentation fault.
3. The problem occurs in function _gnutls_ciphertext2compressed() (in lib/gnutls_cipher.c). The
parameter ciphertext contains the start of the received message in ciphertext.data and the
ciphertext.size contains value 8 (taken from the Record Length field of the received TLS record).
Now, since block cipher is being used, after the 8 bytes of data has been decrypted the
_gnutls_ciphertext2compressed() proceeds to read the length of the padding bytes in line 505:
pad = ciphertext.data[ciphertext.size - 1] + 1; /* pad */
Since the ciphertext.size is 8, the pad byte is read from the Random field in the Client Hello
message (as stated in the introduction, the flaw was found by sending encrypted Client Hello
message in a TLS Record containig invalid Record Length field) and contains value 0xf0
(240 in decimal) and the pad variable is set to 241.
In line 507, the length of the data is calculated:
length = ciphertext.size - hash_size - pad;
Since, pad is 241, hash_size 20 (SHA1 is used) and ciphertext.size is 8, the length is set
to a negative value. This is not checked immediately.
In line 509, a check is made to make sure padding length is not invalid:
if (pad > ciphertext.size - hash_size)
Due the data types used in the comparison (pad is uin8_t, ciphertext.size is unsigned int and
hash_size is int) the invalid pad length is not catched by this check, instead the comparison is
false leaving the pad_failed to value 0 and the execution proceeds to the next statement in
line 520. The check in line 520 passes and the program proceeds to do the padding check.
The for loop:
for (i = 2; i < pad; i++)
if (ciphertext.data[ciphertext.size - i] !=
ciphertext.data[ciphertext.size - 1])
pad_failed = GNUTLS_E_DECRYPTION_FAILED;
loops the i from 2 to 241, resulting in a segmentation fault.
Patch the affected software with the patches supplied by the vendor.
The issue is covered in the advisory SA-2008-01.
CERT-FI would like to thank Codenomicon for providing the vulnerability information and Simon Josefsson of GnuTLS for his co-operation.
CERT-FI Vulnerability Coordination can be contacted as follows:
Please quote the advisory reference in the subject line
+358 9 6966 510
Monday - Friday 08:00 - 16:15 (EET: UTC+2)
+358 9 6966 515
P.O. Box 313
CERT-FI encourages those who wish to communicate via email to make use of our PGP key. The key is available at https://www.cert.fi/en/activities/contact/pgp-keys.htm