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

43 lines
2.7 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">RailAnnot</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>
RailAnnot обрабатывает служебные аннотации PDF-нарядов. В раздатке есть ELF `railannot`. У объекта аннотации 16-битный счётчик ссылок, а у страницы — отдельный указатель на него. Если счётчик обнулится при переполнении, объект освободится, но указатель страницы продолжит ссылаться на него.
## Решение
Создаём аннотацию через `1) load annotation`: `refs = 1`. Затем `2) attach annotation to page` добавляет вторую ссылку, и `refs` становится равен `2`.
Пункт `3) replay stream references` прибавляет произвольное `n`:
```c
ann->refs = (uint16_t)(ann->refs + n);
```
При нуле объект освобождается, но `page_ref` не обнуляется. Передаём `65534` и получаем `2 + 65534 = 65536`, что в `uint16_t` превращается в `0`. Аннотация удалена, страница продолжает держать висячий указатель.
В `4) allocate replacement cache object` выделяется блок того же размера. Заполняем его поддельной аннотацией:
```python
import struct
payload = struct.pack("<HHI", 0, 7, 0x414e4e52)
payload += b"sealed depot note".ljust(64, b"\x00")
payload += struct.pack("<I", 0x5241494c)
```
Первые два поля — `refs` и `id`, затем `magic`, 64 байта текста и `sealed`. Размер получается 76 байт, а `sealed` стоит на смещении `72`.
Выбираем `4) allocate replacement cache object`. На запрос `replacement bytes:` отправляем строку `76`, затем на `payload:` передаём 76 байт поддельной аннотации без добавленного перевода строки.
Освобождённый блок переиспользуется, и `page_ref` указывает на поддельную структуру. При выборе `5) render sealed annotation` сервис проверяет поля `magic` и `sealed`. Они содержат ожидаемые значения, поэтому сервис выводит флаг.
[Солвер](solve/solve.py).
## Флаг
`caplag{annotation_refcount_wrapped_into_the_depot_note}`