Dissecting the last version of Conti Ransomware using a step-by-step approach

Summary

According to multiple online resources, Conti is one of the most active ransomware families in the last year. One of the infamous attacks happened against HSE healthcare (https://threatpost.com/conti-ransomware-fail-costly/166263/), where the attackers asked for a $20 million ransom. As mentioned by Cybereason at https://www.cybereason.com/blog/cybereason-vs.-conti-ransomware, Conti is sold as a RaaS (Ransomware as a Service) in underground forums, and it has been deployed by the TrickBot gang. One of the major concerns is the fact that the ransomware group employs the double extortion method: they also steal sensitive data from the victim and ask for a ransom. The malware can run with different parameters such as “-p”, “-m”, “-size”, “-log” and “-nomutex” and creates a mutex called “YUIOGHJKCVVBNMFGHJKTYQUWIETASKDHGZBDGSKL237782321344” to ensure that only one instance of the ransomware is running at a single time. The malware deletes all volume shadow copies on the computer using wmic and COM objects. The ransomware targets all valid drives on the system and the SMB shares that could be accessed. The files are encrypted using a custom ChaCha8 implementation that uses randomly generated keys (32 bytes) and nonces (8 bytes), which are encrypted by a hard-coded public RSA key. The encryption is very fast because the sample uses multiple threads to encrypt the files and avoids encrypting larger files entirely (>5MB).

Analyst: @GeeksCyber

Technical analysis

SHA256: 58ca4e482db7cf5c924256e53d8516d422e76cf4b85b43dc2b9ba0c7cb471ff7

The malware does a good job at obfuscating most of the stack strings, and the decryption algorithms used to decrypt them are changing every time (there are some variations of operations and constants). One example of a custom decryption algorithm is shown below:

Figure 1

The important APIs are imported during runtime rather than including all of them in the IAT (import address table). There is a hashing algorithm used to determine which functions should be imported (the first parameter is the hash value, and the second parameter represents an offset):

Figure 2

The process retrieves the command-line string by calling the GetCommandLineW function:

Figure 3

The CommandLineToArgvW API is utilized to obtain an array of pointers to the command line arguments (similar to argv and argc values in C):

Figure 4

The binary can run with different parameters: “-p”, “-m”, “-log”, “-size” and “-nomutex” (we will describe the workflows depending on these parameters in the upcoming paragraphs). It parses the parameters used by the program and compares them with the list, as shown in figure 5:

Figure 5
Command line parametersFunctionality
-p directoryencrypt the directory with a single thread
-m localencrypt the available drives with multiple threads
-m netencrypt the network shares with multiple threads
-m allencrypt the available drives and the network shares with multiple threads
-m backupsNot implemented (maybe encrypt backup directories?)
-size chunkchunk mode for encrypting large files
-log logfilelogging mode (log different activities in a log file)
-nomutexno mutex created

The process decrypts the mutex name using an algorithm similar to the one presented above:

Figure 6

A new mutex is created to ensure that only one instance of malware is running at a single time (if it runs with the “-nomutex” parameter, no mutex is created):

Figure 7

GetNativeSystemInfo is used to get information about the current system, as highlighted in the next figure:

Figure 8

The malicious process creates 2 (the number of processors) new threads that deal with the encryption, as we’ll describe later on:

Figure 9

The CreateToolhelp32Snapshot API is used to retrieve a snapshot of all processes (0x2 = TH32CS_SNAPPROCESS):

Figure 10

The binary enumerates the processes on the system by calling the Process32FirstW and Process32NextW routines:

Figure 11
Figure 12

The malware is looking for the “explorer.exe” process and saves its process ID in a buffer:

Figure 13

The CoInitializeEx routine is utilized to initialize the COM library on the current thread:

Figure 14

CoInitializeSecurity is used to register and set the default security values for the process (0x3 = RPC_C_IMP_LEVEL_IMPERSONATE):

Figure 15

The malware uses COM objects and wmic to delete all volume shadow copies. It calls the CoCreateInstance API to create an IWbemLocator object with the CLSID {dc12a687-737f-11cf-884d-00aa004b2e24}:

Figure 16

The same function is used to create a new IWbemContext interface with the CLSID {44aca674-e8fc-11d0-a07c-00c04fb68820}, as displayed in figure 17:

Figure 17

The binary uses the ConnectServer function to connect to the local “ROOT\CIMV2” namespace and retrieves a pointer to an  IWbemServices object:

Figure 18

CoSetProxyBlanket sets the authentication information that will be used to make calls on a proxy (0xA = RPC_C_AUTHN_WINNT – NTLMSSP, 0x3 = RPC_C_AUTHN_LEVEL_CALL and 0x3 = RPC_C_IMP_LEVEL_IMPERSONATE):

Figure 19

The ransomware obtains an enumerator of all shadow copies by executing the following query “SELECT * FROM Win32_ShadowCopy”:

Figure 20

The following string that is decrypted using a custom algorithm will be used to delete all shadow copies on the system:

Figure 21

The Wow64DisableWow64FsRedirection API is utilized to disable file system redirection for the current thread:

Figure 22

The malware creates a new process to delete the shadow copy corresponding to the specified ID, as follows:

Figure 23

File system redirection for the current thread is restored using a function call to Wow64RevertWow64FsRedirection:

Figure 24

All valid drives on the system are targeted by the malware, which uses the GetLogicalDriveStringsW API to retrieve them:

Figure 25

The WSAStartup routine initiates the use of the Winsock DLL by the current process, as highlighted in the next figure:

Figure 26

The binary creates a new socket via a call to WSASocketW (0x2 = AF_INET, 0x1 = SOCK_STREAM, 0x6 = IPPROTO_TCP):

Figure 27

WSAIoctl is utilized to retrieve a pointer to an extension function supported by the associated service provider (0xc8000006 = SIO_GET_EXTENSION_FUNCTION_POINTER):

Figure 28

The file extracts the standard host name for the local computer by calling the gethostname function:

Figure 29

The local IP address of the local machine is retrieved by calling the gethostbyname routine:

Figure 30

CreateIoCompletionPort is utilized to create an I/O (input/output) completion port that is not associated with a file handle (0xffffffff = INVALID_HANDLE_VALUE):

Figure 31

The ransomware extracts the ARP entries from the local machine and returns the information in a MIB_IPNETTABLE structure:

Figure 32

The IP address in a hex format from each entry is converted to an ASCII string (dotted-decimal format IP address):

Figure 33

The process is interested in local IP addresses only because it compares the IP addresses with the following strings: “172.”, “192.168.”, “10.” and “169.”. One such comparison is shown below:

Figure 34

The binary creates 2 new threads by calling the CreateThread API, and we’ll detail their execution flows in the following paragraphs:

Figure 35
Figure 36

The malware sends an I/O completion packet to the I/O completion port created earlier, as highlighted in figure 37:

Figure 37

Thread activity – sub_10BAF90

The CreateTimerQueue function is utilized to create a queue for timers (objects that specify a callback function to be called at a specific time):

Figure 38

The threads communication is done using the I/O completion port that was created by the main thread. The GetQueuedCompletionStatus function is utilized to receive a buffer sent by the main thread:

Figure 39

The thread creates a new socket by calling the WSASocketW routine (0x2 = AF_INET, 0x1 = SOCK_STREAM, 0x6 = IPPROTO_TCP and 0x1 = WSA_FLAG_OVERLAPPED):

Figure 40

The bind API associates the local address with the newly created socket:

Figure 41

The CreateIoCompletionPort function associates the TCP socket created earlier with the existing I/O completion port (it allows the process to receive notification of the completion of I/O operation involving the socket handle):

Figure 42

The ransomware uses the ntohs routine to convert 0x1bd (445) from network byte order (big endian) to host byte order (little endian):

Figure 43

The process tries to connect to multiple IP addresses on port 445 (192.168.10.x and 192.168.164.x) via the LPFN_CONNECTEX callback function, as highlighted in figure 44:

Figure 44

The CreateTimerQueueTimer API is utilized to create a timer-queue timer. Basically, when the timer expires (0x7530 = 30 seconds in our case), the callback function (0x010BAF60) is called:

Figure 45

Process Monitor captures the network connections initiated by the thread:

Figure 46

setsockopt is used to set the SO_UPDATE_CONNECT_CONTEXT option for the socket (0xffff = SOL_SOCKET, 0x7010 = SO_UPDATE_CONNECT_CONTEXT):

Figure 47

In order to check whether a connection is successful, the malware calls the getsockopt function with the parameter 0x700c = SO_CONNECT_TIME:

Figure 48

The ransomware doesn’t target the network shares available on the local host:

Figure 49

The WSAAddressToStringW routine is used to convert all components of a sockaddr structure into a human-readable string representation:

Figure 50

Thread activity – sub_10BA880

The malicious process retrieves information about the SMB shares on the IPs that were successfully accessed, as shown in figure 51 (here we use the local IP address for debugging purposes):

Figure 51

The “ADMIN$” share will not be encrypted by the malware:

Figure 52

“-log” parameter

If the malware runs with the “-log” parameter, the following strings are constructed and included in the log file:

Figure 53
Figure 54

The binary creates the log file passed as a parameter, as displayed in the next figure:

Figure 55

The current date and time in UTC are retrieved by calling the GetLocalTime API:

Figure 56

The time extracted above is written in the log file:

Figure 57

An example of a log file that contains a list of actions performed by the malware is displayed below:

Figure 58

“-m backups” parameter

This seems to be a testing parameter because there is no additional action performed by the process:

Figure 59

“-nomutex” parameter

No mutex is created if the malware runs with this parameter.

“-m local” parameter

The ransomware creates 2 (the number of processors) new threads. It encrypts all the available drives on the system.

“-m net” parameter

The ransomware creates 2 (the number of processors) new threads. It encrypts all the network shares that could be accessed.

“-m all” parameter

A combination between the 2 cases from above.

“-p directory” parameter

Thread activity – sub_10BC7D0

CryptAcquireContextA is utilized to acquire a handle to the Microsoft RSA and AES Cryptographic Provider, as shown below:

Figure 60

The CryptImportKey function is used to import a public RSA key:

Figure 61

The process creates the ransom note in every directory that it encrypts:

Figure 62

The following 4-byte constants suggest that the encryption algorithm is ChaCha8, as described at https://arxiv.org/pdf/1907.11941.pdf:

Figure 63

The WriteFile API is used to populate the ransom note:

Figure 64

The content of the ransom note is displayed in figure 65:

Figure 65

The malware enumerates the files in the targeted directory using the FindFirstFileW and FindNextFileW APIs:

Figure 66
Figure 67

There is a call to PathIsDirectoryW that checks if the file path is a valid directory:

Figure 68

The following extensions/files are skipped by the malware (“.LSNWX” is the extension of encrypted files):

Figure 69

CryptGenRandom generates 32 random bytes that will be used as the ChaCha8 key:

Figure 70
Figure 71

CryptGenRandom generates 8 random bytes that will be used as the ChaCha8 nonce:

Figure 72
Figure 73

The RSA public key is used to encrypt the ChaCha8 key and the nonce, as shown in the pictures:

Figure 74
Figure 75

The CreateFileW function is utilized to open the files that will be encrypted:

Figure 76

The file extension is compared against a list of multiple targeted extensions (the entire list can be found in the Appendix):

Figure 77

There is a second comparison that occurs between the extensions. Some of them include virtual disk files, ISO files and others:

Figure 78

It’s important to mention that if a file extension doesn’t belong to these lists, that doesn’t mean the file will not be encrypted. For example, the txt file used as an example will be encrypted by the ransomware however, it follows a different execution flow that will be detailed in a few paragraphs. The encrypted ChaCha8 key and nonce are part of the encrypted file, as follows:

Figure 79

There are 3 different cases: small (<1MB), medium (between 1MB and 5MB) and large files (> 5MB) to be encrypted. In the first case, our file has a size of 8 bytes, and then the following 10-byte buffer is appended to the end:

Figure 80

The process reads the content of a file by calling the ReadFile API:

Figure 81

The file content is encrypted using a custom implementation of the ChaCha8 algorithm:

Figure 82

A snippet of the ChaCha8 algorithm implemented by the malware is presented below:

Figure 83

The encrypted buffer has the same size as the initial one:

Figure 84

The WriteFile routine is utilized to fill out the encrypted file:

Figure 85

The file extension is changed to “.LSNWX”, and the encryption activity is complete at this point:

Figure 86

The encrypted file has the following content:

Figure 87

Now, if the file size is between 1MB and 5MB, and the extension doesn’t belong to the targeted lists, the 10-byte buffer is different (note the file size):

Figure 88

In this case, the ransomware only encrypts 1MB of the file:

Figure 89

Whether the file size is between 1MB and 5MB and the extension belongs to the targeted lists, the 10-byte buffer is changing again:

Figure 90

The entire file is encrypted in this case:

Figure 91

In the case of file size that is greater than 5MB and the extension doesn’t belong to the targeted lists, the next buffer will be appended to the encrypted file:

Figure 92

The interesting part of the encryption comes now because the process encrypts 5 chunks of (file size/100 * 10) bytes. In our case, this value is (0x7A1200/0x64*0xa) = 0xC3500 bytes (the malware skips some bytes before encrypting 0xC3500 bytes again):

Figure 93

The last case is when the file size is greater than 5MB, and the extension belongs to the targeted lists. The 10-byte buffer looks like in figure 94:

Figure 94

The value 0x14 allows the ransomware to encrypt files in 3 chunks of (file size/100 * 7). In our case, this value is (0x7A1200/0x64*0x7) = 0x88B80 bytes.

References

MSDN: https://docs.microsoft.com/en-us/windows/win32/api/

VirusTotal: https://www.virustotal.com/gui/file/58ca4e482db7cf5c924256e53d8516d422e76cf4b85b43dc2b9ba0c7cb471ff7/detection

Any.run: https://any.run/report/58ca4e482db7cf5c924256e53d8516d422e76cf4b85b43dc2b9ba0c7cb471ff7/1bc984d4-4772-4a17-93f2-948a769ab7fe

Appendix

List of targeted extensions

  • .4dd
  • .4dl
  • .accdb
  • .accdc
  • .accde
  • .accdr
  • .accdt
  • .accft
  • .adb
  • .ade
  • .adf
  • .adp
  • .arc
  • .ora
  • .alf
  • .ask
  • .btr
  • .bdf
  • .cat
  • .cdb
  • .ckp
  • .cma
  • .cpd
  • .dacpac
  • .dad
  • .dadiagrams
  • .daschema
  • .db
  • .db-shm
  • .db-wal
  • .db3
  • .dbc
  • .dbf
  • .dbs
  • .dbt
  • .dbv
  • .dbx
  • .dcb
  • .dct
  • .dcx
  • .ddl
  • .dlis
  • .dp1
  • .dqy
  • .dsk
  • .dsn
  • .dtsx
  • .dxl
  • .eco
  • .ecx
  • .edb
  • .epim
  • .exb
  • .fcd
  • .fdb
  • .fic
  • .fmp
  • .fmp12
  • .fmpsl
  • .fol
  • .fp3
  • .fp4
  • .fp5
  • .fp7
  • .fpt
  • .frm
  • .gdb
  • .grdb
  • .gwi
  • .hdb
  • .his
  • .ib
  • .idb
  • .ihx
  • .itdb
  • .itw
  • .jet
  • .jtx
  • .kdb
  • .kexi
  • .kexic
  • .kexis
  • .lgc
  • .lwx
  • .maf
  • .maq
  • .mar
  • .mas
  • .mav
  • .mdb
  • .mdf
  • .mpd
  • .mrg
  • .mud
  • .mwb
  • .myd
  • .ndf
  • .nnt
  • .nrmlib
  • .ns2
  • .ns3
  • .ns4
  • .nsf
  • .nv
  • .nv2
  • .nwdb
  • .nyf
  • .odb
  • .oqy
  • .orx
  • .owc
  • .p96
  • .p97
  • .pan
  • .pdb
  • .pdm
  • .pnz
  • .qry
  • .qvd
  • .rbf
  • .rctd
  • .rod
  • .rodx
  • .rpd
  • .rsd
  • .sas7bdat
  • .sbf
  • .scx
  • .sdb
  • .sdc
  • .sdf
  • .sis
  • .spq
  • .sql
  • .sqlite
  • .sqlite3
  • .sqlitedb
  • .te
  • .temx
  • .tmd
  • .tps
  • .trc
  • .trm
  • .udb
  • .udl
  • .usr
  • .v12
  • .vis
  • .vpd
  • .vvv
  • .wdb
  • .wmdb
  • .wrk
  • .xdb
  • .xld
  • .xmlff
  • .abcddb
  • .abs
  • .abx
  • .accdw
  • .adn
  • .db2
  • .fm5
  • .hjt
  • .icg
  • .icr
  • .kdb
  • .lut
  • .maw
  • .mdn
  • .mdt
  • .vdi
  • .vhd
  • .vmdk
  • .pvm
  • .vmem
  • .vmsn
  • .vmsd
  • .nvram
  • .vmx
  • .raw
  • .qcow2
  • .subvol
  • .bin
  • .vsv
  • .avhd
  • .vmrs
  • .vhdx
  • .avdx
  • .vmcx
  • .iso