Below is my analysis of the problem. The patch is short:
From 0fee3917077e191dea3c9787c95c072979532086 Mon Sep 17 00:00:00 2001
From: Simon Josefsson
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 .
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
@@ -69,11 +69,12 @@ int _gnutls_server_select_comp_method (gnutls_session_t
/* Clears the handshake hash buffers and handles.
-inline static void
_gnutls_handshake_hash_buffers_clear (gnutls_session_t session)
+ session->internals.handshake_mac_handle_init = 0;
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:
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
the latest code triggers the behaviour.
4. run cd tests; make mini;./mini
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:
md_write (gcry_md_hd_t a, const void *inbuf, size_t inlen)
if (a->bufpos && fwrite (a->buf, a->bufpos, 1, a->ctx->debug) != 1)
if (inlen && fwrite (inbuf, inlen, 1, a->ctx->debug) != 1)
for (r = a->ctx->list; r; r = r->next)
(*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
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
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.