[Help-gnutls] GnuTLS 1.2.3 and 1.0.25

Regit regit at inl.fr
Tue May 3 10:38:08 CEST 2005

Le jeudi 28 avril 2005 à 13:06 +0200, Simon Josefsson a écrit :

> We do not have the resources to analyze and write an explanation of
> this security problem.  Volunteers who want to read the bug reports
> and the CVS changes, and write up an explanation in plain English, are
> most welcome!  Having a detailed track record of security problems can
> be a useful reference when discussing security in free software
> packages in general.


The problem was discovered by INL when we were studying a crash of
nuauth, a daemon which is part of the NuFW project
(http://www.nufw.org). During stress test we made on our solution, we
open a lot of tls sessions simultaneously (more than 200). After some
times the application crash with a segfault.

I will try to write a detailed track record of this security problem :

The problem occur in the _gnutls_ciphertext2compressed function which is
called to deciphers the ciphertext packet and puts the result to
compressed datas. It is mainly called in _gnutls_decrypt so it has to
directly deal with user entries.

The prototype of the function is as follow :

int _gnutls_ciphertext2compressed(gnutls_session session,
                                        opaque* compress_data, int compress_size,
                                        gnutls_datum ciphertext, uint8 type)

It is important to notice that ciphertext is an entry from the user. The
length of ciphertext (ciphertext.size) is computed by
_gnutls_check_record_headers. It is done by parsing the header of the
recived packet. 

The problem occurs in the 
beginning at line 450.

I copy here under the important part of this code taken from the bug
analysis point of view :

                if ((ciphertext.size < blocksize)
                    || (ciphertext.size % blocksize != 0)) {

* pad is computed with user datas so this can be set with
 any value we want.  
                pad = ciphertext.data[ciphertext.size - 1] + 1; /* pad */

                length =
                    ciphertext.size - hash_size - pad;
* We do a coherence check here. Code use later the pad value as
* a revert array index for ciphertext. So it is good to check
* that we will not exit out of the array when iterating.
* But gnutls_assert is not an exit function so we continue
* as specified in the comment.
                if (pad > ciphertext.size - hash_size) {
                        /* We do not fail here. We check below for the
                         * the pad_failed. If zero means success.
                        pad_failed = GNUTLS_E_DECRYPTION_FAILED;

                /* Check the pading bytes (TLS 1.x)
* Here we are, we enter in the conditionnal even
* if we fail before
                if ( ver >= GNUTLS_TLS1 )
* We iterate over i with i < pad, but as we can have 
* pad > ciphertext.size this lead to a crash :
* If pad > ciphertext.size code does when i = ciphertextsize+1 : 
* 	ciphertext.data[-1]
* So code segfault
                for (i=2;i<pad;i++) {
                        if (ciphertext.data[ciphertext.size-i] != ciphertext.data[ciphertext.size - 1])
                                pad_failed = GNUTLS_E_DECRYPTION_FAILED;

/************** End of code *******************/

To sum up the error was the following : 

code computes value relative to the size of an array with two methods
without taking care that this could lead to out of the array indexes. In
fact, this is a case of badly checked entry from user (even it is really
low level). This bug is quiet severe as it should be easy to forge a
packet where the two values are different.

As gnutls_decrypt is called as soon as the application try to handshake,
the bug can be exploited by unauthenticated users. So theoritically all
applications running gnutls are vulnerable to this distant denial of
service available even for unauthenticated users.

Éric Leblond, eleblond at inl.fr
Téléphone : 01 44 89 46 40, Fax : 01 44 89 45 01
INL, http://www.inl.fr

More information about the Gnutls-help mailing list