TimeLock (https://www.algomachines.com /) - это бесплатная программа, разрабатываемая для облегчения безопасной временной блокировки файлов размером до 10 КБ, причем разблокировка возможна только во время временное окно, указанное пользователем при первоначальной блокировке.

В текущей итерации (V3.1) блокировка требует, чтобы пользователь предоставил входной файл, пароль, а также дату начала и окончания, в течение которых сейф может быть открыт. Проверка времени основана на цепочке блоков биткойнов, поскольку программа подключается к нескольким узлам биткойнов и запрашивает в файлах заголовков последние блоки.

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

Вызов V3.1

Это было 9-е задание (вы можете просмотреть все здесь: https://www.algomachines.com/people) с ограничением по времени 26 января (или, другими словами, оно было разработано для открывается 26 числа).

Я не буду вдаваться в подробности общего поведения приложения, поскольку оно не изменилось по сравнению с предыдущей итерацией (отчет можно найти здесь: https://medium.com/@elronvhubbard/timelock-v2-2- проблема-уязвимость-отчет-bb3cd61307c7 ) с той заметной разницей, что был улучшен алгоритм шифрования. Адреса, упомянутые в этом отчете, являются виртуальными адресами, смещенными на 0x0.

Первоначальный анализ

Поскольку файлы локальных ящиков предыдущей итерации были уязвимы для исправления зашифрованных дат начала и окончания (как в предыдущем отчете), моим первым подходом было просто сгенерировать 2 идентичных ящика и изучить зашифрованный вывод. Быстрое сравнение двух сгенерированных файлов в шестнадцатеричном редакторе показывает, что они совершенно разные, в отличие от ранее. Это означает, что при создании каждого сейфа используется какая-то соль. Вместо того, чтобы начать анализировать новые реализованные методы, я решил посмотреть, где происходит расшифровка, и посмотреть, смогу ли я найти там слабые места.

Анти-отладка

В предыдущей сборке приложения анти-отладка присутствовала в виде некоторых временных проверок, которые вызвали бы сбой, если бы выполнение было остановлено на достаточно долгое время. Насколько мне известно, в новой версии ничего особо не изменилось, но я чувствовал, что вылетал быстрее и это раздражало больше, поэтому я решил присмотреться к нему.

Первое, что я сделал в этом отношении, - это открыл в IDA таблицу импорта и поискал QueryPerformanceCount. Он всегда используется только в одной единственной функции, и если мы используем IDA для генерации псевдокода для функции, это будет выглядеть так:

Кажется, что эта функция запускается снова и снова с фиксированным интервалом, и если задержка между запусками не соответствует точному интервалу, файл cookie безопасности отравлен и приложение не работает. Чтобы обойти это, мы можем просто удалить инструкцию внутри самого внутреннего «if». Это достигается заменой инструкции cmovz rax, rcx длиной 4 байта на 4 байта NOP (без операции). Патч необходимо применить к смещению файла 21F3F8:

48 0F 44 C1 (cmovz rax, rcx) - ›90 90 90 90 (ноп ноп ноп ноп)

Однако после сохранения изменений и повторной попытки отладки у нас все еще происходит сбой. Чтобы найти этот второй сбой, я начал смотреть, какие потоки выполнялись во время процесса дешифрования, и заметил, что по крайней мере два потока зацикливали эту названную функцию:

Итак, здесь мы снова видим, что было выполнено сравнение, проверяющее, меньше ли прошедшее время между предыдущим измерением (сохраненным в qword_2EA048) и текущим измерением порогового значения. Так что давайте просто исправим это, чтобы он всегда думал, что он ниже. Поскольку мы хотим, чтобы код всегда брал основную ветвь «if», мы можем просто удалить инструкцию перехода (путем ее NOP). Патч необходимо применить к смещению файла 43251:

76 0E (jbe 43E61) - ›90 90 (ноп ноп)

Аааа и теперь, если мы попытаемся снова запустить код в отладчике, мы сможем делать паузы, сколько захотим, и ничто нас никогда не беспокоит. Это делает анализ и тестирование намного более удобными.

Обнаружение уязвимости

Поскольку я тщательно проанализировал предыдущую задачу, я знал, где находится последняя функция дешифрования, место, где выполняются важные проверки даты и времени. Найти ту же функцию в новой версии было несложно, потому что начальная часть кода не изменилась (и я этого не ожидал), поэтому мне нужно было только сгенерировать байтовый шаблон в V2.2 и найти тот же байт. шаблон в V3.1. Это длинная сложная функция, которая начинается с виртуального адреса 44F40. В предыдущем задании я не много анализировал, поэтому сейчас я сосредоточился на этом.

Теперь это самая интересная часть кода:

В последней строке кадрированного изображения мы видим, что «результат» равен 1, если все происходит по плану, то есть, если декодирование прошло успешно. Если мы начнем рассматривать функции до этого, одна из них - это та, которую я назвал «magic_decode_function» (чтобы я мог легко ее найти), с адресом 69A70. Вот он:

Так что очевидно, что это будет нелегко понять, просто взглянув на это, но я играл с отладчиком снова и снова, с разными тестовыми ящиками, некоторые из которых открываются, а некоторые заблокированы, чтобы увидеть, что там происходит. .

Прежде всего, важно отметить, что v25 содержит зашифрованный период текущей даты, рассчитанный непосредственно из цепочки блоков. Я не понял, где это происходит, потому что приложение запускает десятки потоков для подключения к одноранговым узлам, и отслеживать их все немного затруднительно. После инициализации v25 из файла lockbox извлекается довольно много параметров, и все они используются вместе для вычисления магического числа, обозначенного здесь v27. Это число затем передается в функцию sub_BC70, которая выполняет с ним некоторые операции шифрования. Наконец, зашифрованный вывод проверяется на соответствие зашифрованному периоду цепочки блоков, и если они отличаются, возвращается время цепочки блоков.

Теперь переходим к инструкции

v27 = v22 * v22 + этаж (v17)

отладчик ясно показывает, что v22 не зависит от времени (текущая дата не играет роли в его значении), поэтому единственное, что зависит от времени, - это v17.

Теперь тестирование ящиков с замками в отладчике показало мне важную вещь о v17, а именно интервал, которому он должен соответствовать для разблокируемого ящика:

-0.5 ≤ v17 ≤ 0.0

Теперь, зная, какое значение имеет открываемый ящик, и зная, что, если локально вычисленное значение отличается от значения блокчейна, блокчейн имеет приоритет, мы готовы открыть наш ящик задач.

Открытие сейфа

Мы собираемся сделать это с помощью отладчика. Перво-наперво, давайте удостоверимся, что переменная v17 находится в диапазоне от -0,5 до 0. Чтобы контролировать значение переменной, мы хотим установить точку останова на смещение файла 6915F и еще один в смещении файла 691D2. Как только оба будут установлены, вы можете открыть файл запроса и ввести правильный пароль (77C8C66C-4452–41DF-A99A-599C9CA4AE11) и дождаться срабатывания первой точки останова. Когда он срабатывает, нам нужно изменить регистр XMM0. Мы можем использовать онлайн-конвертер двойного в шестнадцатеричный, например https://gregstoll.com/~gregstoll/floattohex/, чтобы преобразовать, скажем, -0,25 в 0xBFD0000000000000. Мы хотим изменить значение XMM0 с любого на 0xBFD0000000000000.

Затем мы можем продолжить выполнение, пока не будет достигнута вторая точка останова. Теперь мы хотим взять первую ветвь (мы не хотим, чтобы прыжок запускался), поэтому один из способов убедиться, что мы всегда берем первую ветвь, - это просто исправить прыжок в инструкции NOP, как и для анти-отладки. . Мы можем сделать это во время выполнения (в памяти) или мы могли бы сделать это в файле перед запуском исполняемого файла. Выбор остается за вами. Как только патч будет установлен, вы можете удалить точку останова и возобновить выполнение.

Хорошо, мы закончили. Теперь есть небольшая деталь, о которой я пока не упоминал, а именно то, что эта функция дешифрования запускается 3 раза (скорее всего, потому, что она пытается трижды проверить результаты блокчейна). На нас это не сильно влияет, это просто означает, что нам придется перезаписать регистр XMM0 в первой точке останова еще два раза. Итак, мы снова попадаем в первую точку останова и перезаписываем, используя то же значение, что и в первый раз. А потом снова продолжаем выполнение, и снова мы на том же месте, и перезаписываем регистр. Наконец, мы продолжаем выполнение, и, если все прошло хорошо, нас должно приветствовать окно просмотра папок, которое запрашивает у нас место для сохранения нашего расшифрованного файла. Успех!

Примечания и заключительные мысли

  1. В приложении * все еще * есть ошибка, из-за которой пользователь не может выбрать даты начала и окончания. Под этим я подразумеваю, что даты, выбранные пользователем, не принимаются во внимание.
  2. Хотя я не потратил много времени на изучение текущей схемы шифрования, я считаю, что более безопасная окончательная проверка вместе с обфускацией кода (и, возможно, более сильная защита от отладки / взлома) являются наиболее важными вещами, которые можно улучшить в будущем. итерация.

Хочу поблагодарить автора за этот веселый и чрезвычайно интересный вызов. Я очень ждал этого пару месяцев. Надеюсь, этот отчет поможет сделать TimeLock безопасным и нерушимым, и, конечно же, я буду с нетерпением ждать будущих проблем.