tags: malware analysis
Анализ шифровальщика XCRY написанного на Nim
Не так давно, публично стало известно о 32-битном шифровальщике написанном на языке Nim, под названием XCRY. Спасибо MalwareHunterTeam!
SHA-256: e32c8b2da15e294e2ad8e1df5c0b655805d9c820e85a33e6a724b65c07d1a043
Образец не упакован и содержит много Debug информации. Имя шифровальщику было дано по тэгу добавляемому к зашифрованным файлам.
В сети есть краткое описание шифровальщика (некоторые моменты оттуда здесь описаны не будут), но не показана внутренняя работа. Цель данной статьи заполнить этот пробел. Как уже было сказано ранее, из-за присутствия в образце информации, оставленной после компиляции, мы можем видеть понятные имена функций. Вообще, данный факт позволяет нам понять как выглядит Main функция программ на Nim и в дальнейшем, если мы рассматриваем малварь скомпилированную без Debug информации, мы сможем выявить нужные функции. Для примера, на скрине слева - Xcry, справа рандомная малварь, тоже написанная на Nim, но скомпилированная без Debug. На нем визуально выделяются одинаковые функции
Кстати, чтобы такого не происходило, можете найти инструкцию по минимальной компиляции Nim программ в этой статье.
В функции PreMainInner() происходит инициализация всех используемых библиотек
Среди которых можно заметить библиотеку nim libsodium - обертка над libsoidum, которая является основой для работы шифровальщика.
Функция PreMainInner() очень важна, так как именно в ней мы можем посмотреть реальные импорты.
Строки в Nim имеют свой кастомный тип - NimString
. Они имеют динамический размер, поле с указанием длины строки и завершаются нулем. Также непосредственно перед строкой стоит байт 0x40
Строки в языке Nim могут быть созданы как объекты функцией newObjRC1
и как просто память в куче
proc boehmAllocAtomic(size: int): pointer {.
importc: "GC_malloc_atomic", boehmGC.}
Nim программы получают адреса используемых функций в рантайме. nimLoadLibrary
- обертка над функцией LoadLibrary
.
add ecx, 8
- взятие “реальной” строки kernel32 (картинка выше) по смещению 8.
Для прямого вызова WinApi функций, в образце используется библиотека Winim, она облегчает написание кода. Здесь можно посмотреть примеры использования. Ниже можно увидеть получение адресов функций.
Весь этот код генерируется компилятором. Из-за этого статический анализ (просмотр импортов) дает неполную информацию. Возможно данный факт немного помогает в написании малвари на Nim. Например, CreateProcessW
отсутствует в импортах, но используется во время работы во вредоносных целях, как и FindFirstFile
для прохода по файлам.
Проверка повторного запуска происходит путем проверки существования файла lock_file
. Данный файл создается по окончанию всей работы.
Xcry содержит публичный ключ, который используется библиотекой libsodium
. Он хранится в памяти в виде строки и потом превращается в байтовый массив функцией hex2bin
в libsodium
При старте копирует себя в APPDATA
под рандомным именем.
Это имя генерируется функцией randombytes библиотеки libsodium
Интересно, что шифровальщик не перебирает подключенные диски, а имеет заранее приготовленную строку с потенциальными буквами диска. Начиная с буквы Z
, которая должна соответствовать сетевым дискам.
Каждый найденный диск шифруется в отдельном потоке, который был создан функцией Spawn в Nim.
Теперь переходим к основному функционалу. Для шифрования файлов используется алгоритм XSalsa20 (отличается от Salsa20 более длинным nonce - 192 бита вместо 64). В начале, генерируется приватный ключ функцией crypto_stream_keygen
Этот ключ шифруется публичным ключом, функцией crypto_box_seal.
В результате получается значение длиной 0xA1
Далее это значение используется для генерации хэша (ShortHash), функцией crypto_shorthash - которая возвращает случайные 16 байт
Затем, создается строка вида:
Short Hash + пробел + private key + байт 0x0A
Значение сохраняется в папку APPDATA под именем encryption_key
.
Именно этот файл просят злоумышленники от жертвы, чтобы расшифровать данные за выкуп. То есть, у шифровальщика отсутствует функционал для работы с сетью и работа по отправке ключа на почту авторов лежит на жертве.
Что интересно, приватный ключ генерируется для каждого 1000 (0x3E8) файла (на скрине ниже - проверка модуля числа) и каждый такой ключ записывается в файл encryption_key
.
Ниже показана структура файла encryption_key
в виде Kaitai Struct
meta:
id: xcry_encryption_key
file-extension: xcry_encryption_key
seq:
- id: enc_key
type: enc_key_struct
repeat: eos
types:
enc_key_struct:
seq:
- id: short_hash_key
size: 16
- id: inner_delimeter
size: 1
- id: private_key
size: 0xa0
- id: delimeter
size: 1
Далее происходит чтение файла кусками по 0x2710
(10000) байт и его шифрование
Основная функция для шифрования - crypto_secretbox_detached, которая использует сгенерированный ранее случайный ключ.
В начало каждого зашифрованного файла вставляет строка с ShortHash
. Делается это для того, чтобы позже найти нужный приватный ключ для каждого конкретного файла, для расшифровки. На скрине - содержимое зашифрованного файла со строкой ShortHash
Тот же самый ShortHash
в файле encryption_key
Оригинальный файл просто удаляется без перезаписывания, что позволяет просто восстановить файлы. Например вот способ затирания файлов, перед их удалением в open source шифровальщике GonnaCry
Также не удаляются теневые копии.
Вот и все! В итоге, мне было просто интересно посмотреть как выглядят внутренности программ на Nim и то, как может быть написан работающий шифровальщик на этом языке. Надеюсь, я понятно показал оба момента и спасибо, если дочитали до конца.
Если вы хотите посмотреть на то как выглядят другие шифровальщики на Nim, можете обратить внимание на атаку на форум IObit и соответствующий образец.
Вверх