Учёт провизии
Описание задачи
В терминале учёта провизии требуется ввести правильный пароль.
Дан файл Учёт провизии.xlsx. При открытии — «терминал» на листе Panel с полем ввода пароля. Нужно найти пароль, чтобы на экране отобразился флаг.
Разведка
XLSX — это ZIP с XML внутри. Распаковываем архив и смотрим структуру:
xl/workbook.xml — описание листов xl/worksheets/ sheet1.xml — Panel (видимый) sheet2.xml — s1 (скрытый) sheet3.xml — s2 (скрытый) sheet4.xml — s3 (скрытый) xl/sharedStrings.xml — строковые константы
unzip "Учёт провизии.xlsx" -d unpacked
В workbook.xml видим три скрытых листа: s1, s2, s3. Все три участвуют в верификации пароля и рендере результата.
Анализ листов
Panel (sheet1) — фронтенд
Ячейка B11 — сообщение валидации (через IF со ссылкой на s1). Ячейки B14 и далее — пиксели «экрана»: каждая ячейка берёт бит из s3 и закрашивается условным форматированием → pixel art.
s1 (sheet2) — проверка пароля
Лист содержит многослойную линейную систему над полем Z₂₅₁ (простое p = 251). Слой 1: входной вектор x (8 байт пароля) проходит через матрицу 8×8 M1 с аффинным сдвигом c1. Слой 2: второй набор SUMPRODUCT-формул смешивает v1 и x с константами c2. Слой 3: аналогично смешивает v1, v2 и несколько отдельных линейных комбинаций. Целевой вектор v3 сравнивается с [92, 97, 27, 240, 199, 80, 217, 23]. Несмотря на трёхслойность, вся система линейна по входу x — итого одна система A_total · x ≡ targets - b_total (mod 251).
s2 (sheet3) — поточный шифр
65-строчный ГПСЧ (ЛЦСР-подобный), реализованный формулами Excel. Состояние инициализируется байтами пароля. Генерирует поток ключевых байт через BITXOR/BITLSHIFT/BITRSHIFT операции.
s3 (sheet4) — шифртекст и расшифровка
Ячейки D2:M27 — 260 байт шифртекста (10×26 блоков). Столбец B — ключевой поток из s2. Ячейки P2:Y27 — расшифровка: P[i] = XOR(D[i], B[i]). Расшифрованные байты — биты 26×80 пиксельного изображения (терминал).
Решение
Гауссово исключение над Z₂₅₁
Строим матрицу A_total (8×8) и вектор b_total (8), извлекая коэффициенты из XML всех трёх слоёв. Затем решаем систему методом Гаусса по модулю 251:
P = 251
def modinv(a, p):
return pow(a, p - 2, p)
def gauss_mod(A, b, p):
n = len(b)
M = [A[i][:] + [b[i]] for i in range(n)]
for col in range(n):
pivot = next(r for r in range(col, n) if M[r][col] % p != 0)
M[col], M[pivot] = M[pivot], M[col]
inv = modinv(M[col][col], p)
M[col] = [(v * inv) % p for v in M[col]]
for r in range(n):
if r != col and M[r][col]:
factor = M[r][col]
M[r] = [(M[r][j] - factor * M[col][j]) % p for j in range(n + 1)]
return [M[i][n] for i in range(n)]
# targets из s1: [92, 97, 27, 240, 199, 80, 217, 23]
x = gauss_mod(A_total, [(t - b_total[i]) % P for i, t in enumerate(targets)], P)
# → [120, 108, 109, 97, 116, 114, 105, 120]
password = bytes(x).decode() # → "xlmatrix"
Верификация через реализацию шифра
Реализуем ГПСЧ из s2 на Python, воспроизводя Excel-формулы:
keystream = generate_keystream(b"xlmatrix", 260)
plaintext = [ct ^ ks for ct, ks in zip(ciphertext, keystream)]
# Рендер bitmap 26×80 пикселей
for row in range(26):
line = ""
for col in range(80):
bit = plaintext[row * 10 + col // 8] >> (7 - col % 8) & 1
line += "█" if bit else " "
print(line)
На экране появляется флаг в терминальном шрифте.
Ввод пароля
Открываем Excel, вводим xlmatrix в поле пароля на листе Panel. Формулы пересчитываются, pixel art отображает флаг.
Вывод
1. Распаковка XLSX → анализ скрытых листов
2. Обратная инженерия формул → выявление линейной структуры системы валидации
3. Линейная алгебра над Z₂₅₁ → Гауссово исключение даёт пароль xlmatrix
4. Воспроизведение поточного шифра → расшифровка шифртекста
Ключевой инсайт: несмотря на три слоя SUMPRODUCT-формул с разными промежуточными переменными, вся система является составной аффинной функцией от входа — и как таковая решается методом Гаусса, а не брутфорсом.