Rating: 0
# Kolobok
## Идея
Снаружи задача выглядит как maze-game с pyjail:
- нужно собрать `8/8` звёзд;
- обычный вход в `X` даёт `Nice try, you almost got the flag :)`;
- после сбора всех звёзд сервер всё ещё держит `maintenance_open = false`.
Ключевой баг не в классическом web, а в том, как sandbox-объекты сериализуются в строку.
## Что ломается
`mapdata_ref` при индексировании выглядел пустым: `mapdata_ref[i] == 0`.
Но `str(mapdata_ref)` возвращал не просто заглушку, а строку вида:
```text
...|LOCK=87cd75c7014ecce9c6b97ac50a35ca9c...
```
Это уже первая утечка.
Вторая утечка была в `auxdata_ref`:
- наружу через `/game_state` видны только первые `56` значений `aux_data`;
- внутри kernel доступны все `64`;
- если скопировать `auxdata_ref[56:64]` в `auxdata_ref[0:8]`, наружу утекает скрытый 8-байтовый ключ.
То есть sandbox позволял вытащить:
1. `LOCK` из `str(mapdata_ref)`
2. ключ из скрытого хвоста `auxdata_ref`
## Расшифровка
`LOCK` надо XOR-ить этим 8-байтовым ключом по кругу.
После XOR получается человекочитаемая строка:
```text
|A=708;B=525;C=744|K1=A*2+B*2+C*2+96;K2=A*7+B*2+C*6+17;K3=K1*2+K2*2+C*3+94|SLOTS=16,31,35
```
Отсюда считаем:
```text
K1 = 4050
K2 = 10487
K3 = 31400
```
Важно: `SLOTS` относятся не к `out_ref`, а к `auxdata_ref`.
Если записать:
```python
auxdata_ref[16] = 4050
auxdata_ref[31] = 10487
auxdata_ref[35] = 31400
```
сервер отвечает `Maintenance unlocked.` и выставляет `maintenance_open = true`.
## Финал
После unlock реальные выходы `X` становятся валидными. Дальше достаточно дойти до любого `X` и запросить `/get_flag`.
## Эксплоит
Готовый автоматический скрипт:
[solve_kolobok.py]
Что он делает:
1. регистрирует нового пользователя;
2. автоматически собирает `8/8` звёзд;
3. дампит `LOCK` через `str(mapdata_ref)`;
4. дампит скрытый 8-байтовый ключ через `auxdata_ref[56:64]`;
5. XOR-ит данные и вычисляет `K1/K2/K3`;
6. пишет значения в нужные слоты `auxdata_ref`;
7. доходит до выхода и получает флаг.