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!
SHA-256: e32c8b2da15e294e2ad8e1df5c0b655805d9c820e85a33e6a724b65c07d1a043
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.
Вверх