CTF — это не сложно [NQ2K18]

0b0fb5bd97c47363518d0153d90af3e4.png


И вновь завершился очередной отборочный online-этап ежегодного соревнования по кибербезопасности — NeoQUEST-2018.
Что было? Хм… Оказалось, что в Атлантиде тоже используют Android, но файлы передают по старинке: с помощью Bluetooth, беспокоятся о безопасности транзакций и создают распределенные сети, взламывают сайты конкурентов и используют информационную разведку, а ещё — почти все компьютеры работают на таинственном «QECOS», написанном на LUA, но с большим количеством опечаток. Как здесь выжить? Читайте под катом.

«Но ведь уже есть несколько WriteUp’ов! Зачем ещё?» — скажете вы, но этот — не такой как у других. Здесь мы рассмотрим некоторые задания из NQ18 со стороны человека, который вообще может ничего не знать об информационной безопасности.

Задание #1 — «Зелёное объединение»

Нашему вниманию представляется файл с расширением APK и отсылка к Unity. Если мы не знаем, что значат эти «иероглифы» — используем любую поисковую систему. Становится понятно, что ничего не понятно файл представляет собой архивный исполняемый файл, а значит необходимо провести распаковку.
Далее у нас несколько вариантов. Первый — мой традиционный. Второй — правильный.

  1. Открываем »1.apk» в iDefense MAP Strings или SysInternals Strings. Из прошлого опыта знаем, что ключ едва ли будет меньше 32 символов. Ищем всё подозрительное и похожее на ключ. Находим.
    1704dc03ecc96567ba05171f5e2b9edb.jpg

    К сожалению, ничего кроме первого ключа найти таким образом не получится.
  2. Ищем в интернете что-нибудь наподобие «apk unpacker» и находим — DevXUnity-Unpacker Magic Tools. Запускаем. Открываем нужный нам файл:»1.apk».
    5d1be7cea85407cfc899b54b44eda39e.jpg

    Неистово «протыкиваем» всё, что двигается.
    176cb94a998a6309fca05d0143ef5a97.jpg

    Первый ключ сразу бросается в глаза. Продолжаем изучать структуру файла. Обращаем внимание на файлы: button.cs, key_part_N и ветку »*** ***».
    Файл button.cs содержит в себе функцию GetSequenceKey (), состоящую из массива чисел, а также переменную «text» (34 символа) из звёздочек и точек. Очень похоже, что массив это ASCII код. Проверяем: convert the height to hex!!!
    Наc вежливо просят перевести высоты в HEX. Интересно, о каких высотах речь? Теперь настало время посмотреть телевизор на рисунок кнопки:
    a405c3496942afc77adc08ab70852550.jpg

    Разноцветные «ступеньки»… и сколько их? Ровно 34!
    Любым удобным методом мерим высоту в пикселях и переводим в HEX. На выходе получаем следующий массив значений: ['68', '5b', '59', '00', '59', '58', '40', '44', '17', '58', '48', '57', '14', '47', '45', '48', '16', '58', '4f', '11', '5c', '55', '00', '5b', '49', '41', '40', '45', '0c', '0e', '11', '02', '00', '19']
    Вписываем эти значения вместо «вертикально расположенных» в функции GetSequenceKey ()
    Далее по функции видим, что эти значения будут гаммироваться со строкой в переменной «text». Кажется, что мы где-то уже видели эти звездочки. Бинго!
    7b5286b708d8bbb73ce7f878a8cc00c7.jpg

    Меняем звёздочки в функции на найденный текст. Переписываем всё, например, на Python:
    #!/usr/bin/python2
    text_ojb='You hold the key to my heart . . .'
    heigth=['68', '5b', '59', '00', '59', '58', '40', '44', '17', '58', '48', '57', '14', '47', '45', '48', '16', '58', '4f', '11', '5c', '55', '00', '5b', '49', '41', '40', '45', '0c', '0e', '11', '02', '00', '19']
    heigth_int=[int(x,16) for x in heigth]
    array=heigth_int
    arg = ''
    for i in range(len(array)):
        arg += chr(array[i] ^ ord(text_ojb[i]))
    print(arg)

    В результате получаем: 14, 17, 7, 24, 16, 11, 3, 21, 1, 7
    Похоже, что это последовательность. Кого? Чего? И вновь пришло время вспомнить что ты делал прошлым летом про найденные key_part_N в ресурсах файла. Собираем ключ (40 символов) из полученной последовательности и гордимся!

Задание #2 — «Пара-пара-пар!»

Нам выдали файл и сказали что-то про Bluetooth. Что бы это могло значить? Откроем текстовым редактором.

94dfa9da14ab528aec219e4d0282f51a.jpg


Ничего не понятно. Какие-то символы и модели телефонов. Попробуем «загуглить» первое попавшееся слово — «btsnoop»
Ага. Это дамп трафика сетевого взаимодействия Bluetooth устройств. Нужно снова воспользоваться силой земли и найти все инструменты, способные анализировать сетевой трафик. Самая популярная из таких программ — Wireshark. Устанавливаем. Открываем наш файл — 1513 сетевых пакетов.
Что с этим делать дальше? RTFM и только хардкор!
Немного пролистав пакеты, можно заметить, что идёт обмен между Galaxy S6 (смартфон) и неким устройством — ActionsS_32:8f: a3 (TS007). Снова «гуглим». Беспроводная гарнитура :)
Сделаем предположение, что между устройствами должна идти передачи звукового файла.
Wireshark позволяет в автоматическом режиме отследить очередь пакетов передачи аудиофайлов: Telephony → RTP → RTP Streams

0af483c1356179de655965f0381b5150.jpg


Программа смогла выявить и распознать передачу одного звукового файла. Жмём «Analyze», а затем «Play Streams»

1d7cd860e9a762f4cb3075bcb0847a36.jpg


А вот и наша звуковая дорожка. Слушаем, осознаём тщетность бытия, идём либо учить азбуку Морзе, либо прямо из этого окошка на слух переписываем сигнал в точки и тире:»·– — ·–·· ·– –· — ·· –·· ·– ····· ––––– ····· ·––––». Ищем сайт декодирования кода Морзе. Например, вот этот. Получаем слово: «atlantida5051».
И казалось бы, что на этом всё, но нет! Мы же знаем, что ключ — 40 символов. Берём от ключа SHA1 — задание выполнено!

Задание #4 — «Дерижабль? Ага!»

Суть выполнения этого задания очень хорошо расписал @Nokta_strigo. Однако, получение второго ключа описано слишком умными для обывателя словами. А мы что? А мы сделаем это заданием через… Microsoft Excel 2016 и без программирования (нет, это не шутка)
Первым делом, открываем базу данных сайта NeoChat с помощью DB Browser for SQLite:

a8b694eaab7ae72b2811551bcb070c0e.jpg


Видим, что в таблице есть 5 зашифрованных записей разной длины. Но как их восстановить и получить SecondKey?
Вспоминаем, что кроме этой базы данных у нас есть ещё и копия всех профилей пользователей. Тот, который нам нужен (admin) имеет ID = 4 (смотрим на таблицу)

319bde647d700964dea716e2633d2c29.jpg


А вот и его папка, где следует искать данные. Переходим в неё и открываем файлы «data.json» и «content.json». Что нам из них необходимо:

"username": "admin"
"address": "1NeoChatZK4DxZJdg5WwocwxcUppA1eJgL"
"cert_user_id": "neochatadmin@zeroid.bit"

Итак, теперь мы можем сопоставить табличные зашифрованные значения и исходные. Но каков алгоритм шифрования? Секрет кроется в файле «lfsr.js»:

data[i] = data[i] ^ keystream[i]


Ссылка на статью про гаммирование находится чуть выше по тексту. Да, здесь тоже оно.
Данный алгоритм шифрования не является криптостойким, а значит, что можно восстановить keystream из исходного и зашифрованного текстов:

keystream[i] = XOR(HEX(шифротекст[i]), HEX(исходный_текст[i]))

Восстанавливаем keystream:

cac66cfa99e9c4 = XOR(84a303b9f188b0,4e656f43686174) <- SiteName
25f089bcb0f2c713f7936d7fc8b708ffcac66cfa99e9c4 = XOR(6e656f6368617461646d696e407a65726f69642e626974, 4b95e6dfd893b37293fe041188cd6d8da5af08d4fb80b0) <- AdminCert
d7c2b1cb2d881566404f3e25f089bcb0f2c713f7936d7fc8b708ffcac66cfa99e9c4 = XOR(e68cd4a46ee074121a040a6188d3f6d495f24480fc0e08b0d45d8fba875d9fd38e88, 314e656f436861745a4b3444785a4a64673557776f637778635570704131654a674c) <- SiteAddress

Видим, что для всех операций шифрования применяется один и тот же keystream (обратите внимание, что получается одна и та же строка, если смотреть на неё задом наперёд).
Чтобы расшифровать SecondKey необходимо ещё 72 символа, но где их достать?
А теперь, мои юные падаваны, настало время основ криптографии.
LFSR — Регистр сдвига с линейной обратной связью (РСЛОС) — имеет ряд недостатков. Один из них — возможность восстановить цепочку методом корреляционного вскрытия, либо с помощью алгоритма Берлекэмпа — Мэсси (далее — АБМ).
Здесь у нас есть снова два варианта решения поставленной задачи:

  1. Перевернуть keystream, а также открытый и закрытый текст, потом восстановить последовательность при помощи полинома, получить открытый текст, а затем снова перевернуть результат
  2. Восстановить последовательность из конца в начало, т.е. по принципу «AS IS» (для таких извращенцев как я)


Как вы уже догадались, здесь я опишу именно второй способ. Запускаем универсальное средство криптоанализа — CrypTool. Используем модуль «Алгоритм Берлекэмпа — Мэсси» из вкладки «Криптоанализ». Добавляем элемент «Text Input», соединив его со входом модуля АБМ, а для выхода генератора полинома добавляем «Text Output». Преобразуем полученный keystream (68 символов) в бинарный вид и вписываем в «Text Input». Запускаем проект.

1cc1b00117a5eb05bc911909feef06c4.jpg


На выходе мы получаем полином формирования псевдослучайной последовательности РСЛОС. Да, он такой страшный. Да, будем работать через Excel. Нет, верёвки и мыла нет.
Ещё не забыли, что мы делаем? Правильно — восстанавливаем последовательность ПСЧ
А теперь изюминка этого поста — открываем Microsoft Excel 2016 (начиная с этой версии была добавлена функция ИСКЛИЛИ () для нескольких аргументов)
Перематываем страницу куда-нибудь до столбца «ААА». И в строчку размещаем наш keystream в бинарном виде (по биту в каждую ячейку).
Теперь необходимо понять — что же такое полином (который мы получили). Немного теории:
Полином, в данном случае — это ключ формирования псевдослучайной последовательности. К примеру, если у вас есть последовательность »010101» и вы задаёте для неё полином: $x^3 + x + 1$, то следующий элемент последовательности будет всегда генерироваться по формуле: x[i] = x[i-3] ^ x[i-1]. После первой итерации у нас будет:»0101010», после второй:»01010100», после третьей:»010101001». Точно также и у нас. Эй! Убери ножницы от глаз.
В данном примере я рассмотрел нормальную генерацию последовательности «от начала в конец», а нам нужна наоборот. Вернёмся к нашей книге Excel.
Теперь, зная, что такое полином, необходимо чуть-чуть поменять полином и записать формулу генерации ПСПЧ в ячейку ZZ[1] (если keystream начинается с AAA[1])
В чём преимущество Excel? В том, что здесь вам не нужно думать о том как изменить формулу, какие индексы будут относительно генерируемого элемента, ибо все ячейки имеют относительный адрес. Всё, что нужно — изменить первый и последний аргументы формулы (по сути — поменять местами). Мы получим (в ячейке ZZ[1]):

=ЕСЛИ(ИСКЛИЛИ(AEV1;AEU1;AER1;AEP1;AEO1;AEN1;AEM1;AEL1;AEI1;AEH1;AEG1;
AEC1;AEB1;AEA1;ADX1;ADW1;ADU1;ADT1;ADO1;ADK1;ADJ1;ADI1;ADH1;ADG1;ADE1;ADB1;
ADA1;ACY1;ACR1;ACQ1;ACP1;ACN1;ACL1;ACK1;ACI1;ACH1;ACF1;ACE1;ABZ1;ABV1;ABU1;
ABS1;ABP1;ABM1;ABL1;ABK1;ABF1;ABE1;ABD1;ABB1;ABA1;AAZ1;AAX1;AAV1;AAU1;AAS1;AAJ1;
AAH1;AAF1;AAE1;AAB1;AAA1;AEX1);1;0)

А теперь просто берём за уголок ячейки и вытягиваем её [формулу] влево до, например, ячейки AA[1].
Поздравляю! Вы восстановили последовательность.
Переводим из бинарного вида в HEX и берём последние 140 символов:
»847bf908c48a48cf4a4dd8b9d965d3867bc55e2aa33a56197228e1050ffb5157f21e4c1ad7c2b1cb2d881566404f3e25f089bcb0f2c713f7936d7fc8b708ffcac66cfa99e9c4»
Теперь делаем XOR (keystream, secondkey) и декодируем из HEX в текстовый вид:
«CALCULATEMYSHA1suA1EeNRrIiIeGUopBSyVLU7juqgNaOmGLsTf2erZHfQ0VgB6CwxykY»
Строка как бы говорит нам, что нужно получить дайджест SHA1. Конец :)

Задание #6 — «Кто тут инженер?»

Уже в брифинге в нас кидают парой пар заумных слов: акселерометр, RTTY, смещение, чашка
Файл представляет собой, форматированный в два столбца, текст: время и значение.
Тщательно поискав, можно узнать, что некоторое программное обеспечение для работы с RTTY (например, fldigi) позволяет воспроизвести запись эфира из «WAV» файлов — функция playback. Может нам нужно сделать из текстового файла — аудиофайл?
Снова используем поиск в мировой паутине — говорят, что всем известный аудиоредактор Audacity умеет это делать.
Запускаем. Выбираем «Создание» → «Sample Data Import». Указываем путь до файла. Ошибка: Audacity не понимает такую структуру файла. Что же, я бы тоже не сразу понял, что от меня хотят.
Давайте подумаем:, а нужно ли нам время? А пространство? Запомним, что там 9.961 секунд, удаляем левый столбец и оставляем только значения.
Повторно импортируем:

fjdzdnbcrrkbunlhps7gll-ph58.jpeg


Сразу заметно, что Audacity не понимает «сырые данные» сверх нормированных (от -1 до 1) значений.
Самое быстрое и рабочее решение — разделить каждое значение в файле на максимальное. Любым удобным способов реализуем это (и даже здесь Excel может помочь). И ещё раз импортируем:

gzord3bckl4vzccckifow_w9jc4.jpeg


Очень краси… Стоп! А почему весь файл длится 0,135 секунд? Там, кажется, около 10 секунд было.
Ещё раз выделяем всю аудиодорожку и выбираем «Эффекты» → «Смена скорости». Выставляем следующие значения:

wons8bbe73mj2ubvbic746teoic.jpeg


Вот теперь хорошо! Экспортируем в аудиофайл с расширением «WAV»
Запускаем fldigi: «Op Mode» → «RTTY» → «Custom». Выставляем следующие настройки на вкладке «Tx»:

qyey3cxqfrwbsw7k6pqds49vkva.jpeg


Сохраняем настройки и закрываем конфигурационное окно.
Выбираем «File» → «Audio» → «Playback» и указываем путь до экспортированного аудиофайла «WAV». На вопрос о зацикливании воспроизведения отвечаем утвердительно. Настраиваем приёмник и «ловим ключ»:

akmlq5u1i8s6mvp7yjmorvhnx5i.jpeg


На этой ноте данный WriteUp заканчивается, но огорчаться не стоит: впереди ещё много времени, чтобы наточить свои навыки для победы в конкурсах и олимпиаде по кибербезопасности, которые (я уверен) точно будут в рамках МиТСОБИ.
Большое спасибо организаторам и каждому из тех, кто принимал участие в создании NeoQUEST-2018.
До встречи на «очной ставке» :)

© Habrahabr.ru