4.0 KiB
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 ключа шифрования для расшифровки указателей.
Решение
-
Сделать
HELLO- Отправить
0x10 HELLO. - Получить
nonce.
- Отправить
-
Аутентифицироваться
- Отправить
0x11 AUTHс токеномnonce ^ 0xC0DEC0DEC0DEC0DE. - Получить обычный ACK.
- Отправить
-
Построить/подготовить session (
heap grooming)ALLOCна 5 слотов (count=5).FREEодного слота (idx=1).SELECTслота с индексом 0 (idx=0).
-
Вытянуть замаскированные указатели через LEAK
LEAKсstage=0-> получаемmasked_dataиmasked_vtable.LEAKсstage=1-> получаемkey_hint.
-
Вычислить XOR-ключ. Например, это можно сделать следующим образом:
key = key_hint ^ (nonce + 0x9E3779B97F4A7C15) key = ror(key, 11)Где
ror— циклический сдвиг вправо на 11 бит в 64-битах. -
Расшифровать реальные поля:
data = masked_data ^ key vtable = masked_vtable ^ keyТут
dataуказывает на управляемый буфер,vtable— легитимный указатель на dispatch таблицу. -
Записать OOB-переписыванием fat pointer
-
Сформировать payload:
offset = 0xFFF0len = 16data = p64(data) + p64(vtable)
-
Отправить
0x30 WRITEсflags=1.За счёт переполнения
endзапись уходит в область после буфера и перезаписывает указатель обработчика на контролируемые значения.
-
-
Вызвать обработчик
- Отправить
0x31 TRIGGER. - Должен отработать изменённый callback.
- Отправить
-
Получить доступ к shell.