# 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  
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  
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.  
Line numbers below are valid for the wolfSSL Git tag  
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`  
/* 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;  
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));  
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  
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.  
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`.  
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))  
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);  
Finally, the server enters the state  
`VerifyServerSuite` while verifying the server suite when processing  
/* 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;  
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`  
int wolfSSL_accept_TLSv13(WOLFSSL* ssl)  
if (ssl->options.serverState ==  
ssl->options.clientState = CLIENT_HELLO_RETRY;  
while (ssl->options.clientState < CLIENT_HELLO_COMPLETE) {  
if ((ssl->error = ProcessReply(ssl)) < 0) {  
ssl->options.acceptState = TLS13_ACCEPT_SECOND_REPLY_DONE;  
### 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.):  
0. Sending an initial genuine Client Hello (`CH1`) to the server and  
then completing a full handshake, thus establishing a PSK.  
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  
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  
300. 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  
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  
We suggest aborting the handshake when receiving `CH2` instead of  
offering the client a retry.