Writeup GRSU CTF Junior.Crypt.2023

Writeup GRSU CTF Junior.Crypt.2023

Paul

Стеганография

Easy flag

Для решения этой задачи нужно увеличить число отображаемых пикселей. Воспользуемся утилитой 010 editor.

Интерфейс 010 editor.

Смотрим на википедии структуру файла jpg, находим то что нужно.


Увеличиваем значение Y_image примерно на 300 пикселей, сохраняем.

Открываем картинку и видим ту часть изображения, которая раньше не отображалась.

grodno{0n3_m1nut3_t45k}

Зверь внутри

DOCX - формат для хранения электронных документов Microsoft Office. Формат представляет собой zip-архив, содержащий текст в виде XML, графику и другие данные. Откроем файл как zip-архив.

Поверхностно просмотрев все xml-файлы замечаем в document.xml подозрительный комментарий.

По формату сразу понимаем, что это кодировка base-64, раскодируем и получаем флаг.

grodno{d0cx_1s_an_arch1v3}

Шум

Открываем аудиофайл в любом текстовом или hex-редакторе. Сразу замечаем "PNG", то есть внутри зашита картинка. Гуглим сигнатуру png: 89504e47odoa1a0a

Всё что находится перед сигнатурой png удаляем. Меняем расширение файла на png, запускаем.

grodno{3to_pro100_k4rt1nka}

Стего в офисе. II

Читая условие, обращаем внимание на второй элемент, который образует книги – «расстояние между буквами». Инструмент для поиска находим во вкладке «Шрифт» - «Дополнительно» - «Межзнаковый интервал» - «Масштаб». Используя форму «Найти и заменить» легко находим, что в тексте присутствуют интервалы с масштабом 100% (нормальный текст), а также 101%, 102%, 103% и 104%. Заменяем латинскими буквами A, B, C, D (они не встречаются в тексте).

Весь остальной текст убираем. Получили такую строку:

ADADADCAACCABCDBDACDABACACCADDCAACDADAADAAACAAADCCBAABACBAADABDBCDADBBDDAADDAADDDADCDACADACCDADDBADDAAAADAACBAAAADDDCADDDBADDBADCDDBDBADDDACCCDABDCABBDCCDADDAAADCDAADACAABCCCCABCDABCCCAAAAABBDDDBCABCDAACABDCDAACDADCABDDADBDAACADDADBCDAAABADAAACCAAACCBCBDCDACDBDADBADDCAADCADCAADABAABAACADDCDADCAAACDCDABBDCBAAAACDBBDAAACADBADDDBADBAAABADAACAAAADDCADDCBABAABDDCAACACADBBACCDCDADBBDCACDBADCDAABBADACABDDCBDACABCBCDCDAACDAAAAACACBDBDCDBCBADBCDDADDAADBADADDDAADACBADDBAADADAADAAAAAADDACDCCDDAACCDDBADBDDCDADDDADCABAAADACAAAABADDDADACABADBDADAAABADDAABCDABBDABADCBAAAAAADCDAABADDADDADDDCDADDBADBDBAAADCDADBDCDDACDDBBCBCDAADACCAABDAABAAABABDDADDBCBBBCBDDBADDAADCCBDCBACCAADACAAAABADBBDCDADBAABADDADAACBDADDCAADCCCCDDADDACCBCBACBDABDBCCBACDCDDABABDACABDDDAACDDAACCBDACBACAABAAADADCDBCDAAACBDADCDDAACABDACCDBBBBDDAAADAAABAACABCBDDCCAADDCDADBDBDCCBAADBABADBBDADBBBDDDADBCDBAADACBCBABADCDCABDDCCBDABBABDDCBADCDDDBABDDDDCADBCBDCBABBBDBBABACDDDAADDADCCBDDBDADDBDADCBBBDDADACDDDDDADBAACCDAADBADACDDDDDACDCDBACDADDACBDDADDCDAABCDDADBDDDABBBDDCDBABDBDDACDADCDDDBDABDDDADBAABADDACCCDAAAADBBDCAAAABDADBDDABDBBAAACBDBADCBDDBDCDCCADCDCCADDCDCACADDAABCDCADBBCACDDADACADCCADBDDBADACAABDDADAAABADDBBAADADBCCABDDBCCDBBBAADABADADDDDDABBCDBDBBDABDCAABADCDADAAAADDCBCAADACDADACBBBDBDCBBDDBDADABCAAAADABDBBDABCDDDAADDAADDBDBCADCDAACBDCADBCADDACDCBDAAADABBBBDCBDDDDBBCADCDADDCCBDBAADDADDCDDBABDBDDAABDAADCCADDBDCDBDCABDBABBDCACACDCDADBDBAAAADADDDADAAACDBDABAAADBABDDABDDDAACCDBCADDDCDDADDBDACCADDADDDCCABBBCAAAADBDADDDDADDCADADDBADBBCDAADCCADBCBACCDABBDDDDDADCDDBADACAACDDADBCAAAADBDACADADABDABDBDDDDADBDDCCBACBDACABADDDCAADCAADDDCCADADADBBDCBBDBACDABACADBDADBAAABABDDDBBBDDADAADBCCABBDBDDA

Можно предположить различные схемы кодировки флага. Поскольку мы получили 4 символа, то возможна кодировка 4х пар битов. Или каждый символ кодирует 0 или 1. Тогда придется выбрать 2 рабочих символа, а 2 оставшихся – считать мусором и не рассматривать вообще.

Рассмотрим вторую схему. С двумя рабочими символами. Заменим их латинскими буквами A, B, C, D (они не встречаются в тексте). Остальные символы – удалим.

Получили несколько перемешанных битовых карт. А именно – 12 пар: AB, AC, AD, BA, BC, BD, и т.д. Первая буква – 0, вторая – 1.

Пишем скрипт для обработки:

s = 'ADADAC . . . BDDA'
c1, c2 = "A", "D"
s0 = "".join(["0" if x == c1 else "1" for x in s if x in c1 + c2])
print("".join([chr(int(s0[i:i+8], 2)) for i in range(0, len(s0), 8)]))

С битовой карты AD получили строку:

The Angel would tell me that lambs were not the color of tigers ... grodno{Now_I_know_that_neither_the_Angel_nor_Satan_spoke_the_truth}.

grodno{Now_I_know_that_neither_the_Angel_nor_Satan_spoke_the_truth}

Стего в офисе. III

Замечаем что между словами есть как один пробел так и два пробела. Считая один пробел за '0', а два пробела за '1', получим битовую строку. Попытка построить текст на основе такой кодировки и найти в нем флаг не дает результата.

Попробуем считать переходы. Переход от «пробела» к «двум пробелам» и наоборот. Т. е. «пробел» -> «два пробела» = 0, «два пробела» -> «пробел» = 1.

Для этого напишем скрипт на python:

s = "000..."
s = "".join(['1' if s[i:i+2] == '10' else '0' for i in range(0, len(s), 2) if s[i] != s[i+1]])
print("".join([chr(int(s[i:i+8], 2)) for i in range(0, len(s), 8)]))

Получаем флаг:

grodno{Is_life_a_pursuit_of_a_chimera_or_a_search_for_truth?}

Стего в офисе. V

Из условия можно сделать предположение, что текст флага окрашен в цвет, отличный от цвета основного текста. Не будем выяснять в какой. 

Просто удалим весь текст основного цвета стандартной функцией «Заменить» (этот цвет называется «Авто»):

Удалим в оставшемся тексте все пробелы и преобразуем в формат флага:

grodno{BorgesnumberisthenumberofbooksintheLibraryofBabel}

RED Heat

Для решения необходим оригинал постера.

Скачиваем картинку

Получив Red_Heat.jpg и Red_Heat.png, сравниваем их попиксельно.

В названии RED написано капсом, что намекает на значение красного в пикселях.

В полученных при сравнение отличающихся пикселях значение красного заменено ASCII кодом символов флага. 

Код на Python:

grodno{aF4#gr8*';er!@QW}

Crypto

Дискалькулия

Чтобы создать безопасную среду для людей, страдающих дискалькулией, числа в названии шифра заменили на буквы. Что это за популярный шифр с числами в названии? очевидно, это шифр Цезаря со сдвигом. То есть "Шифр L" обозначает сдвиг 11 в шифре Цезаря. Если ссылку зашифровали сдвигом 11, то для расшифрования нужно применить сдвиг 26-11 = 15. Получаем эту ссылку, в которой и находится флаг

https://docs.google.com/document/d/1SgWpfxAGeYwBWyEdooPMryFH_TwhWkPO0ffxBw2ORSY

Коммутативность

Условие задачи намекает, что в ссылке каким-то образом переставлены символы. Тут я вспомнил что ссылки Google docs начинаются с символа "1" и как ни странно единица - второй символ в ссылке:

I1N85BPgD23qCMIIWbwQoHZKVJB11TVctD05MtPIums4

Тут же проверил вариант, где символы переставляются первый со вторым, третий с четвертым и т.д.

Можно было сделать вручную, но я написал короткий код:

def swap_pairs(string):
    swapped_string = ''
    for i in range(0, len(string), 2):
        swapped_string += string[i+1] + string[i]
    return swapped_string


input_string = "I1N85BPgD23qCMIIWbwQoHZKVJB11TVctD05MtPIums4"
result = swap_pairs(input_string)
print(result)

И я оказался прав, ссылка успешно открылась!

https://docs.google.com/document/d/1I8NB5gP2Dq3MCIIbWQwHoKZJV1BT1cVDt50tMIPmu4s

Бег по кругу

Сразу пришло в голову сдвинуть каждый символ на определенное число позиций, например строка "qwerty" после одного сдвига будет выглядеть "wertyq". Как и в предыдущем задании, вспоминаем что ссылка начинается с "1". Тогда из ссылки

2sOeVtCFY3i3K9UEXqVWuoxvO-6X8LwE1Zp9J7D23ZOe

получаем

1Zp9J7D23ZOe2sOeVtCFY3i3K9UEXqVWuoxvO-6X8LwE

https://docs.google.com/document/d/1Zp9J7D23ZOe2sOeVtCFY3i3K9UEXqVWuoxvO-6X8LwE

Like a simple RSA

Рассмотрим функции шифрования и расшифрования.

c = (m*f)modh

m = (c*g)modh

Подставим с во второе уравнение и выполним некоторые преобразования:

m = (g*(m*f)modh)modh

m = (m*f*g)modh

По функции расшифрования понятно, что mmodh = m;

mmodh = (m*f*g)modh

1 = (f*g)modh
То есть, g - обратное f по модулю h число (мультипликативная инверсия по модулю).
Зная это свойство секретного ключа g, имея открытый ключ (h, f) можно с легкостью расшифровать сообщение:

from Crypto.Util.number import inverse


def decrypt(c, public_key, private_key):
    m = (c * private_key) % public_key[0]
    return m


f = 6416887830534433629567050229667684734973282397714251811755752025377169146409477365161229363756766441347368380751372023179641944107478195964183801916988232697767349642657617
h = 8470387347298476177456807592234800305223146039241805029722152502623609066137655800632000167660507958129073278126249206662322559690599099900670113252751054479281818630329178438136876189437058031446326100810231155131782952040600724
c = 4501577816736015596060497850546201260925821617971707200151522683849342994222685636584343269899367495933875369681542486925080096048706520947165012158736670485935607004216130611982263313862618848808033812929735326651289123228929207
g = inverse(f, h)
M = decrypt(c, [h, f], g)
print(M)

Полученное число преобразуем в hex, а потом в текст.

grodno{Asymmetric_encryption_uses_two_keys}

Report Page