Skip to content
This repository was archived by the owner on Jan 15, 2023. It is now read-only.

Commit 21bcd07

Browse files
committed
asciira, betagost, cmap1, cmap2, shout, webdept, webdeptcrypto, well: writeups
1 parent 60495e3 commit 21bcd07

File tree

11 files changed

+209
-9
lines changed

11 files changed

+209
-9
lines changed

tasks/asciirsa/WRITEUP.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
# ASCII-RSA: Write-up
22

3+
Перед нами ещё одна странная реализация RSA. Странно в ней абсолютно всё — побуквенное шифрование, модуль 256 (очевидно не состоящий из двух простых делителей), непонятное умножение символов.
4+
5+
Пойдём самым простым путём: давайте просто посмотрим на все символы с кодами от 32 до 126 (это все «печатаемые символы» — пробел, цифры, буквы, подчёркивание и много чего ещё) и узнаем, во что зашифруется каждый символ.
6+
7+
Для этого можно, например, в текстовый файл `flag.txt` строку
8+
9+
```
10+
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~
11+
```
12+
13+
и зашифровать её. Теперь осталось найти в полученном файле символы из нашего зашифрованного флага и сопоставить.
14+
315
Флаг: **ugra_dont_roll_asciirsa_crypto_4b72d642e66ced42**

tasks/betagost/WRITEUP.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
11
# ГОСТ 34.13-2022: Write-up
22

3+
Итак, в этом задании флаг просто ксорится с некой ключевой последовательностью, которую выдаёт класс `Stream`.
4+
5+
Известно, что функция `XOR` обладает простым свойством — `a xor b xor b = a`. Это означает, что таск решается предельно просто — запустим тот же скрипт, но для зашифрованного файла — и файл расшифруется. Но не тут-то было.
6+
7+
Что ж, воспользуемся предложенным сайтом. Попробовав зашифровать одно и то же сообщение несколько раз видим, как шифротекст меняется. Дело в том, что переменная `value` — глобальная — и меняется после каждого шифрования.
8+
9+
Алгоритм, по которому генерируется ключевая последовательность, довольно стандартный и известный. Называется он _LFSR_, или регистр сдвига с линейной обратной связью.
10+
11+
Значение `value` в нём и обозначает этот самый регистр. Для решения задания даже необязательно разбираться, как работает алгоритм. Достаточно заметить две вещи:
12+
13+
* `value` никогда не превысит $2^{18}$
14+
* следующий бит и новое значение `value` зависит только от текущего `value`
15+
16+
Следовательно, если значение `value` совпадет с каким-то из предыдущих, вся дальнейшая последовательность `value` будет циклическим повтором. При этом `value` гарантированно хотя бы раз за $2^{18}$ шагов повторится — потому что всего различных возможных чисел у нас $2^{18}$.
17+
18+
> Интересно то, что для данного секрета длина такого цикла — 262143 — максимально возможная. Это связано с тем, что под неприметным числом 174807 скрывается многочлен $x^{17} + x{15} + x^{13} + x^{11} + x^9 + x^7 + x^6 + x^4 + x^2 + x + 1$ — это примитивный многочлен степени 17.
19+
20+
Осталось перебрать все эти 262143 возможных значений `value` и для каждого узнать, не начинается ли флаг с `ugra`.
21+
22+
[Код для перебора](exploit.py)
23+
324
Флаг: **ugra_lfsr_is_so_cyclic_dbf6deae053a5be4**

tasks/betagost/exploit.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,8 @@ def encrypt(plaintext: bytes) -> bytes:
3535
with open("flag.enc", "rb") as flag_file:
3636
encrypted = flag_file.read()
3737

38-
for offset in range(131071):
39-
value = Stream.value
40-
41-
if encrypt(PREFIX) == encrypted[:len(PREFIX)]:
42-
Stream.value = value
43-
print(offset, encrypt(encrypted))
44-
38+
for value in range(1, 262144): # value == 0 is impossible
4539
Stream.value = value
46-
Stream.bit()
4740

41+
if encrypt(PREFIX) == encrypted[:len(PREFIX)]:
42+
print(value, PREFIX + encrypt(encrypted[len(PREFIX):]))

tasks/cmap/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ ppc 150, @nsychev
1010
>
1111
> _Примечание._ В этом задании два флага.
1212
>
13+
> _Примечание 2 (добавлено 28 февраля в 17:32)._ не все программы для просмотра PDF показывают содержимое файла корректно. Вы должны увидеть много букв на каждой странице. Если вы их не видите — например, в Chromium, попробуйте другую программу.
14+
>
1315
> _flag.pdf_
1416
1517
[Write-up](WRITEUP.cmap1.md)

tasks/cmap/WRITEUP.cmap1.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
# cmap I: Write-up
22

3+
В файле видим длинный текст из английских заглавных букв. Однако, при копировании мы получаем некую последовательность нечитаемых символов.
4+
5+
Название таска нас отсылает к библиотеке [для языка вёрстки LaTeX](https://www.ctan.org/tex-archive/macros/latex/contrib/cmap), которая делала получаемые PDF-файлы «искабельными и копируемыми». Нам это, правда, никак не помогает.
6+
7+
Что ж, в файле всего 32 различных символа. Можно заметить, что одинаковые буквы в тексте превращаются в одинаковые символы при копировании. Найдём руками соответствие букв и символов и заменим.
8+
9+
Получаем файл в кодировке base32, после декодирования видим PNG-картинку с флагом.
10+
311
Флаг: **ugra_you_do_not_need_usepackage_cmap_4228eae1c90b**

tasks/cmap/WRITEUP.cmap2.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
# cmap II: Write-up
22

3+
Цель второго таска не ясна. Но нам явно говорят, что где-то в файле есть скрытая пометка. Можно начать исследование от первого таска — в нём была проблема со шрифтом, в нём и попробуем разобраться.
4+
5+
Воспользуемся любой утилитой для извлечения шрифта из PDF. Исследуем все символы в полученном шрифте. На случайных позициях видим заглавные буквы и цифры — ровно эти позиции и нужно было найти в первой части задания (возможно, так даже проще их находить).
6+
7+
Но наше внимание в этот раз привлекают символы с кодами больше 65536 — зачем они нужны? Присматриваемся в глифы (рисунки символов) и видим, что они последовательно составляют флаг.
8+
9+
Это и есть та самая метка.
10+
311
Флаг: **ugra_oh_what_a_weird_font_here_017272589fec**

tasks/shout/WRITEUP.md

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,42 @@
11
# Поорите: Write-up
22

3-
Флаг: **ugra_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_690868a2aeaff4c1**
3+
Перед нами задание на бинарную эксплуатацию. Посмотрим, что оно умеет, не открывая ни бинарный файл, ни дебаггер.
4+
5+
Нередко в таких заданиях бывает переполнение, и, чтобы забить нужное количество байт, обычно используют буквы `A`. Возможно, на это нам и намекают, когда просят поорать. Что ж, попробуем: введём 100 букв `A`:
6+
7+
```bash
8+
$ ./shout
9+
Shout here:
10+
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
11+
Too weak, try again.
12+
```
13+
14+
Попробуем ввести больше букв. Например, 2000:
15+
16+
```
17+
Too weak, try again.
18+
Segmentation fault (core dumped)
19+
```
20+
21+
На этот раз приложение упало с ошибкой. Это означает, что уязвимость всё-таки есть — и эта уязвимость — переполнение стека. Уязвимость заключается в том, что приложение неправильно валидирует данные, хранящие на стеке приложения: мы можем ввести больше данных, чем есть места — в этом случае приложение их просто начнёт писать дальше, затирая какие-то важные данные.
22+
23+
Всё-таки дальше без изучения приложения не обойтись. Откроем его в IDA или Cutter и посмотрим, что внутри. Сразу замечаем какую-то непонятную секцию `.flags`, местоположение которой, скажем прямо, удивляет — она находится через 532 МБ от начала приложения.
24+
25+
В этой секции мы находим единственную функцию `print_flag` по адресу `0x21414141`, которая открывает файл `flag.txt` и печатает его. Казалось бы, вот и решение — но незадача: эта функция нигде в коде не вызывается.
26+
27+
Теперь нужно понять, как можно использовать найденную уязвимость для вызова функции. На самом деле, стек вызовов выглядит примерно так:
28+
29+
```
30+
... | shout | main | ...
31+
... | local vars | ebp | return addr | args | local vars | ebp | return addr | args | ...
32+
```
33+
34+
Таким образом, если мы затрём данные после локальной переменной в функции `shout`, мы сможем переписать `ebp`, `return addr` и всё, что находится дальше. А что произойдёт потом? А потом программа успешно дойдёт до конца функции и попробует вернуться в «предыдущую функцию» по адресу, который лежит в return addr. И, если нам удастся подставить сюда адрес нашей функции `print_flag`, то мы победим.
35+
36+
Следующая проблема — формат ввода. Как известно, мы вводим в программу буквы, а адрес перехода состоит из цифр. Что же делать? Тут всё очень просто — мы вводим те символы, шестнадцатеричные коды которых соответствуют байтам нашего адреса. Есть одно маленькое «но» — в Linux порядок байт little endian, поэтому адрес нужно вводить «наоборот». Числу `41` соответствует символ `A`, а числу `21` — символ `!`. Получается, наш адрес мы будем вводить как `AAA!`.
37+
38+
Наконец, нужно понять, сколько байт нам нужно всего. Можно просто перебрать. Проще — локально. Создадим файл `flag.txt` и будем запускать наш файл с разным количеством `A`. Можно отреверсить этот бинарный файл и узнать, сколько байт выделяется на стеке.
39+
40+
В итоге мы получаем эксплойт — 1039 букв `A` и восклицательный знак. Ровно так нужно проорать, чтобы получить флаг.
41+
42+
Флаг: **ugra_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_690868a2aeaff4c1**

tasks/webdept/WRITEUP.webdept.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
11
# Веб-департамент: Write-up
22

3+
Сначала зайдём на сайт с помощью любого приложения для двухфакторной аутентификации — например, Google Authenticator или Authy. На сайте есть две страницы — одна позволяет запросить содержимое любого сайта, а вторая — отправить сообщение.
4+
5+
Обращаем внимание, что все ответы от сервера сопровождаются заголовком `X-Backend: 10.13.37.159`. Это намекает нам на атаку SSRF (Server-side request forgery) — мы можем получить доступ к внутренним ресурсам.
6+
7+
Начнём с сети `/24` и популярных веб-портов — например, с `:80`. В локальной сети обнаруживаем хост `http://10.13.37.62` — по этому адресу отдаётся некий JSON.
8+
9+
Изучая его, приходим к выводу, что это схема API — доступных эндпоинтов на этом сервере и формата запросов. Всего видим два эндпоинта:
10+
11+
* `GET /request-otp?username=...`
12+
* `POST /check-otp` `username=...&code=...`
13+
14+
POST-запросы мы делать не можем, попробуем GET с именем пользователя `admin`: `http://10.13.37.62/request-otp?username=admin`. Получаем непонятный текст, но видим в нём `PNG`. Вероятно, это такая же картинка с кодом, что была у `andy77`.
15+
16+
`\x89` выглядит как байт с хекс-кодом 89. Очень похоже на то, как Python кодирует бинарные строки. И действительно, даже куки намекают на использование aiohttp — это веб-фреймворк для Python.
17+
18+
Самый простой способ получить картинку — обернуть это всё в `b"..."` — это байтовая строка, и вывести полученную переменную в файл. Код, кстати, можно скопировать из какого-нибудь таска на криптографию:
19+
20+
```python3
21+
image = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR...\x00IEND\xaeB`\x82"
22+
with open("image.png", "wb") as f:
23+
f.write(image)
24+
```
25+
26+
Открываем картинку, видим QR-код администратора. Входим, получаем флаг.
27+
328
Флаг: **ugra_isolate_your_public_hosts_d611010a81dd**
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
11
# Крипто-департамент: Write-up
22

3+
После решения задания [«Веб-департамент»](WRITEUP.webdept.md) мы авторизованы в системе под администратором. Кроме флага, мы также узнаём, что есть и вторая сеть — 10.13.38.0/24. Просканировав все порты 80 и в ней, находим странную веб-страницу для печати сообщений — 10.13.38.116.
4+
5+
Но какие же сообщения можно расшифровать? Вспоминаем о второй части сайта — сервисе с жалобами. Возвращаясь к ней уже с повышенными привилегиями, мы можем увидеть список последних жалоб. Но в зашифрованном виде.
6+
7+
Попробуем напечатать любую жалобу — и, действительно, сайт нам сообщает о её печати на принтере. Если же мы отправим абракадабру, то ничего не напечатается: нам скажут, что произошла ошибка.
8+
9+
> Обратите внимание: сама форма на загруженной странице работать и не будет — надо вручную дописывать `enc` именно в URL запроса.
10+
11+
Вспоминаем про AES-CBC, упомянутый ранее. Атака, которая подойдёт нам в этом случае — [Padding Oracle](https://robertheaton.com/2013/07/29/padding-oracle-attack/) — она позволяет всего лишь по сообщению о корректном или некорректном шифротексте получить флаг.
12+
13+
Посмотрим на формат зашифрованных сообщений. Независимо от длины сообщения, часть до `|` — base64 от 16 байт данных, а справа — base64 данных длины чуть большей того текста, что мы отправляем — если быть точным, длина округляется до 16 байт вверх.
14+
15+
Предпологаем, что слева от `|` находится IV, а справа — шифротекст, заполненный паддингом. Самый распространённый формат паддингов — PKCS7; предположим, что он и использовался.
16+
17+
[Код эксплойта](exploit.py)
18+
319
Флаг: **ugra_do_not_ever_use_cbc_420d096df213**

0 commit comments

Comments
 (0)