Как мы (почти) победили DirCrypt
Перевод статьи от компании Check Point«s Malware Research Team.
DirCrypt — один из самых злостных видов вымогающей деньги малвари. Она не просто зашифровывает все найденные пользовательские файлы, требуя выкуп за их расшифровку, но и остается в системе, подхватывая и зашифровывая сохраняемые пользователем файлы на лету. После ее появления рабоат на компьютере превращается в мучение.
Жертвам подобных вредоносных программ обычно рекомендуют восстановить файлы из раннего бэкапа. Но если такого бэкапа нет, перед нами остается трудный выбор — смириться с потерей данных или заплатить злоумышленнику. Однако, нам (Check Point«s Malware Research Team) удалось найти способ в случае DirCrypt восстановить почти все данные, воспользовавшись его слабыми сторонами в реализации шифрования.
Типичная жертва DirCrypt узнает об атаке только по факту ее совершения. Малварь прочесывает жесткие диски в поисках документов, изображений и архивов. После этого к имени найденных файлов добавляется строка ».enc.rtf». Если вы попытаетесь просмотреть их содержимое в «сыром» виде — то не увидите и следа старых данных. А после открытия этого файла как RTF-документа перед вами предстанет инструкция по выплате денег мошеннику.
Файловая система до шифрования файлов
Файловая система после шифрования файлов
Здесь мы и начинаем наше расследование. Дизассемблированный код программы запутан и обфусцирован, и нам предстоит разыскать в нем код, выполняющий шифрование. Поскольку к каждому обработанному файлу добавляется суффикс ».enc.rtf», можно сделать логический вывод о том, что где-то в районе шифрующих функций мы увидим ссылку на эту строку. Но после беглого осмотра в Hex-Rays» IDA-Pro мы обнаруживаем, что большинство строк и бинарнике обфусцированы. Таким образом, перед нами встает первая задача: расшифровать эти строки.
Один из способов поиска зашифрованных строк — просто изучить все секции на предмет наличия блока на первый взгляд рандомных данных. После этого нужно найти функцию с кросс-ссылкой (DATA XREF) на этот кусок бинарных данных. Если в эту функцию передается индекс, находящийся в неких ограниченных пределах, но мы наши, что искали.
Найти область с зашифрованными данными оказалось довольно легко — это большой чанк в секции .data.
Зашифрованные строки
Изучив перекрестные ссылки на этот блок данных, мы узнаем, что к нему обращается несколько функций, одна из которых вызывается в коде аж 170 раз. Обращаясь непосредственно к местам ее вызова, мы видим, что в нем первым параметром передается индекс. Бинго, мы тебя поймали!
Сам код для расшифровки строк довольно длинный и сложный. Так как наша цель — расшифровать строки как можно скорее, мы не стали реверсить этот алгоритм, а написали небольшой скрипт для Windbg, который сделал сложную работу за нас:
.while (@$t0 < 0xe1) { .printf "\n%02x”, @$t0; r eip = 0x40456a; p; p; p; eb esp @$t0; p; du @eax; r $t0 = @$t0 + 0x01 } Этот код в цикле вызывает расшифровывающую функцию, передавая в ее по очереди значения индекса из диапазона, а сами расшифрованные строки выводятся в окно терминала.
Пример вывода декодированных строк Windbg
Теперь нам надо перенести полученные строки в IDA-Pro для удобства статического анализа. Мы решили включить их в текст в виде комментариев в тех местах, где они используются. Для этого на IdaPython был написал небольшой скрипт, который принимает на вход файл («strings.txt») с дампом вывода Windbg и вставляет строки туда, где вызывается функция по их расшифровке. Теперь мы можем быстро перемещаться по перекрестным ссылкам на интересующие нас строки из диалога DecryptString.
Теперь в таблице таких ссылок найдем ».enc.rtf» и обнаружим, что она используется лишь в одном месте. Поскольку суффикс добавляется к файлам после шифрования, можно смело предположить, что мы близко подобрались к самому коду, выполняющему его.
Код функции, которая ссылается на ».enc.rtf», довольно нагляден, да и IDA-Pro сделала часть работы за нас, правильно определив ее аргумент как имя файла. В начале функции вызывается другая функция, после которой происходит получение и декодирование суффикса ».enc.rtf», а затем — переименование файла. То есть видно, что сам процесс шифрования происходит до переименования в той самой первой функции.
Переместившись в ее, мы находим объемный код с повторяющимся паттерном: из файла чанками считываются данные, которые потом изменяются и записываются обратно в файл. Это классическое поведение криптующих функций, так что можно быть уверенным: мы нашли, что искали. Теперь начинается самое интересное.
Внешняя обертка функции, выполняющей шифрование
Малварь в цикле считывает чанки содержимого файла, в памяти зашифровывает их и записывает по тем же смещениям, затирая предыдущие данные.
Цикл, в котором выполняется шифрование
Забираясь немного глубже, мы понимаем, что на самом деле шифрующих функций две. Первая вызывается для каждого чанка прочитанных данных и шифрует, таким образом, файл целиком. В качестве аргумента в эту функцию передается указатель на объект, данные по которому создаются во второй функции. Опытный глаз узнает здесь инициализацию S-блока алгоритма RC4.
Шифрование выполняется для каждого файла по отдельности, и при этом инициализация S-блока выполняется по одному и тому же ключу для каждого файла.
Инициализация и работа алгоритма RC4
Если вам когда-либо приходилось видеть ляпы в криптографии, здесь вы просто не поверите своим глазам. А раз S-блок постоянно инициализируется заново, то для каждого файла используется одинаковый ключевой поток. Нам остается сделать финальный шаг: если мы знаем исходное содержимое зашифрованного файла на компьютере жертвы, то для получения ключевого потока нам нужно побайтово выполнить операцию XOR между оригинальными и зашифрованными данными.
ОК, нам нужно найти файл, который гарантированно имеется на файловой системе Windows. Это, например, стандартные изображения для фона рабочего стола. Их размер — около 100 Кб, так что мы, не потратив значительных усилий, можем получить ключевой поток такого же размера.
И только эта мысль промелькнула у нас в голове, как нам в глаза бросился следующий код:
Дописывание ключа RC4 в конец зашифрованного файла
Подобрав с пола челюсти, мы начинаем всерьез жалеть автора этой недоделанной малвари. Вероятно, находясь в замешательстве относительно того, где сохранить ключ, он каким-то образом решил дописать его в конец файла, где каждый сможет его найти. Почему-то эта идея показалась ему подходящей.
Для нас это тоже выгодно: теперь мы сможем воспользоваться тем же RC4, чтобы полностью расшифровать файл.
Но постойте. Здесь перед нами встает другая проблема: для шифрования файлов используется не только RC4.
Шифрование первых 1024 байт RSA
Первый чанк размером 1024 байта шифруется по алгоритму RSA. Внутри файла приватный ключ не сохраняется, так что одним из способов его получения является выплата денег злоумышленнику в обмен на ключ. Предполагая, что денег мы платить не станем (и атаковать сервер выдачи ключей не будем), единственным выходом остается спасти все, кроме первых 1024 байт.
В ряде случаев (в зависимости от формата восстанавливаемого файла) эту задачу можно успешно решить. Давайте для примера возьмем стандартный .doc-файл. В нем, начиная со смещения 0×1A00, находится текст файла в юникоде. Дадим DirCrypt зашифровать наш экспериментальный файл и сравним его содержимое «до» и «после»:
Экспериментальный документ, созданный в Microsoft Word
Документ в hex-редакторе до шифрования
Документ в hex-редакторе после шифрования
В случае .doc-файлов мы написали несложный скрипт на Python, который извлекает RC4-ключ, расшифровывает с его помощью файл (кроме первых 1024 байт) и сохраняем текст в ASCII. Запустив его для нашего экспериментального файла, мы смогли полностью восстановить текст документа.
Извлеченный текст
Послесловие
В этой статье мы рассказали о приемах, которые помогут вам найти уязвимость в криптомалвари. Изначальной целью для нас было продемонстрировать, что атаки, которые совершает эта категория вредоносных программ, поддается анализу в большей или меньшей степени, и защищающаяся сторона почти всегда может обнаружить и воспользоваться неудачными ходами злоумышленника.