Files
Kubok-Regionov/MEGA-router/writeup_tasks_1_2.md
2025-12-22 05:19:38 +03:00

79 lines
4.7 KiB
Markdown
Raw Permalink 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.
# Разбор решения MEGA-router: задачи 1 и 2
Ниже разбор решения первых двух задач. Сначала общий вход (как получить бинарник), затем отдельно по каждому флагу.
## Общий вход: утечка бинарника через /ping
- Авторизация не нужна: `logged_in()` просто проверяет, что в Cookie есть и `username=`, и `password=`; значения не важны (`challenge/src/server_http.hpp`). Альтернатива — дефолтные креды `admin/admin` или `admin/admin888` (`challenge/src/server.cpp`).
- `/ping?id=...` читает 4096 байт из `Global.BUFFER + offset*4096` без проверки границ и отдает base64 (`challenge/src/server.cpp`). Падение дочернего процесса дает пустой `result`, сервер не падает.
- Зацепка: зайти на `/portal` с любыми Cookie `username=...; password=...` и открыть `main.js` — там видно, что клиент делает `POST /ping` и затем многократные `GET /ping?id=...`, то есть это ключевая точка.
- Так как `offset` — беззнаковый и `-` запрещен, читаем «назад» через переполнение: `4096 = 2^12`, значит `2^64/4096 = 2^52`. Берем `offset = 2^52 - N`, получаем адрес `BUFFER - N*4096`. Этим выходим на `.text`/`.rodata` и находим ELF.
- Минимальный запрос, который дает первую зацепку в ответе (base64-кусок памяти) и позволяет начать сканировать ELF:
```
GET /ping?id=4503599627370495 HTTP/1.1
Host: <host>:31337
Cookie: username=a; password=b
```
- Дальше — обычный дамп страниц и склейка в файл; можно искать строку `ELF` или специфичные строки вида `/giv_me_please_flag_...`.
## Задача 1 (Flag 1)
- В бинарнике виден скрытый эндпоинт `"/giv_me_please_flag_nu_ochen_nado"` (`challenge/src/server.cpp`).
- Делаем GET на него, ответ — редирект на `/index` с заголовком `Flag: <base64>`. Декодируем base64.
- Дешифрование: байты зашифрованы `ROTR3` после XOR с `(i*4 + 0xc0)`. Значит, для каждого байта: `ROTL3`, затем XOR.
```python
import base64
def rotl8(x, n):
return ((x << n) | (x >> (8 - n))) & 0xff
cipher = base64.b64decode(flag_header)
plain = bytes(
rotl8(b, 3) ^ ((i * 4 + 0xC0) & 0xff)
for i, b in enumerate(cipher)
)
print(plain.decode())
```
- Итоговый флаг: `caplag{r0ut3r_p0rtals_ar3_ult1mat3ly_imp3n3trabl3_b3caus3_th3y_ar3_r3al_w3bapp}`.
## Задача 2 (Flag 2)
- В логике `/login` видно, что для юзера `hoEjB9OtHLCuDibAT6Ag` вызывается `gen_flag`, но он защищен длинной подстрокой и MD5 (прямой вызов нецелесообразен) (`challenge/src/server.cpp`).
- В `gen_flag` видно массив `flag_byte_offsets[]` и схему: берется `pi_bytes.bin` (2048 байт, соответствуют позициям 1_000_000..1_002_047), а флаг строится как `bytes[offset - 1_000_000]`.
- Поэтому нужно: (1) вытащить `flag_byte_offsets[]` из слитого бинарника; (2) сгенерировать байты π для диапазона 1_000_000..1_002_047. Функция `get_byte` — это две hexцифры π подряд. Быстро считается через BBP (как в `#ifdef DEBUG` части, `challenge/src/server.cpp`).
```python
def pi_hex_digit(n):
n -= 1
def series(m):
s = 0.0
for k in range(n + 1):
ak = 8 * k + m
s = (s + pow(16, n - k, ak) / ak) % 1.0
k = n + 1
t = 1.0
while True:
ak = 8 * k + m
t /= 16.0
term = t / ak
if term < 1e-17:
break
s = (s + term) % 1.0
k += 1
return s
x = (4*series(1) - 2*series(4) - series(5) - series(6)) % 1.0
return int(x * 16)
def get_byte(pos):
return (pi_hex_digit(pos) << 4) | pi_hex_digit(pos + 1)
pi_bytes = [get_byte(p) for p in range(1_000_000, 1_002_048)]
flag = ''.join(chr(pi_bytes[o - 1_000_000]) for o in offsets)
print(flag)
```
- Итоговый флаг: `caplag{leibniz_david_bailey_peter_b0rwein_and_sim0n_pl0uffe_good_idea_guys_00}`.