CBC Encrypt Out-of-Bounds Read/Write on Sub-Block Length
CVSS v4.0
Score: 7.7 (High)
Vector: CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:H/VA:H/SC:N/SI:N/SA:N
- AV:N — The library is commonly used in network-facing applications processing encrypted protocol data.
- AC:L — No special conditions required beyond calling the function with a sub-block length.
- AT:P — The attacker needs the calling application to pass sub-block-length data to the encrypt function, which requires the application to handle variable-length inputs without pre-padding.
- PR:N — No privileges required; the attacker influences input data length through normal application interaction.
- UI:N — No user interaction needed.
- VC:L — Up to 15 bytes of adjacent memory are leaked through the encrypted output (information disclosure).
- VI:H — Up to 15 bytes of adjacent memory are corrupted (XORed with IV and AES-encrypted in-place), destroying integrity of adjacent data structures.
- VA:H — Reliable crash/denial of service when heap protections detect corruption; silent corruption in unprotected environments.
CWE
CWE-787: Out-of-bounds Write
Affected Files
aes.c:505— Loop conditionfor (i = 0; i < length; i += AES_BLOCKLEN)enters the loop body for any length > 0aes.c:507-508—XorWithIvandCipherprocess a full 16-byte block regardless of actual buffer size
Description
When AES_CBC_encrypt_buffer() is called with a length value between 1 and 15 (inclusive), the loop condition i < length evaluates to true (since i starts at 0 and 0 < length for any positive length). The function then processes a full 16-byte block through XorWithIv() and Cipher(), reading and writing 16 bytes from a buffer that may be as small as 1 byte.
This is a specific and particularly dangerous case of the non-block-aligned length vulnerability. Unlike the general case where the overflow is at the end of a multi-block message, here the very first (and only) block operation overflows from the start. If the caller provides a buffer of length bytes:
XorWithIv()at line 507 reads and writes bytesbuf[0]throughbuf[15], accessing16 - lengthbytes beyond the bufferCipher()at line 508 reads and writes the full 16-bytestate_t, corrupting the same out-of-bounds regionmemcpy(ctx->Iv, Iv, AES_BLOCKLEN)at line 513 may copy from the corrupted buffer region
For a caller with a 1-byte buffer, 15 bytes of adjacent memory are read, encrypted, and written back — a combined read and write overflow of 15 bytes.
This exact scenario can occur when a caller tries to encrypt a final partial block, or when processing variable-length protocol messages that happen to be shorter than 16 bytes.
Reproduction Steps
#include <stdio.h>
#include <string.h>
#include "aes.h"
int main() {
struct AES_ctx ctx;
uint8_t key[16] = {0};
uint8_t iv[16] = {0};
// Only 5 bytes of actual data
uint8_t small_buf[5] = {'H', 'e', 'l', 'l', 'o'};
AES_init_ctx_iv(&ctx, key, iv);
// This will read/write 16 bytes starting at small_buf,
// overflowing 11 bytes past the end
AES_CBC_encrypt_buffer(&ctx, small_buf, 5);
return 0;
}
Compile with AddressSanitizer: gcc -fsanitize=address -o test_small test_small.c aes.c
ASan will report a stack-buffer-overflow on the XorWithIv or Cipher call.
Impact
When a caller passes a buffer smaller than 16 bytes with a non-zero length, up to 15 bytes of adjacent memory are corrupted (read, XORed, AES-encrypted, and written back). This can:
- Crash the application (denial of service)
- Corrupt adjacent stack variables, local data, or heap metadata
- Potentially enable code execution if return addresses or function pointers are overwritten
- Leak adjacent memory contents through the encrypted output (information disclosure)
Suggested Fix
Add a bounds check at the start of AES_CBC_encrypt_buffer():
void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length)
{
if (length < AES_BLOCKLEN) return; // Reject sub-block inputs
// Or: length &= ~(AES_BLOCKLEN - 1); // Truncate to block boundary
// ... rest of function
}