Order Of Six Angles

Main Logo

A security researcher's blog about reverse-engineering, malware and malware analysis

Home | RU | Translations | Tools | Art | About

9 March 2021

tags: malware analysis

Анализ шифровальщика XCRY написанного на Nim

Не так давно, публично стало известно о 32-битном шифровальщике написанном на языке Nim, под названием XCRY. Спасибо MalwareHunterTeam!

VirusTotal

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 и соответствующий образец.

Вверх