Init. commit

This commit is contained in:
Caplag
2026-03-02 21:44:22 +03:00
committed by Ivan Z
commit 9511b38280
38 changed files with 4397 additions and 0 deletions

95
OxidePool-PWN/README.md Normal file
View File

@@ -0,0 +1,95 @@
# OxidePool
НУ ооочень простой протокол обмена сообщениями....
## Протокол
Сетевой протокол little-endian, пакет имеет такой layout:
`op:u8 flags:u8 seq:u16 len:u16 csum:u16` + `payload`
, где `csum` — сумма всех байтов payload по модулю `2^16`.
Команды:
- `0x10 HELLO`, payload: empty.
- Ответ: `nonce:u64` + `version:u32`.
- `0x11 AUTH`, payload: `token:u64`, где `token = nonce ^ 0xC0DEC0DEC0DEC0DE`.
- `0x20 ALLOC`, payload: `count:u16` (количество аллокаций в сессии).
- `0x21 FREE`, payload: `idx:u16` (освобождение слота).
- `0x22 SELECT`, payload: `idx:u16` (выбор активного слота).
- `0x30 WRITE`, payload: `offset:u16`, `len:u16`, `data[len]`.
- Работает если выставлен флаг `flags & 1`.
- `0x31 TRIGGER`, payload: empty (запускает handler).
- `0x40 LEAK`, payload: `stage:u8` (`0` или `1`).
## Идея эксплойта
Уязвимость в `Session` связана с неверной проверкой границ в `WRITE`:
- layout структуры:
```text
[ buf: [u8; 0xFFF0] ][ handler: Box<dyn Handler> ][ guard: u64 ]
```
- проверка границы идёт через `u16`:
```rust
let end = offset.wrapping_add(data.len() as u16);
if end <= BUF_SIZE as u16 {
ptr::copy(data.as_ptr(), buf.add(offset as usize), data.len());
}
```
Из-за `wrapping_add` и `u16` `end` переполняется, и запись с `offset=0xFFF0`, `len=16` проходит проверку, хотя фактически выходит за `buf`.
Это даёт OOB overwrite в `handler` (fat pointer: data ptr + vtable ptr) и позволяет переписать `vtable` на адрес из утёкших данных после декодирования.
ASLR/PIE включены, поэтому нужен leak ключа шифрования для расшифровки указателей.
## Решение
1. Сделать `HELLO`
- Отправить `0x10 HELLO`.
- Получить `nonce`.
2. Аутентифицироваться
- Отправить `0x11 AUTH` с токеном `nonce ^ 0xC0DEC0DEC0DEC0DE`.
- Получить обычный ACK.
3. Построить/подготовить session (`heap grooming`)
- `ALLOC` на 5 слотов (`count=5`).
- `FREE` одного слота (`idx=1`).
- `SELECT` слота с индексом 0 (`idx=0`).
4. Вытянуть замаскированные указатели через LEAK
- `LEAK` с `stage=0` -> получаем `masked_data` и `masked_vtable`.
- `LEAK` с `stage=1` -> получаем `key_hint`.
5. Вычислить XOR-ключ. Например, это можно сделать следующим образом:
```python
key = key_hint ^ (nonce + 0x9E3779B97F4A7C15)
key = ror(key, 11)
```
Где `ror` — циклический сдвиг вправо на 11 бит в 64-битах.
6. Расшифровать реальные поля:
```python
data = masked_data ^ key
vtable = masked_vtable ^ key
```
Тут `data` указывает на управляемый буфер, `vtable` — легитимный указатель на dispatch таблицу.
7. Записать OOB-переписыванием fat pointer
- Сформировать payload:
- `offset = 0xFFF0`
- `len = 16`
- `data = p64(data) + p64(vtable)`
- Отправить `0x30 WRITE` с `flags=1`.
За счёт переполнения `end` запись уходит в область после буфера и перезаписывает указатель обработчика на контролируемые значения.
8. Вызвать обработчик
- Отправить `0x31 TRIGGER`.
- Должен отработать изменённый callback.
9. Получить доступ к shell.