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

Analysis of XCRY ransomware written in Nim

Not so long ago, a 32-bit ransomware written in Nim called XCRY became publicly known. Thanks to MalwareHunterTeam!

VirusTotal

SHA-256: e32c8b2da15e294e2ad8e1df5c0b655805d9c820e85a33e6a724b65c07d1a043

Download sample

The sample is not packed and contains a lot of Debug information. The ransomware has been named by the tag added to the encrypted files.

There is a brief description of the sample but I will try to provide more details. As mentioned before, because of the presence in the sample of Debug information left after the compilation, we can see the clear names of the functions. In general, this fact allows us to understand how the main functions of Nim programs looks like and later, if we analyze a sample compiled without Debug information, we will be able to identify the necessary functions. For example, in the image below we have Xcry and a sample also written in Nim and compiled without Debug. We can see the similarities between the functions

By the way, to minimize binary size you can read this article.

The PreMainInner() function initializes all libraries

Among which you can notice the library nim libsodium - a wrapper over libsoidum, which is the basis for the ransomware.

The PreMainInner() function is very important because it’s where we can see the actual imports.

Strings in Nim have their own custom type - NimString. They have a dynamic size, a field specifying the length of the string and end with a zero. There is also a byte 0x40 immediately before the string

Nim strings can be created as objects with the function newObjRC1

or in a heap memory


proc boehmAllocAtomic(size: int): pointer {.

  importc: "GC_malloc_atomic", boehmGC.}

Nim programs get the addresses of the functions dynamically. nimLoadLibrary is a wrapper over the function LoadLibrary.

add ecx, 8 - take the “real” kernel32 string (image above) at offset 8.

To directly call WinApi functions, the sample uses Winim library, it makes writing code easier. Here you can see examples of how to use it.

All of this code is generated by the compiler. Because of this, static analysis gives incomplete information. Perhaps this fact helps a little in writing malware on Nim. For example, CreateProcessW is not in the imports, but it is used at runtime for malicious purposes, as is FindFirstFile for file traversal.

Re-run checks are done by checking if the lock_file file exists. This file is created at the end of all work.

Xcry contains a public key which is used by the libsodium library. It is stored in memory as a string and then converted to a byte array with the function hex2bin in libsodium.

On startup, Xcry copies itself into APPDATA under a random name.

This name is generated by the randombytes function of the libsodium library

Interestingly, the ransomware doesn’t enumerate the connected drives, but has a hardcoded string of potential drive letters. Starting with the letter Z, which should correspond to network drives.

Each drive found is encrypted in a separate thread, which was created by the Spawn function in Nim.

The XSalsa20 algorithm is used to encrypt the files (it differs from Salsa20 in that it has a longer nonce - 192 bits instead of 64). First, a private key is generated with the function crypto_stream_keygen

This key is encrypted with the public key (function crypto_box_seal).

The result is a value of length 0xA1.

This value is then used to generate a hash (ShortHash), with the function crypto_shorthash - which returns a random 16 bytes

Then, a string of the form is generated:

Short Hash + space + private key + byte 0x0A.

The value is saved in the APPDATA folder under the name encryption_key.

This is the file the attackers are asking for from the victim to decrypt the data for ransom. What is interesting, a private key is generated for each 1000 (0x3E8) files and each such key is written to the encryption_key file.

Below is the structure of the encryption_key file in 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

Next, the file is read in chunks of 0x2710 (10,000) bytes and encrypted

The main function for encryption is crypto_secretbox_detached, which uses a previously generated random key.

A line with ShortHash is inserted at the beginning of each encrypted file. This is done in order to find the right private key for each particular file to decrypt later. Here is a screenshot of the content of the encrypted file with the ShortHash string

The same ShortHash in the encryption_key file

The original file is simply deleted without being overwritten, which makes it easy to restore the files. For example, here’s a way to overwrite files before deleting them in the open source ransomware GonnaCry

Xcry also doesn’t remove shadow copies.

That’s all! I was just curious to see what the insides of Nim programs look like and how a ransomware could be written in that language. I hope I made both points clear and thank you if you read to the end.

If you want to analyze another ransomware written in Nim, you can check out attack on IObit forum and download the corresponding sample.

Вверх