1/16 April 11, 2022 Conti ransomware source code investigation - part 2. cocomelonc.github.io/investigation/2022/04/11/malw-inv-conti-2.html 2 minute read ﷽ Hello, cybersecurity enthusiasts and white hackers! This post is the second part of Conti ransomware source code self-investigation. first part In the last part, I wrote about encryption/hashing methods and bypassing AV-engines. Today I will consider network connections and filesystem and some identified IoCs. network connections First of all, let’s go back a little to the logic of the encryptor: https://cocomelonc.github.io/investigation/2022/04/11/malw-inv-conti-2.html https://cocomelonc.github.io/investigation/2022/03/27/malw-inv-conti-1.html 2/16 As you can see when the encryption mode is ALL_ENCRYPT or NETWORK_ENCRYPT , the malware retrieves info about network. Let’s go to definition of StartScan : Let’s go to deep into logic of network_connections. 3/16 GetCurrentIpAddress is just get info about current IP address: Function GetSubnets uses GetIpNetTable API which is called to restore the ARP table of the infected system. For earch entry the specified IPv4 addresses are checked against the following masks: If the current ARP matches of this masks ( 172.*, 192.168.*, 10.*, 169.* ) the subnet is extracted and added to the subnet’s queue: 4/16 (pl szIpAddress p2 szIpAddress p3 szIpAddress p4 szIpAddress) BOOL Found FALSE; PSUBNET INFO SubnetInfo NULL; TAILQ FOREACH(SubnetInfo, SubnetList, Entries) { (!memcmp(SSubnetInfo-=dwAddress, GdwAddress, 3)) { Found TRUE; (!Found) { BYTE bAddres[4]; (ULONG*)bAddres dwAddress; bAddres[3] 0; PSUBNET INFO NewSubnet (PSUBNET INFO)m malloc( (SUBNET INFO)); (!NewSubnet) { ' 5 DWBUOUwWoOOWWWUwOee } Rt LCopyMemory (SNewSubnet->dwAddress, bAddres, 4); TAILQ INSERT TAIL(SubnetList, NewSubnet, Entries); = i Re Bic Aj « network_scanner.cpp x queue.h EMPTY (head) ((head)->tqh_first NULL) ta) ta) FIRST(head) ((head) ->tgh_first) ta) ta) FOREACH(var, head, field) TAILQ FIRST( (head) ); \ TAILQ NEXT((var), field)) ta) ta) ay auf WwW N ta) ta) FOREACH REVERSE(var, head, headname, field) TAILQ LAST((head), headname) ; \ \ TAILQ PREV((var), headname, field) ) tay la La) La) S&S fh fk hw WwW WwW Ww Ww Ww Ww Ww [te] BW NN Fe © la) 5/16 Function ScanHosts tries a connection to IPv4 on the SMB port (445) using the TCP protocol: If connection is successfull, saves the valid IP’s via AddHost : 6/16 in a queue: And what about HostHandler : 7/16 and PortScanHandler : 8/16 HostHandler waits for some valid IP in the IP’s queue and for each IP enum the shares using the NetShareEnum API: 9/16 And PortScanHandler (1) repeat the scan via ScanHosts (2) each 30 sec. (3): So, what happens when calls network_scanner::StartScan ? 1. Add 172.*, 192.168.*, 10.*, 169.* subnet addresses to queue. 2. Create two threads. 3. First thread via HostHandler enum the shares. 4. Second thread via PortScanHandler tries to connect SMB 445 port, for earh successfully connection, saves valid IPs and scan every 30 sec: 10/16 Concluding the execution, the WaitForSingleObject API is invoked on each thread to wait for the completion of operations before closing the main process and CloseHandle for cleanup: 11/16 process killer The logic of the prockiller.cpp is simple. It enum through all processes and if it’s not equal to explorer.exe then adds it’s PID to the queue: filesystem In the filesystem module there is a function filesystem::EnumirateDrives which, as the name implies, scan drives: 12/16 As you can see it uses GetLogicalDriveStringsW API. The logic of this function is used in the final enumeration during encryption. The malware uses a whitelist for both directories and files to avoid the encryption of unnecessary data. The following directories names and file names are avoided during the enumeration process: 13/16 File Edit View Selection Find Packages Help STATIC B CheckDirectory( in LPCWSTR Directory) { LPCWSTR BlackList[] { OBFW(L"tmp"), OBFW(L"winnt"), OBFW(L"temp"), OBFW(L"thumb"), OBFW(L"$Recycle.Bin"), OBFW(L"$RECYCLE.BIN"), OBFW(L"System Volume Information"), OBFW(L"Boot"), OBFW(L"Windows"), OBFW(L"Trend Micro"), OBFW(L"perflogs") ti INT Count (BlackList) (LPWSTR); (INT i 0; 2 < Count; i++) { (pStrStrIW(Directory, BlackList[i])) { TRUE; 13/16 14/16 yara rules Let’s go to upload locker.exe to VirusTotal: 15/16 https://www.virustotal.com/gui/file/e1b147aa2efa6849743f570a3aca8390faf4b90aed490a568 2816dd9ef10e473/detection 57 of 69 AV engines detect this sample as malware Yara rule for Conti: rule Conti { meta: author = "kevoreilly" description = "Conti Ransomware" cape_type = "Conti Payload" strings: $crypto1 = {8A 07 8D 7F 01 0F B6 C0 B9 ?? 00 00 00 2B C8 6B C1 ?? 99 F7 FE 8D [2] 99 F7 FE 88 ?? FF 83 EB 01 75 DD} $website1 = "https://contirecovery.info" ascii wide $website2 = "https://contirecovery.best" ascii wide condition: uint16(0) == 0x5A4D and any of them } I hope this post spreads awareness to the blue teamers of this interesting malware techniques, and adds a weapon to the red teamers arsenal. first part WSAStartup WSAAdressToStringA CreateToolhelp32Snapshot CloseHandle https://www.virustotal.com/gui/file/e1b147aa2efa6849743f570a3aca8390faf4b90aed490a5682816dd9ef10e473/detection https://cocomelonc.github.io/investigation/2022/03/27/malw-inv-conti-1.html https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-wsastartup https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaaddresstostringa https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle 16/16 StrStrIW CreateThread WaitForSingleObject NetShareEnum GetLogicalDriveStringsW This is a practical case for educational purposes only. Thanks for your time happy hacking and good bye! PS. All drawings and screenshots are mine https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-strstriw https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject https://docs.microsoft.com/en-us/windows/win32/api/lmshare/nf-lmshare-netshareenum https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlogicaldrivestringsw