55 lines
3.0 KiB
Markdown
55 lines
3.0 KiB
Markdown
<h1 align="center">Бортовой Журнал</h1>
|
||
|
||
<p align="center">
|
||
<img src="https://img.shields.io/badge/category-PWN-blueviolet" alt="PWN"/>
|
||
<img src="https://img.shields.io/badge/points-1000-critical" alt="1000 pts"/>
|
||
</p>
|
||
|
||
Сервис принимает JSON-программы и выполняет над буферами операции в стиле мини-VM: `alloc`, `write`, `read`, `compute`, `typeof`, `free`, `realloc`. Вычислительные функции (`xor`, `rot`, `rev`) лежат внутри C-таблицы указателей `dispatch_table` — и это уже подозрительно, потому что в бинарнике есть скрытая функция победы `win_fn`, читающая `/tmp/flag`. Нужно подложить её адрес в `dispatch_table` и вызвать обычным `compute`.
|
||
|
||
## Решение
|
||
|
||
Атака склеивается из двух багов:
|
||
|
||
| Баг | Что делает |
|
||
|---|---|
|
||
| `typeof` сливает адреса | Возвращает `data_ptr` буфера, адрес `dispatch_table` и адрес `_emergency_nav` (= `win_fn`) |
|
||
| `write` не проверяет `offset` | `dest = buffer.DataPtr + offset` — можно писать куда угодно относительно буфера |
|
||
|
||
Подбираем `offset = dispatch_table - data_ptr`, и запись в буфер улетает прямиком в таблицу указателей. По сути, воспроизводится тот же принцип, что и классический [GOT overwrite](https://ir0nstone.gitbook.io/notes/types/stack/aslr/plt_and_got), просто вместо Global Offset Table — user-space jump table.
|
||
|
||
Эксплуатация в два раунда. В первом — выделяем два буфера и снимаем адреса через `typeof`:
|
||
|
||
```json
|
||
{
|
||
"program": [
|
||
{"op": "alloc", "id": "a", "size": 256},
|
||
{"op": "alloc", "id": "b", "size": 256},
|
||
{"op": "typeof", "id": "a"}
|
||
]
|
||
}
|
||
```
|
||
|
||
Из ответа забираем три адреса:
|
||
|
||
```text
|
||
data_ptr — адрес буфера a
|
||
dispatch — адрес dispatch_table
|
||
_emergency_nav — адрес win_fn
|
||
```
|
||
|
||
Во втором раунде считаем `offset = dispatch - data_ptr`, пишем в буфер `a` по этому смещению `p64(win_addr)` — и переписываем `dispatch_table[0]` (слот `xor`) адресом `win_fn`. Дальше `compute("b", "xor")` вместо честной xor-функции вызывает `win_fn`, та кладёт флаг в буфер `b`, и обычный `read` с base64-декодом выдаёт результат:
|
||
|
||
```json
|
||
{
|
||
"program": [
|
||
{"op": "write", "id": "a", "data": "<p64(win)>", "offset": "<dispatch-data_ptr>"},
|
||
{"op": "compute", "id": "b", "func": "xor"},
|
||
{"op": "read", "id": "b", "count": 256}
|
||
]
|
||
}
|
||
```
|
||
|
||
## Флаг
|
||
`caplag{p3g4s_d1sp4tch_t4bl3_3xpl01t3d}`
|