Files
Serdtse-Sysoly/qualifiers/lease-mesh-pwn-medium/WRITEUP.md
T
2026-09-17 00:50:07 +03:00

53 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<h1 align="center">LeaseMesh</h1>
<p align="center">
<img src="https://img.shields.io/badge/category-PWN-blueviolet" alt="PWN"/>
<img src="https://img.shields.io/badge/difficulty-medium-orange" alt="medium"/>
</p>
LeaseMesh позволяет создавать, просматривать, закрывать и пересылать «аренды» маршрутов. В раздатке есть ELF `lease-mesh` с PIE и сохранёнными символами. Для получения флага нужно узнать адрес `flag_export` и подменить обработчик маршрута.
## Решение
У маршрута размер 144 байта. Для атаки важны следующие поля:
| Смещение | Поле | Назначение |
|---:|---|---|
| 0 | `magic`, 8 байт | `0x4c454153454d4553` |
| 8 | `name[24]` | Имя маршрута |
| 32 | `generation`, 8 байт | Поколение объекта |
| 40 | `handler`, 8 байт | Указатель на обработчик |
| 48 | `approved`, 4 байта | Для экспорта нужно `0x51524f4b` |
| 52 | `memo_len`, 4 байта | Длина заметки |
| 56 | `memo[88]` | Сама заметка |
Открываем маршрут в слоте `0` через `1) open route lease`, имя и заметка любые. В пункте `4) inspect route` выбираем слот `0`. Из `handler=%p` получаем адрес `route_status` в запущенном процессе и сохраняем его числом в `leaked_handler`. Через `readelf -sW public/lease-mesh` находим значения символов `route_status` и `flag_export`. Смещения относительно базы PIE обозначим `offset_route_status` и `offset_flag_export`. Вычитаем первое из утечки и прибавляем второе:
```python
pie_base = leaked_handler - offset_route_status
target = pie_base + offset_flag_export
```
Теперь освобождаем маршрут через `2) close route lease`, указывая слот `0`. В `close_route` есть `free(routes[idx])`, но нет обнуления `routes[idx]`. Значит, слот продолжает ссылаться на освобождённую память.
Пункт `3) import raw decoder packet` выделяет ровно `sizeof(struct route)` и заполняет блок переданными байтами. Собираем подделку:
```python
import struct
payload = struct.pack("<Q", 0x4c454153454d4553)
payload += b"export-reclaim".ljust(24, b"\x00")
payload += struct.pack("<QQII", 0x2605061337, target, 0x51524f4b, 0)
payload = payload.ljust(144, b"\x00")
```
Выбираем `3) import raw decoder packet`. На запрос `raw packet bytes:` отправляем строку `144`, затем на `packet payload:` передаём 144 байта payload без добавленного перевода строки.
После переиспользования блока `routes[0]` указывает на этот пакет как на настоящий маршрут. Вызываем `5) dispatch route`, слот `0`. Поля `magic` и `approved` содержат ожидаемые значения, а `handler` указывает на `flag_export`.
[Солвер](solve/solve.py).
## Флаг
`caplag{stale_handle_reclaimed_the_route}`