From 326ba3c0bb6e54473cd80fb9bf22c27780d46018 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 18 Sep 2023 14:08:11 +0200 Subject: [PATCH] mbedtls_ssl_decrypt_buf(): fix buffer overread with stream cipher With stream ciphers, add a check that there's enough room to read a MAC in the record. Without this check, subtracting the MAC length from the data length resulted in an integer underflow, causing the MAC calculation to try reading (SIZE_MAX + 1 - maclen) bytes of input, which is a buffer overread. Signed-off-by: Gilles Peskine --- ChangeLog.d/ssl_decrypt_buf-short_record.txt | 3 +++ library/ssl_msg.c | 13 ++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 ChangeLog.d/ssl_decrypt_buf-short_record.txt diff --git a/ChangeLog.d/ssl_decrypt_buf-short_record.txt b/ChangeLog.d/ssl_decrypt_buf-short_record.txt new file mode 100644 index 000000000..957cec470 --- /dev/null +++ b/ChangeLog.d/ssl_decrypt_buf-short_record.txt @@ -0,0 +1,3 @@ +Security + * Fix a buffer overread when parsing short TLS application data records in + ARC4 or null-cipher cipher suites. Credit to OSS-Fuzz. diff --git a/library/ssl_msg.c b/library/ssl_msg.c index 8a2ab7b9b..4e9cc7ff3 100644 --- a/library/ssl_msg.c +++ b/library/ssl_msg.c @@ -1149,6 +1149,14 @@ int mbedtls_ssl_decrypt_buf(mbedtls_ssl_context const *ssl, #if defined(MBEDTLS_ARC4_C) || defined(MBEDTLS_CIPHER_NULL_CIPHER) if (mode == MBEDTLS_MODE_STREAM) { + if (rec->data_len < transform->maclen) { + MBEDTLS_SSL_DEBUG_MSG(1, + ("Record too short for MAC:" + " %" MBEDTLS_PRINTF_SIZET " < %" MBEDTLS_PRINTF_SIZET, + rec->data_len, transform->maclen)); + return MBEDTLS_ERR_SSL_INVALID_MAC; + } + padlen = 0; if ((ret = mbedtls_cipher_crypt(&transform->cipher_ctx_dec, transform->iv_dec, @@ -1561,7 +1569,7 @@ hmac_failed_etm_enabled: unsigned char mac_expect[MBEDTLS_SSL_MAC_ADD] = { 0 }; unsigned char mac_peer[MBEDTLS_SSL_MAC_ADD] = { 0 }; - /* If the initial value of padlen was such that + /* For CBC+MAC, If the initial value of padlen was such that * data_len < maclen + padlen + 1, then padlen * got reset to 1, and the initial check * data_len >= minlen + maclen + 1 @@ -1573,6 +1581,9 @@ hmac_failed_etm_enabled: * subtracted either padlen + 1 (if the padding was correct) * or 0 (if the padding was incorrect) since then, * hence data_len >= maclen in any case. + * + * For stream ciphers, we checked above that + * data_len >= maclen. */ rec->data_len -= transform->maclen; ssl_extract_add_data_from_record(add_data, &add_data_len, rec,