============================================================================================================================================= | # Title : ESSIV AEAD Signed-to-Unsigned Validation Bypass in Linux Kernel | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) | | # Vendor : https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=248ff2797ff52a8cbf86507f9583437443bf7685 | ============================================================================================================================================= [+] Summary : a validation bypass in the ESSIV AEAD implementation of the Linux kernel. The vulnerability occurs when the assoclen field (unsigned int) is subtracted by the IV size without proper validation. In certain paths (in-place operations or decryption), negative results are cast to unsigned, producing extremely large memory copy lengths. This can lead to: Kernel panic Memory corruption System hang (DoS) [+] Affected Versions: Any Linux kernel before the patch that added early validation: if (ssize < 0) return -EINVAL; Patched Versions: All kernels where this check is applied at the start of essiv_aead_crypt(), preventing negative-length conversions and unsafe memory operations. Impact: Denial of Service (DoS) and potential memory corruption due to signed-to-unsigned conversion in unsafe paths. The following code only illustrates the theoretical mechanism and does not represent an effective exploit because it requires a special driver or syscall that uses essiv insecurely and allows the user to specify assoclen. In a real kernel, these values are usually defined by the encryption protocol itself. [+] POC : #include #include #include #include #include struct aead_request { unsigned int assoclen; unsigned char iv[16]; void *src; void *dst; unsigned int cryptlen; bool is_encrypt; }; enum kernel_result { KERNEL_SUCCESS, KERNEL_PANIC, KERNEL_MEMORY_CORRUPTION, KERNEL_SILENT_FAILURE }; enum kernel_result essiv_aead_crypt_vulnerable(struct aead_request *req) { unsigned int ivsize = 16; // IV size for encryption int ssize; // signed int (after promotion) printf("\n[ VULNERABLE VERSION - PRE-PATCH]\n"); printf("------------------------------------\n"); printf("req->assoclen: %u (0x%08x)\n", req->assoclen, req->assoclen); printf("ivsize: %u\n", ivsize); ssize = req->assoclen - ivsize; printf("ssize = assoclen - ivsize = %d (0x%08x)\n", ssize, ssize); if (req->src == req->dst || !req->is_encrypt) { printf("\n[*] Entering vulnerable path (in-place or decryption)\n"); printf(" NO ssize check performed!\n"); unsigned int len_to_copy = (unsigned int)ssize; printf(" len_to_copy (unsigned cast): %u (0x%08x)\n", len_to_copy, len_to_copy); if (len_to_copy > 1024 * 1024) { printf(" [] LARGE COPY DETECTED: %u bytes\n", len_to_copy); if (len_to_copy == 0xFFFFFFF8) { // -8 as unsigned printf(" [] This is the exact CVE-2025-40019 scenario!\n"); printf(" [] Kernel will attempt to copy 4GB-8 bytes\n"); printf("\n [] POSSIBLE OUTCOMES:\n"); printf(" • Kernel panic (if unmapped memory accessed)\n"); printf(" • Memory corruption (if memory is mapped)\n"); printf(" • System hang (if copy loops indefinitely)\n"); return KERNEL_PANIC; } } } return KERNEL_SUCCESS; } enum kernel_result essiv_aead_crypt_patched(struct aead_request *req) { unsigned int ivsize = 16; int ssize; printf("\n[PATCHED VERSION - POST-PATCH]\n"); printf("----------------------------------\n"); printf("req->assoclen: %u (0x%08x)\n", req->assoclen, req->assoclen); printf("ivsize: %u\n", ivsize); // Calculating ssize (same operation) ssize = req->assoclen - ivsize; printf("ssize = assoclen - ivsize = %d (0x%08x)\n", ssize, ssize); printf("\n[*] Early validation check:\n"); if (ssize < 0) { printf(" ssize < 0 detected! Returning -EINVAL\n"); printf(" [] Attack blocked\n"); return KERNEL_SUCCESS; // In reality, this would return -EINVAL } printf(" ssize >= 0, continuing safely\n"); return KERNEL_SUCCESS; } void test_scenario(unsigned int assoclen, bool in_place, bool is_encrypt) { struct aead_request req = { .assoclen = assoclen, .src = malloc(1024), .dst = NULL, .is_encrypt = is_encrypt }; req.dst = in_place ? req.src : malloc(1024); printf("\n=========================================="); printf("\n TEST SCENARIO:"); printf("\n assoclen = %u", assoclen); printf("\n in-place = %s", in_place ? "yes" : "no"); printf("\n operation = %s", is_encrypt ? "encrypt" : "decrypt"); printf("\n=========================================="); essiv_aead_crypt_vulnerable(&req); essiv_aead_crypt_patched(&req); free(req.src); if (!in_place && req.dst) free(req.dst); } int main() { printf("╔════════════════════════════════════════════════╗\n"); printf("║ CVE-2025-40019 - ESSIV Validation Bypass ║\n"); printf("║ Improved Demonstration (with integer promotion)║\n"); printf("╚════════════════════════════════════════════════╝\n"); printf("\n📌 KEY INSIGHTS FROM CODE REVIEW:\n"); printf(" • assoclen is unsigned int (32-bit) in real kernel\n"); printf(" • Integer promotion to int happens before subtraction\n"); printf(" • Negative ssize becomes large unsigned when cast\n"); printf(" • CVE-2025-40019: missing check in specific paths\n"); test_scenario(8, true, false); test_scenario(5, false, false); test_scenario(32, true, true); test_scenario(15, true, false); printf("\n\n SUMMARY:\n"); printf(" • Vulnerability: ssize validation was missing in certain paths\n"); printf(" • Cause: A negative value converts to a very large unsigned value\n"); printf(" • Consequence: DoS (Kernel Panic) or memory corruption\n"); printf(" • Fix: Moved validation to the start of the function\n"); return 0; } Greetings to :============================================================================== jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)| ============================================================================================