Details on the gnutls_handshake local crash problem [GNUTLS-SA-2008-2]

Simon Josefsson simon at josefsson.org
Mon Jun 30 23:42:18 CEST 2008


Below is my analysis of the problem.  The patch is short:

>From 0fee3917077e191dea3c9787c95c072979532086 Mon Sep 17 00:00:00 2001
From: Simon Josefsson <simon at josefsson.org>
Date: Mon, 30 Jun 2008 22:44:47 +0200
Subject: [PATCH] (_gnutls_handshake_hash_buffers_clear): Make sure deinitialized MAC hashes are initialized.
 Report and tiny patch from Tomas Mraz <tmraz at redhat.com>.

---
 lib/gnutls_handshake.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c
index d798180..0192c9f 100644
--- a/lib/gnutls_handshake.c
+++ b/lib/gnutls_handshake.c
@@ -69,11 +69,12 @@ int _gnutls_server_select_comp_method (gnutls_session_t session,
 
 /* Clears the handshake hash buffers and handles.
  */
-inline static void
+static void
 _gnutls_handshake_hash_buffers_clear (gnutls_session_t session)
 {
   _gnutls_hash_deinit (&session->internals.handshake_mac_handle_md5, NULL);
   _gnutls_hash_deinit (&session->internals.handshake_mac_handle_sha, NULL);
+  session->internals.handshake_mac_handle_init = 0;
   _gnutls_handshake_buffer_clear (session);
 }
 
-- 
1.5.6

/Simon

I have received a report against gnutls v2.4.0 which triggers a local
segmentation fault when any application calls gnutls_handshake() for an
already valid session.  This is valid but not common usage.

Btw, earlier stable releases before v2.4.0 are not affected.  The
problem was introduced in v2.3.5 in the first @@-section of:

http://git.savannah.gnu.org/gitweb/?p=gnutls.git;a=commitdiff;h=3aea821a6ce36bd14f2a3a41598db698d031fadc#patch5

How to reproduce:

1. download ftp://ftp.gnutls.org/pub/gnutls/gnutls-2.4.0.tar.bz2
   you'll need libgpg-error/libgcrypt installed if you do not
   have it already
2. build it: ./configure && make
3. download
   'http://git.savannah.gnu.org/gitweb/?p=gnutls.git;a=blob_plain;f=tests/mini.c;h=HEAD;hb=HEAD'
   into tests/mini.c,
   the latest code triggers the behaviour.
4. run cd tests; make mini;./mini

Detailed analysis:

The code ends up invoking gcry_md_write() on a handle that points to a
free()'d structure.  Since the pointer has been free()'d, the pointer
could point to anything at the time when the code is invoked.  An
attacker can't control where the pointer points to, but an attacker
could have sent data into a buffer that is in the victims memory.  If
the pointer happens to point to the data buffer sent by the attacker,
the attacker may be able to control execution.

Libgcrypt's code is:

http://cvs.gnupg.org/cgi-bin/viewcvs.cgi/trunk/cipher/md.c?root=Libgcrypt&view=auto

static void
md_write (gcry_md_hd_t a, const void *inbuf, size_t inlen)
{
  GcryDigestEntry *r;
  
  if (a->ctx->debug)
    {
      if (a->bufpos && fwrite (a->buf, a->bufpos, 1, a->ctx->debug) != 1)
	BUG();
      if (inlen && fwrite (inbuf, inlen, 1, a->ctx->debug) != 1)
	BUG();
    }

  for (r = a->ctx->list; r; r = r->next)
    {
      if (a->bufpos)
	(*r->digest->write) (&r->context.c, a->buf, a->bufpos);
      (*r->digest->write) (&r->context.c, inbuf, inlen);
    }
  a->bufpos = 0;
}

For me, the crash happens because a->ctx is still valid but
a->ctx->debug is garbage, so it crashes in the fwrite, writing to a bad
file descriptor.

Given that the code de-reference a->ctx->list or a->buf early, which is
typically garbage, normally the code will crash before using any of the
inbuf/inlen data.

The inbuf/inlen data is normally not under attacker control, it is
generated by the local implementation to be a TLS handshake packet.
However, some elements of the TLS handshake packets may have been
influenced by the other side during the first handshake, so the
conservative approach would be to treat it as tainted.

To turn this into an exploit, I believe the attacker needs to cause some
specific data to be located where the free()'d pointer 'a' points.  To
do this reliable, you need to predict where the 'a' pointer will point
to and to put some custom data at that memory address.  Once that is
done, you could make the first fwrite() in the function above write some
data to an already open file descriptor.  Of course, the attacker needs
to know the address of the file descriptor, and make that address be
part of the data sent to to the victim.

However, causing custom data to be placed at the point where an earlier
free()'d pointer points to appears difficult to do on normal platforms
where you typically don't know the heap memory addresses.





More information about the Gnutls-devel mailing list