xorl %eax, %eax

CVE-2009-4355: OpenSSL zlib_stateful_finish() Remote Memory Leak DoS

leave a comment »

I was requested by a reader via email to write about this vulnerability, so here is my post…
So, first of all the bug affects OpenSSL 0.9.8l and earlier and 1.0.0 Beta through Beta 4 as we can read in the CVE-2009-4355 name assigned. The vulnerability was discovered by a user and it was initially reported in rPath’s issue tracking system which is available here. Here is the buggy code as seen in 0.9.8l version of OpenSSL…

static void zlib_stateful_finish(COMP_CTX *ctx)
	{
	CRYPTO_free_ex_data(CRYPTO_EX_INDEX_COMP,ctx,&ctx->ex_data);
	}

This simple wrapper function resides in crypto/comp/c_zlib.c and it’s used to free the allocated space for the compressed context (represented by ‘COMP_CTX’) using CRYPTO_free_ex_data() which can be found at crypto/ex_data.c like this:

/* The implementation we use at run-time */
static const CRYPTO_EX_DATA_IMPL *impl = NULL;

/* To call "impl" functions, use this macro rather than referring to 'impl' directly, eg.
 * EX_IMPL(get_new_index)(...); */
#define EX_IMPL(a) impl->cb_##a
     ...
/* Internal function that checks whether "impl" is set and if not, sets it to
 * the default. */
static void impl_check(void)
	{
	CRYPTO_w_lock(CRYPTO_LOCK_EX_DATA);
	if(!impl)
		impl = &impl_default;
	CRYPTO_w_unlock(CRYPTO_LOCK_EX_DATA);
	}
/* A macro wrapper for impl_check that first uses a non-locked test before
 * invoking the function (which checks again inside a lock). */
#define IMPL_CHECK if(!impl) impl_check();
     ...
/* Cleanup a CRYPTO_EX_DATA variable - including calling free() callbacks for
 * each index in the class used by this variable */
void CRYPTO_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad)
	{
	IMPL_CHECK
	EX_IMPL(free_ex_data)(class_index, obj, ad);
	}

Here you can read that it’ll perform an unimportant to us implementation check and then use the callback free_ex_data() (depending on the implementation specified earlier) to free the passed objects. So, as Joe Orton discovered and reported in Red Hat’s bug tracker

the last time it returns after a graceful, the ex_idx is set to 4.

When using gdb to intercept the brk call as above, the ex_idx in the child
process is set to zero.  This matches the root cause in bug 447268 - the
zlib_stateful_free_ex_data callback won't get called on the ex_data because the
index doesn't match.  We've seen this type of issue before when libcrypto.so
gets reloaded from the process and static data is reinitialized.

Since the required callback won’t be called the allocated space will never get freed. To fix this, zlib_stateful_free_ex_data() was completely removed from crypto/comp/c_zlib.c as we can read in the published patch, and zlib_stateful_finish() was changed to include zlib_stateful_free_ex_data() code like this:

 static void zlib_stateful_finish(COMP_CTX *ctx)
 	{
+	struct zlib_state *state =
+		(struct zlib_state *)CRYPTO_get_ex_data(&ctx->ex_data,
+			zlib_stateful_ex_idx);
+	inflateEnd(&state->istream);
+	deflateEnd(&state->ostream);
+	OPENSSL_free(state);
 	CRYPTO_free_ex_data(CRYPTO_EX_INDEX_COMP,ctx,&ctx->ex_data);
 	}

In addition, the main zlib compression routine COMP_zlib() was updated to remove the initialization of zlib_stateful_free_ex_data() since it no longer exists like this:

 			if (zlib_stateful_ex_idx == -1)
 				zlib_stateful_ex_idx =
 					CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_COMP,
-						0,NULL,NULL,NULL,zlib_stateful_free_ex_data);
+						0,NULL,NULL,NULL,NULL);
 			CRYPTO_w_unlock(CRYPTO_LOCK_COMP);

This by itself is not a security bug but under circumstances which include having Apache web server running with multithreading support, having mod_php installed with SSLv3 enabled and a SIGUSR1 is required to be send in order to trigger a graceful restart as Michael K Johnson pointed out.
Then, Andy Grimm made another discovery. The aforementioned developers were using the following algorithm to reproduce the bug:

php-5.1.6-23.2.el5_3
httpd-2.2.3-31.el5_4.2
mod_ssl-2.2.3-31.el5_4.2
openssl-0.9.8e-12.el5

1. Install mentioned components
2. service httpd start
3. note that memory utilization is stable for httpd
4. start a request loop:
   while :; do curl https://localhost/ -3 --insecure >/dev/null 2>&1; done
5. service httpd graceful

But Andy Grimm discovered that mod_php was calling an SSL cleaning routine from cURL library which was this:

void Curl_ossl_cleanup(void)
{
  /* Free the SSL error strings */
  ERR_free_strings();

  /* EVP_cleanup() removes all ciphers and digests from the
     table. */
  EVP_cleanup();

#ifdef HAVE_ENGINE_cleanup
  ENGINE_cleanup();
#endif

#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
  /* this function was not present in 0.9.6b, but was added sometimes
     later */
  CRYPTO_cleanup_all_ex_data();
#endif
}

So, in case of ‘HAVE_CRYPTO_CLEANUP_ALL_EX_DATA’ enabled cURL library the previously mentioned OpenSSL routine will be invoked. Of course, the above OpenSSL patch fixes this bug since it removes that function but cURL should also be updated to remove this call:

 #endif
-
-#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
-  /* this function was not present in 0.9.6b, but was added sometimes
-     later */
-  CRYPTO_cleanup_all_ex_data();
-#endif
 }

Since this is no longer available from OpenSSL. I don’t think that this is an important vulnerability since there are many constraints that have to be met in order to be able to perform a remote DoS because of memory consumption. There are obviously easier ways to perform much more reliable remote DoS.

P.S.: Dear reader, my apologies for the delayed post but I almost forgot it and since I was having a look at a forensic challenge :P

Written by xorl

January 19, 2010 at 19:06

Posted in bugs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s