Share
## https://sploitus.com/exploit?id=D789A432-2137-57A8-BD99-560ABE0F7D40
# CVE-2022-2602

## Target
Elevation of privilege: tạo một account mới với đặc quyền **root** bằng cách ghi vào file **"/etc/passwd"** (must be read-only with normal user)

## Root cause
Khi userland đưa ra yêu cầu `OP_WRITEV` tới kernel thông qua `io_uring` API, nó sẽ yêu cầu dữ liệu được ghi vào `file discriptor` bằng cách sử dụng `struct iovec`. (ví dụ về [writev](/io_uring/writev.c))

Trong lúc kernel thread đang thực thi lệnh `OP_WRITEV` thì "bằng cách nào đó" nó dừng lại (ta sẽ tìm hiểu cách để pause kernel thread sau), đồng thời bên userland ta tìm cách free và thay thế file ban đầu (Read/Write) bởi file ("/etc/passwd" - only Read) ta muốn ghi vào.

Việc thay thế (hoặc free) một file trong khi kernel thread vẫn còn đang sử dụng (đang chờ để tiếp tục thao tác ghi) dẫn đến lỗi UAF.

### How to pause kernel thread.
Một trong những cách có thể dùng là sử dụng cơ chế `userfaultfd`:
- Tiến trình sử dụng `userfaultfd` để đăng ký một khu vực nhớ (memory range) và chỉ định mục đích của khu vực đó.
- Khi kernel cố gắng truy cập đến một trang (page) trong khu vực đó mà chưa được phân bổ trên thực tế (zero-page), nó sẽ không có sẵn trong bộ nhớ RAM và CPU sẽ tạo ra một interrupt.
- Kernel sẽ gửi một sự kiện (event) tới user-space thông qua file descriptor của userfaultfd, mà tiến trình đã đăng ký. Khi tiến trình nhận được sự kiện, nó sẽ phản hồi bằng cách thực hiện một số hành động, chẳng hạn như cấp phát bộ nhớ, đọc dữ liệu từ disk hoặc đọc dữ liệu từ cache để điền vào vùng nhớ được đăng ký.
- Sau khi user-space xử lý xong, nó sẽ phản hồi lại kernel thông qua userfaultfd, kernel sẽ cập nhật trang được đọc vào trong bộ nhớ vật lý và tiếp tục thực thi.

but how to trigger page fault in io_uring context???

### How to free and replace file when kernel thread is paused.
[unix_gc garbage collector](https://th3-5had0w.github.io/2023/01/21/unix-garbage-collector/)

## io_uring
io_uring là cơ chế I/O mới được giới thiệu trong phiên bản kernel Linux 5.1 (phiên bản trước đó dùng POSIX AIO - Asynchronous I/O), nó sinh ra để giải quyết các vấn đề của AIO như:
- AIO không hỗ trợ I/O buffered, chỉ hỗ trợ I/O trực tiếp. (các process sẽ đọc/ghi trực tiếp từ I/O device mà không thông qua bộ nhớ đệm).
- Hiệu suất của AIO kém, phải chờ đợi kết quả của từng I/O request trước khi có thể tiếp tục xử lý I/O request tiếp theo.

<p align="center">
    <img src="io_uring/io_uring.png">
</p>

### Communication Channel

io_uring bao gồm hai kênh giao tiếp cơ bản là:
- **Submission Queue (SQ)**: chứa các yêu cầu I/O (biểu diễn dưới dạng **submission queue entry - sqe**: `struct io_uring_sqe`) được ứng dụng gửi yêu cầu tới cho kernel thực hiện.
- **Completion Queue (CQ)** kernel sau khi nhận được yêu cầu I/O (lấy từ **SQ->head**) và xử lý xong thì sẽ trả về kết quả dưới dạng **completion queue entry - sqe** (`struct io_uring_sqe`), thêm vào **CQ->tail**.

**Lưu ý:** **SQE** luôn được thêm vào cuối hàng đợi **SQ** còn **CQE** có thể được thêm vào **CQ** theo thứ tự bất kỳ (tùy thuộc vào I/O nào xử lý xong trước), do đó để ứng dụng có thể lấy chính xác kết quả **CQ** tương ứng với **SQ** mà nó yêu cầu bằng cách thông qua giá trị trong trường `user_data`:

<p align="center">
    <img src="io_uring/sqe_cqe.png">
</p>

### Syscall API
#### io_uring_setup
Dùng để khởi tạo và cấu hình cơ chế I/O uring

```c
int io_uring_setup(u32 entries, struct io_uring_params *p);
-> return ring file discriptor
```
trong đó:
- `u32 entries`: số entry tối thiểu của **SQ** và **CQ**.
- `struct io_uring_params *p`: bao gồm các thông tin dùng để config io_uring.

#### io_uring_register
- io_uring_register được sử dụng để đăng ký các tài nguyên I/O trên io_uring context, bao gồm file descriptor, buffer và hoạt động I/O.
- Nó cho phép kernel thực hiện nhiều thao tác I/O đồng thời trên nhiều tài nguyên đã được đăng ký mà không cần phải bắt đầu từ đầu cho mỗi thao tác
- Ví dụ, việc đăng ký một file descriptor cho io_uring context sẽ cho phép ứng dụng thực hiện các thao tác I/O trên file đó mà không cần phải mở file mỗi lần thực hiện một thao tác I/O. Thay vào đó, tài nguyên được đăng ký với io_uring context và duy trì bởi kernel, cho phép ứng dụng thực hiện các thao tác I/O nhanh chóng hơn và tăng hiệu suất.

```c
int io_uring_register(unsigned int fd, unsigned int opcode, void *arg, unsigned int nr_args);
```