Files
Geroi-Kodeksa/OxidePool-PWN/README.md
2026-03-02 21:44:22 +03:00

4.0 KiB
Raw Blame History

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 структуры:
[ buf: [u8; 0xFFF0] ][ handler: Box<dyn Handler> ][ guard: u64 ]
  • проверка границы идёт через u16:
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-ключ. Например, это можно сделать следующим образом:

    key = key_hint ^ (nonce + 0x9E3779B97F4A7C15)
    key = ror(key, 11)
    

    Где ror — циклический сдвиг вправо на 11 бит в 64-битах.

  6. Расшифровать реальные поля:

    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.