Overview
Packed Sample
: https://bazaar.abuse.ch/sample/03b9c7a3b73f15dfc2dcb0b74f3e971fdda7d1d1e2010c6d1861043f90a2fecd/
Unpacked Sample
: https://bazaar.abuse.ch/sample/d3c75c5bc4ae087d547bd722bd84478ee6baf8c3355b930f26cc19777cd39d4c/
MD5
: B3D6BA0AA663F699283D25DDCB6561B9
SHA 256
: D3C75C5BC4AE087D547BD722BD84478EE6BAF8C3355B930F26CC19777CD39D4C
This post deals with the unpacked sample.
Basic Static Analysis
According to PE Bear as well as IDA, the ransomware imports three DLLs namely
kernel32.dll
,USER32.dll
andWS2_32.dll
.Interesting data found by extracting strings from the executable
1
2
3
4
5
6
7
8
9
10
11
12
13Our website
TOR VERSION :
(you should download and install TOR browser first https://torproject.org)
http://m232fdxbfmbrcehbrj5iayknxnggf6niqfj6x4iedrgtab4qupzjlaid.onion
HTTPS VERSION :
contirecovery.best
YOU SHOULD BE AWARE!
Just in case, if you try to ignore us. We've downloaded you
---BEGIN ID---
sRttGzzkzsoiC9s8LgcrQk64ew7H47a5JSjCsLGbwdijogjulfu3RO9XBJbfEgCZ
---END ID---
.CECJF
RSA1
- This reveals the name of the malware
A:\source\conti_v3\Release\cryptor.pdb
Advanced Static Analysis
String Decryption
Analysing the function sub_401010
in IDA, we notice some stack strings and their corresponding decryption loop. We can extract those strings and write a decryptor.
1 | enc_s_1 = [1,16,47,57,16,62,14,80 ,90 ,82 ,62, 62, 78] |
Running this script gives us the names of some DLLs. However, PE Bear was able to detect only three DLLs. This means that the program might be loading these DLLs manually using some API function.
1 | λ python decryptor.py |
After decrypting the dll names, the malware calls sub_955650(15, 0xBE3D21A8, 107)
, and calls the pointer returned by it with the first argument as a pointer to the dll name. Please note that the function name may be different in different instances of the program.
1 | v26 = sub_951B80((unsigned __int8 *)v45); |
Stepping into this function call using dynamic analysis, we find that this call is nothing but a call to LoadLibraryA
with the first argument as the dll name.
This means that the call sub_955650(15, 0xBE3D21A8, 107)
returns a pointer to LoadLibraryA
which is eventually called with the first argument as the decrypted dll name, subsequently returning a handle to the particular dll. Further, handles to the DLLs are passed to the function sub_951730
which calls sub_955650
with some different arguments. First of all, let’s find out some of the functions that are returned by subsequent calls to sub_955650
. I’ve renamed this function to mw_resolve_api
.
1 | mw_resolve_api(15, 0xBE3D21A8, 107) -> LoadLibraryA |
Command Line Arguments
Decrypting enc_s_13 by calling decrypt_string_1(enc_s_13,1,26,113)
gives - p
which is possibly a command line argument. The function sub_11A7810(char*)
parses various command line arguments. Let’s find out the possible values.
1 | cmd_enc_1 = [38,113,11,113,113,113] |
Running this script gives the following output:
1 | - p |
Conti can only be ran with command-line arguments, so there must be a loader for supplying appropriate command line arguments and launching the malware. Further investigation yields the following setup:
Argument | Functionality |
---|---|
-nomutex | Do not use mutex |
-p dir_path | Encrypt all the files in a directory whose path is given by dir_path |
Let’s analyze each command line argument, one by one
-p dir_path
mw_wrap_RtlInitializeCriticalSection(0, 1)
initializes a critical section and mw_wrap_CreateThread(0)
spawns a number of threads. In this case (when you use the -p flag), only 1 thread is spawned by the malware. I’ve renamed the function executed by the thread to mw_fn_thread_1
. It calls a function sub_1AB520(&var_cryptoPov)
which initially inserts some characters into a buffer. I’ve renamed the buffer to var_crypt_provider
, and after that performs a string decryption.
1 | enc_sth = b"\x11NJTR\x00RL+r6'#s'J!uri\x15\trs'ur\t6\x15r_T\x04}+RwTs}#NJr\x13TR\x02Nu!T\x08" |
This gives Microsoft Enhanced RSA and AES Cryptographic Provider
. This Cryptographic service provider supports various algorithms such as Triple DES, AES 128, AES 192, MAC,etc. For more information, check this. After that, the malware calls CryptAcquireContextA(arg_phProv, 0, var_crypt_provider, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)
. If it succeeds, the function returns, otherwise it sets up a new buffer, decrypts it to get the provider Microsoft Enhanced RSA and AES Cryptographic Provider
and calls CryptAcquireContextA
again. If it fails, the malware again sets up a buffer, decrypts it and calls CryptAcquireContextA
. This entire thing can be done at most 4 times.
After that, the malware calls CryptImportKey
with the second argument as a global buffer which might be a public RSA key.
Following different functions over here, we ultimately get something interesting.
Let’s decrypt the blob of code as seen in the image above.
1 | enc_sth_5 = b'\x10}"}\x14}^}>}"} }\x17}%}\x17}}' |
This gives us the output r e a d m e . t x t
. If you run the malware standalone, with the -p
flag, you’ll notice that the decryption instructions are stored in this file.
The malware creates a file readme.txt
in the directory supplied as a command line argument, and writes the data stored in a global buffer into it.