Security
Headlines
HeadlinesLatestCVEs

Headline

wolfSSL Buffer Overflow

In wolfSSL versions prior to 5.5.1, malicious clients can cause a buffer overflow during a resumed TLS 1.3 handshake. If an attacker resumes a previous TLS session by sending a maliciously crafted Client Hello, followed by another maliciously crafted Client Hello. In total 2 Client Hellos have to be sent. One which pretends to resume a previous session and a second one as a response to a Hello Retry Request message.

Packet Storm
#vulnerability#git#rce#buffer_overflow#ssl

wolfssl before 5.5.1: CVE-2022-39173 Buffer overflow when refining

cipher suites

INFO

=======

The CVE project has assigned the id CVE-2022-39173 to this issue.

Severity: high 7.5
Affected version: before 5.5.1
End of embargo: The embargo for this vulnerability ended 29th of September, 2022

SUMMARY

==========

In wolfSSL before 5.5.1 malicious clients can cause a buffer-overflow
during a resumed TLS 1.3 handshake. If an attacker resumes a previous
TLS session by sending a maliciously crafted Client Hello, followed by
another maliciously crafted Client Hello. In total 2 Client Hellos
have to be sent. One which pretends to resume a previous session and a
second one as a response to a Hello Retry Request message.

The malicious Client Hellos contain a list of supported cipher suites,
which contain at least ⌊sqrt(150)⌋ + 1 = 13 duplicates and less than
150 ciphers in total. The buffer-overflow occurs in the RefineSuites
function. An overflow of 44700 bytes has been confirmed. Therefore,
large portions of the stack can get overwritten, including return
addresses.

We confirmed the vulnerability by sending packets over TCP to a
Wolfssl server, freshly built from the sources with the
--enable-session-ticket flags (or simply --enable-all). We can
provide sources for our software (tlspuffin) that produce those
packets (and that automatically found the attack trace). The command
given at the end of this document triggers the buffer overflow.

It is very likely that there is a way to craft an exploit which can
cause a RCE. We have not yet created such an exploit as it would
likely depend on the memory layout of the binary which uses wolfSSL.

Moreover, the size of the overflow can be fine-tuned in order to not
smash the stack and continue the execution with a too large length of
suites buffer and that will cause other routines that iterate over
thus buffer (e.g., FindSuiteSSL) to misbehave. Hypothetically, this
might be exploited to make the server use a cipher it should not
accept such as nullcipher that would open up new attack vectors such
as downgrade attacks.
While this has not been confirmed yet, the buffer overflow itself has
been confirmed.

DETAILS

==========

Line numbers below are valid for the wolfSSL Git tag
v5.4.0-stable.

The bug we found is in the RefineSuites function. In the following
we want to explain why the function is able to overflow the suites
array.

/* Refine list of supported cipher suites to those common to server and client.  
 *  
 * ssl         SSL/TLS object.  
 * peerSuites  The peer's advertised list of supported cipher suites.  
 */  
static void RefineSuites(WOLFSSL* ssl, Suites* peerSuites)  
{  
    byte   suites[WOLFSSL_MAX_SUITE_SZ];  
    word16 suiteSz = 0;  
    word16 i, j;

    XMEMSET(suites, 0, WOLFSSL_MAX_SUITE_SZ);

    for (i = 0; i < ssl->suites->suiteSz; i += 2) {  
        for (j = 0; j < peerSuites->suiteSz; j += 2) {  
            if (ssl->suites->suites[i+0] == peerSuites->suites[j+0] &&  
                ssl->suites->suites[i+1] == peerSuites->suites[j+1]) {  
                suites[suiteSz++] = peerSuites->suites[j+0];  
                suites[suiteSz++] = peerSuites->suites[j+1];  
            }  
        }  
    }

    ssl->suites->suiteSz = suiteSz;  
    XMEMCPY(ssl->suites->suites, &suites, sizeof(suites));  
#ifdef WOLFSSL_DEBUG_TLS  
    [...]  
#endif  
}  

tls13.c:4355

The RefineSuites function expects a WOLFSSL struct which contains
a list of acceptable ciphers suites (ssl->suites->suites), as well
as an array of peer cipher suites (peerSuites). Both inputs are
bounded by WOLFSSL_MAX_SUITE_SZ, which is equal to 300 bytes or 150
cipher suites.

Let us assume that ssl->suites consists of a single cipher suite
like TLS_AES_256_GCM_SHA384 and the peerSuites list contains the
same cipher repeated thirteen times. The RefineSuites function will
iterate for each element in ssl->suites over peerSuites and append
the suite to suites if it is a match. The suites array has a
maximum length of WOLFSSL_MAX_SUITE_SZ == 300 bytes == 150 suites.

With the just mentioned example input, the length of suites will now
equal thirteen. The suites array is now copied to the WOLFSSL
struct in the last line of the listing above. Therefore, ssl->suites
contains now thirteen times the TLS_AES_256_GCM_SHA384 cipher suite.

Let us now call the same RefineSuites function again on the modified
WOLFSSL struct and the same peerSuites list. The RefineSuites
function will iterate for each element in ssl->suites over
peerSuites and append the suite to suites if it is a match.
Because ssl->suites contains already 13 times the
TLS_AES_256_GCM_SHA384 cipher suite, in total 13 x 13 = 169 cipher
suites are written to suites. 169 cipher suites require 338 bytes,
which is more than what’s available on the stack. The suites buffer
overflows.

The maximum size of peerSuites is 150 cipher suites. Therefore, an
overflow of 44700 bytes is possible and has been confirmed.

The buffer ssl->suites->suites is supposed to be reset to only
contain the acceptable ciphers at each session start, and thus
initially contains no duplicate. However, by provoking a HELLO CLIENT RETRY REQUEST, it is possible to make the server call RefineSuites
twice as explained next.

TRIGGERING THE BUFFER OVERFLOW

=================================

In order to cause the above buffer-overflow, it is required to call
RefineSuites twice. Malicious clients need to perform the handshake
in a certain way to reach this situation.
The buffer overflow at the attacked server can be obtained at least in
the following situation:

  1. Resume the previous session by sending a second Client Hello
    (CH2) with the following criteria:

    • Exclude the support_group_extension, to cause a Hello Retry Request
    • Include a binder which cryptographically binds this session to
      the previous one.
    • Include a list of cipher suites that contains a repetition of n
      times the same cipher c with 13 <= n < 150, deemed acceptable by
      the server.

    The server will parse this message, enters the state
    SERVER_HELLO_RETRY_REQUEST_COMPLETE and stores at least n times
    the cipher c in ssl->suites->suites by calling RefineSuites.

  2. Sending a third Client Hello (CH3) with the same criteria as in step 2.
    The server will parse this message and because
    ssl->suites->suites already contains n times the cipher c,
    RefineSuites will write in suites at least until suites[nˆ2]
    which overflows since nˆ2 > 300.

DETAILS ABOUT STEP 1.

========================

During step 2., we want to cause the server to perform a Hello Retry Request.

This is possible by not sending a supported group in the CH2. By not
sending a support group extension, the function
TLSX_SupportedGroups_Find will return false.

static int TLSX_SupportedGroups_Find(WOLFSSL* ssl, word16 name)  
{  
    ...  
        /* Check consistency now - extensions in any order. */  
        if (!TLSX_SupportedGroups_Find(ssl, clientKSE->group))  
            continue;  
    ...  

tls.c:8374

This will cause clientKSE to be NULL and doHelloRetry will be set to 1.

int TLSX_KeyShare_Establish(WOLFSSL *ssl, int* doHelloRetry)  
{  
    ...  
    /* No supported group found - send HelloRetryRequest. */  
    if (clientKSE == NULL) {  
        /* Set KEY_SHARE_ERROR to indicate HelloRetryRequest required. */  
        *doHelloRetry = 1;  
        return TLSX_KeyShare_SetSupported(ssl);  
    }  
    ...  

tls.c:9273

Finally, the server enters the state
SERVER_HELLO_RETRY_REQUEST_COMPLETE in the function
VerifyServerSuite while verifying the server suite when processing
CH2.

    /* Make sure server cert/key are valid for this suite, true on success  
     * Returns 1 for valid server suite or 0 if not found  
     * For asynchronous this can return WC_PENDING_E  
     */  
    static int VerifyServerSuite(WOLFSSL* ssl, word16 idx)  
    {  
        ...  
        if (IsAtLeastTLSv1_3(ssl->version) &&  
                                      ssl->options.side == WOLFSSL_SERVER_END) {  
            int doHelloRetry = 0;  
            /* Try to establish a key share. */  
            int ret = TLSX_KeyShare_Establish(ssl, &doHelloRetry);  
            if (doHelloRetry) {  
                ssl->options.serverState = SERVER_HELLO_RETRY_REQUEST_COMPLETE;  
            }

            ...  
        }  
        ...  

tls.c:30688

DETAILS ABOUT STEP 2.

====================

The server is now in a state in which it expects another Client Hello
(CH3) from the client.

The server is now in the state SERVER_HELLO_RETRY_REQUEST_COMPLETE
and will process the third ClientHello (CH3) with the call of
ProcessReply before reaching the TLS13_ACCEPT_SECOND_REPLY_DONE
state.

int wolfSSL_accept_TLSv13(WOLFSSL* ssl)  
{  
    ...  
        case TLS13_ACCEPT_FIRST_REPLY_DONE :  
            if (ssl->options.serverState ==  
                                          SERVER_HELLO_RETRY_REQUEST_COMPLETE) {  
                ssl->options.clientState = CLIENT_HELLO_RETRY;  
                while (ssl->options.clientState < CLIENT_HELLO_COMPLETE) {  
                    if ((ssl->error = ProcessReply(ssl)) < 0) {  
                        WOLFSSL_ERROR(ssl->error);  
                        return WOLFSSL_FATAL_ERROR;  
                    }  
                }  
            }

            ssl->options.acceptState = TLS13_ACCEPT_SECOND_REPLY_DONE;  
            WOLFSSL_MSG("accept state ACCEPT_SECOND_REPLY_DONE");  
            FALL_THROUGH;  
    ...  

tls13.c:10909

VULNERABILITY VARIANTS

=========================

Adjusting the overflow

Note that the length of the list of ciphers in CH2 does not
necessarily have to be the same as the one of CH1 and can be
adjusted to fine-tune the size of the overflow.

Make the server parse CH2

Note also that, on the contrary to CH1, CH2 does not necessarily
have to put the server in the SERVER_HELLO_RETRY_REQUEST_COMPLETE
state (which should be forbidden by the TLS 1.3 RFC) or make it return
an error, and can thus contain a supported group, which could be
included to possibly make the server continue the processing of CH2
without returning an error.
We have confirmed that we can make the server parses CH2 until the
end and starts computing a Server Hello with ssl->suites->suiteSz
that exceeds 300.

Resuming an existent session

It is also possible to trigger the vulnerability by trying to resume
an existent and genuine session established through a full initial
handshake (step 0.):

  1. Sending an initial genuine Client Hello (CH1) to the server and
    then completing a full handshake, thus establishing a PSK.

EXPLOITATION

===============

We suspect that it is possible to craft an exploit which could lead to
RCE if any of the above bytes coincides with the memory address of
executable code. Depending on the memory layout of the binary it could
be possible to gain RCE.
More bytes could be used to overflow suites if more ciphers were
configured to be accepted with the server, e.g., with options like
--enable-blake2.

We confirmed that this could also be exploited to smash the stack and
cause the server to crash with a segmentation fault by using a large
list of ciphers.

Finally, by fine-tuning the length of the overflow and by including
the supported group in CH3, it could be possible to make the server
process CH3 with a ssl->suites->suites->suiteSz value that exceeds

  1. This way, routines like FindSuiteSSL that will iterate over
    ssl->suites->suites (allocated on 300 bytes) until
    ssl->suites->suiteSz (>300) will also iterate over bytes that
    contain other fields such as ssl->suites->hashSigAlgo. It is likely
    that this could be exploited to make such routines return arbitrary
    values. For example, it might be exploited to make the server use a
    cipher it should not accept such as nullcipher; thus breaking
    confidentiality.

FURTHER CONCERNS

==================

We observed that the server is accepting the CH2 Client Hello
message and issues a Hello Retry Request, even though CH2 does not
contain supported groups. Clients are not allowed to add the supported
groups extension in the retry Client Hello (CH3) according to the
RFC 8446 in section
4.1.2. The
addition of supported groups is not allowed when retrying the Client
Hello.
We suggest aborting the handshake when receiving CH2 instead of
offering the client a retry.

Packet Storm: Latest News

WordPress Really Simple Security Authentication Bypass