Метод восстановления данных с диска, зашифрованного NotPetya с помощью алгоритма Salsa20
Атаки с использованием вирусов-шифровальщиков стали настоящим трендом 2017 года. Подобных атак было зафиксировано множество, однако самыми громкими из них оказались WannaCry и NotPetya (также известный под множеством других имен — Petya, Petya.A, ExPetr и другими). Освоив опыт предыдущей эпидемии, специалисты по всему миру оперативно среагировали на новый вызов и в считанные часы после заражения первых компьютеров принялись изучать экземпляры зашифрованных дисков. Уже 27 июня появились первые описания методов заражения и распространения NotPetya, более того — появилась вакцина от заражения.
Если при запуске NotPetya не смог получить административные привилегии, он зашифровывал алгоритмом AES только пользовательские файлы, но операционная система продолжала работать. К сожалению, для восстановления файлов, зашифрованных таким способом, требуется знание секретного ключа RSA (который вроде бы предлагают купить в Даркнете за 100 биткойнов).
Изложенный далее метод восстановления данных применим в тех случаях, если вирус NotPetya имел административные привилегии и зашифровал жесткий диск целиком при помощи алгоритма Salsa20.
Как оказалось, авторами вируса была допущена ошибка в реализации алгоритма Salsa20, вследствие которой половина байтов ключа шифрования вообще никак не использовалась. Сокращение длины ключа с 256 до 128 бит, к сожалению, не оставляет надежд на отыскание его в разумные сроки.
Однако из-за некоторых особенностей применения алгоритма Salsa20 возможно восстановление данных без знания ключа.
Как работает Salsa20
Напомним, что Salsa20 — синхронный потоковый шифр, в котором при зашифровании генерируется гамма, зависящая от ключа, и байты этой гаммы складываются при помощи операции XOR с байтами открытого текста. Для расшифрования требуется повторить процедуру.
Чтобы гамму можно было вычислять для любого смещения в потоке, генератор гаммы s20_expand32() формирует 64-байтовый массив keystream, в который вперемешку укладываются:
- 256 бит (32 байта) ключа шифрования,
- 8 байт несекретной случайной последовательности nonce (number used once),
- 16 байт константы sigma (“expand 32-byte k” или “-1nvalid s3ct-id”),
- 64 бита (8 байт) номера блока в потоке.
Вот на этой иллюстрации, взятой из отчета Check Point, наглядно показано, как разложены данные:
64 байта keystream пропускаются через функцию перемешивания, и получившиеся 64 байта используются как фрагмент гаммы.
Следует отметить, что генерируемые фрагменты гаммы всегда выровнены на границу, кратную 64 байтам. И чтобы зашифровать, скажем, 7 байт начиная со смещения 100, надо найти номер блока, в который попадает первый байт (100/64 == 1), вычислить для этого блока гамму и использовать из нее 7 байт начиная со смещения (100%64 == 36). Если байтов в блоке не хватает, тогда генерируется гамма для следующего блока и т.д.
В процессе шифрования одного потока (а диск, с точки зрения NotPetya, это один поток) не происходит смены ключа или nonce. Следовательно, для каждого зашифрованного диска единственная варьируемая величина в keystream — это номер блока.
По задумке авторов шифра Salsa20, 2^64 блоков по 64 байта позволяют сгенерировать гамму с периодом 2^70 ~ 10^21 байт. Это достаточно большой период для почти любых практических применений, и жестких дисков такого объема не появится еще очень долго.
Однако в реализации не все так гладко.
Реальный период гаммы в NotPetya
Посмотрим на прототип функции s20_crypt32(), через вызовы которой и выполняется шифрование секторов диска.
enum s20_status_t s20_crypt32(uint8_t *key,
uint8_t nonce[static 8],
uint32_t si,
uint8_t *buf,
uint32_t buflen)
Через аргумент si (вероятно, Stream Index) передается байтовое смещение в потоке. И по типу аргумента понятно, что там не 64, а только 32 бита. Это значение попадает в keystream после деления на 64, то есть остается максимум 26 бит.
// Set the second-to-highest 4 bytes of n to the block number
s20_rev_littleendian(n+8, si / 64);
А теперь посмотрим на еще одну картинку, взятую из того же отчета.
На ней серым цветом выделены те байты, которые не влияют на формирование гаммы вследствие ошибки реализации функции s20_rev_littleendian(). Таким образом, из 26 бит номера блока только 16 бит (байты по смещению 0x20-0x21) повлияют на keystream. Следовательно, максимальный период гаммы составит 2^16=65536 блоков по 64 байта, или 4 мегабайта.
Объем зашифрованных данных наверняка значительно превышает 4 мегабайта, поэтому много разных кусков данных окажутся зашифрованы на одних и тех же фрагментах гаммы. А это позволяет реализовать тривиальную атаку на основе известного открытого текста.
И еще одна ошибка
На этом огрехи разработчиков не заканчиваются. При вызове функции s20_crypt32() они вместо значения смещения в байтах передают… номер 512-байтового сектора!
Секторы обычно зашифрованы парами (1024 байта за одно обращение), и значит, гамма применяемая для зашифрования двух соседних пар секторов, будет совпадать в 1022 байтах (со смещением в 2 байта).
Эвристики для Known Plaintext Attack
Современные версии Windows используют файловую систему NTFS, в которой применяется довольно много различных структур, львиную долю полей в которых можно легко предсказать.
Кроме того, на диске присутствует множество файлов, содержимое которых легко предсказать (частично или полностью).
Вступительные 512 байт гаммы
Для проверки правильности ключа шифрования NotPetya зашифровывает сектор 0x21, содержащий предопределенные значения (все байты 0x07). Это дает нам 512 байт гаммы.
Восстановление гаммы по MFT
NotPetya не зашифровывает первые 16 записей в MFT (32 сектора), но зашифровывает все остальные.
Известно, что каждая файловая запись начинается с последовательности “FILE”, затем обычно идут байты 30 00 03 00 (UpdateSequenceArrayOffset = 0x30, UpdateSequenceArrayLength = 3). Теоретически, эти 4 байта могут иметь и другие значения, но они почти всегда одинаковы для всех файловых записей в пределах одного логического тома NTFS.
Таким образом, из одной файловой записи (занимающей два сектора) мы можем получить 8 байт гаммы, а каждая соседняя запись даст нам два дополнительных байта (и возможность проверить 6 полученных ранее байтов). Последние записи почти полностью состоят из нулей, что может дать еще до 1024 байт гаммы.
А восстановив фрагменты гаммы, использованные для шифрования MFT, мы сможем полностью восстановить структуру файловой системы.
Восстановление гаммы по известным файлам
Вымогатель шифрует и первые два сектора каждого файла, который длиннее 1024 байт. При этом размер кластера обычно больше, чем два сектора (например, 8). В таком случае, найдя зашифрованное начало любого файла и пропустив 1024 байта, мы можем легко получить следующие 3 килобайта в незашифрованном виде. И если у нас будет файл, в котором по смещению 1024 байта от начала находятся точно такие же 3 килобайта — с большой вероятностью и начало файла совпадет. И мы получим еще до 1024 байт гаммы.
Если поставить «чистую» Windows XP и заглянуть в папку Windows, там можно найти 8315 файлов. В активно используемой Windows 8.1 файлов будет больше 200 тысяч. Шансов, что многие из них совпадут с файлами на зашифрованном диске, более чем достаточно.
Так что, если проиндексировать DLL- и EXE-файлы из доступных инсталляций Windows (желательно той же версии и с близким набором обновлений), — возможно, этого хватит, чтобы восстановить гамму полностью.
А получив фрагменты гаммы, можно пытаться восстанавливать и уникальные файлы.
Перспективы и подводные камни
Сложность «ручного» восстановления зашифрованного диска в том, что этот процесс занимает значительное время (часы) и требует больших объемов свободного дискового пространства. Мало у кого из пользователей есть пустой диск, объем которого не меньше, чем объем зашифрованного. А ставить эксперименты на пострадавшем оригинале сродни самоубийству.
Так что маловероятно, что в ближайшее время появится утилита, которая позволит все восстановить «быстро и без SMS». Скорее можно ожидать появления услуги по более полному восстановлению данных — в сравнении с тем, что предлагается сейчас.
С задачей разработки сопутствующего ПО скорее справятся компании, специализирующиеся на data recovery. У них должен быть приличный опыт в решении подобных задач.
Однако не надо забывать, что алгоритм выбора секторов, которые будут зашифрованы (а значит, нуждаются в расшифровании) тоже содержит ошибки (например, при разборе структур NTFS), и это может сказаться на результате.
Восстановление данных с жесткого диска по этой методике требует применения эвристик. Степень восстановления зависит от многих факторов (размера диска, степени его заполнения и фрагментации) и может достигать 100% для дисков большого объема, содержащих много «публичных» файлов (компонентов операционной системы и программных продуктов, одинаковых на многих машинах).
Повторим, что предложенный в статье метод, к сожалению, не позволит расшифровать файлы, которые были зашифрованы алгоритмом AES, используемым NotPetya, если он при запуске не смог получить административные привилегии.
Хочется выразить благодарность Александру Песляку (Solar Designer) за наводящие подсказки, позволившие придумать описанный выше метод.