Wednesday, 8 January 2014

OpenSSL - a story about some old source code audit

At the beginning there was nothing... Then some clever mind has decided to start the OpenSSL project and this post is the story about a finding in one of the most hilarious code base which I've ever audited. The project has quite a huge list of historical vulnerabilities (more info), like the (in)famous KEY_ARG flaw exploited widely back in 2002/2003 (exploit code). In this post I would like to focus on some vulnerable code, which is - unfortunately - not exploitable. You can ask yourself why I am writing this ? You can try to answer this question by yourself - maybe it is a good idea to write the very first post about this topic or maybe it is a good idea to proof that nearly a textbook-like vulnerabilities still exist in modern software or maybe... I am leaving this to the readers imagination.

The OpenSSL project supports external libraries to provide additional authentication methods. One of them supports the Kerberos protocol (network authentication protocol based on so-called tickets). The vulnerable resides in ssl/ssl_sess.c and is located around line 787:


if (s->kssl_ctx && !s->kssl_ctx->client_princ &&
    session->krb5_client_princ_len > 0) [1]
{
    s->kssl_ctx->client_princ = (char *)OPENSSL_malloc(session->krb5_client_princ_len + 1); [2]
    memcpy(s->kssl_ctx->client_princ,session->krb5_client_princ, session->krb5_client_princ_len); [3]
    s->kssl_ctx->client_princ[session->krb5_client_princ_len] = '\0'; [4]
}

It should be clear that this code is vulnerable to integer overflow which leads to heap overflow later.

At [1], basic sanity checks are performed to see if the kssl_ctx is not NULL, and if the client principal was not allocated. Also there is a check to validate if the client principal is larger then 0.

At [2], memory is allocated and the integer overflow occurs. krb5_client_princ_len is defined as unsigned int, thus by delivering UINT_MAX OPENSSL_malloc() tries to allocate UINT_MAX + 1 bytes resulting in the integer overflow.

At [3], memcpy() copies UINT_MAX bytes into very small buffer resulting in the heap overflow.

At [4], NULL byte is placed at the end of the newly copied buffer.

Unfortunately, the condition where session->krb5_client_princ_len can be set to UINT_MAX is not possible because of this simple check located in ssl/s3_srvr.c around line 2622:

size_t len = strlen(kssl_ctx->client_princ);
if ( len < SSL_MAX_KRB5_PRINCIPAL_LENGTH )
{
    s->session->krb5_client_princ_len = len;
    memcpy(s->session->krb5_client_princ,kssl_ctx->client_princ,len);
}

Shame. No candies this time.

I reported this issue some time ago, however the OpenSSL development team decided to leave it with no patch.

No comments:

Post a Comment