4.7 KiB
4.7 KiB
Разбор решения 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с любыми Cookieusername=...; 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.
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).
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}.