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



И вновь завершился очередной отборочный online-этап ежегодного соревнования по кибербезопасности — NeoQUEST-2018.

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

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

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

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

Далее у нас несколько вариантов. Первый — мой традиционный. Второй — правильный.

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


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


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


    Первый ключ сразу бросается в глаза. Продолжаем изучать структуру файла. Обращаем внимание на файлы: button.cs, key_part_N и ветку "*** ***".

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

    Наc вежливо просят перевести высоты в HEX. Интересно, о каких высотах речь? Теперь настало время посмотреть телевизор на рисунок кнопки:


    Разноцветные «ступеньки»… и сколько их? Ровно 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». Кажется, что мы где-то уже видели эти звездочки. Бинго!


    Меняем звёздочки в функции на найденный текст. Переписываем всё, например, на 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. Что бы это могло значить? Откроем текстовым редактором.


Ничего не понятно. Какие-то символы и модели телефонов. Попробуем «загуглить» первое попавшееся слово — "btsnoop"

Ага. Это дамп трафика сетевого взаимодействия Bluetooth устройств. Нужно снова воспользоваться силой земли и найти все инструменты, способные анализировать сетевой трафик. Самая популярная из таких программ — Wireshark. Устанавливаем. Открываем наш файл — 1513 сетевых пакетов.

Что с этим делать дальше? RTFM и только хардкор!

Немного пролистав пакеты, можно заметить, что идёт обмен между Galaxy S6 (смартфон) и неким устройством — ActionsS_32:8f:a3 (TS007). Снова "гуглим". Беспроводная гарнитура.

Сделаем предположение, что между устройствами должна идти передачи звукового файла.
Wireshark позволяет в автоматическом режиме отследить очередь пакетов передачи аудиофайлов: Telephony -> RTP -> RTP Streams


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


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

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

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


Видим, что в таблице есть 5 зашифрованных записей разной длины. Но как их восстановить и получить SecondKey?

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


А вот и его папка, где следует искать данные. Переходим в неё и открываем файлы «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». Запускаем проект.


На выходе мы получаем полином формирования псевдослучайной последовательности РСЛОС. Да, он такой страшный. Да, будем работать через 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 секунд, удаляем левый столбец и оставляем только значения.

Повторно импортируем:


Сразу заметно, что Audacity не понимает «сырые данные» сверх нормированных (от -1 до 1) значений.

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


Очень краси… Стоп! А почему весь файл длится 0,135 секунд? Там, кажется, около 10 секунд было.

Ещё раз выделяем всю аудиодорожку и выбираем «Эффекты» -> «Смена скорости». Выставляем следующие значения:


Вот теперь хорошо! Экспортируем в аудиофайл с расширением «WAV».

Запускаем fldigi: «Op Mode» -> «RTTY» -> «Custom». Выставляем следующие настройки во вкладке «Tx»:


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



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

Большое спасибо организаторам и каждому из тех, кто принимал участие в создании NeoQUEST-2018.

До встречи на «очной ставке»




К сожалению, не доступен сервер mySQL