πŸ”’
There are new articles available, click to refresh the page.
Before yesterdayAvast Threat Labs

Avast releases decryptor for AtomSilo and LockFile ransomware

27 October 2021 at 15:59

On Oct 17, 2021, JiΕ™Γ­ Vinopal published information about a weakness in the AtomSilo ransomware and that it is possible to decrypt files without paying the ransom. Slightly later, he also analyzed another ransomware strain, LockFile. We prepared our very own free Avast decryptor for both the AtomSilo and LockFile strains.

Limitation of the decryptor

During the decryption process, the Avast AtomSilo decryptor relies on a known file format in order to verify that the file was successfully decrypted. For that reason, some files may not be decrypted. This canΒ  include files with proprietary or unknown format, or with no format at all, such as text files.

How AtomSilo and LockFile Work

Both the AtomSilo and LockFile ransomware strains are very similar to each other and except for minor differences, this description covers both of them.Β 

AtomSilo ransomware searches local drives using a fixed drive list, whilst LockFile calls GetLogicalDriveStringsA() and processes all drives that are fixed drives.

A separate thread is created for each drive in the list. This thread recursively searches the given logical drive and encrypts files found on it. To prevent paralyzing the compromised PC entirely, AtomSilo has a list of folders, file names and file types that are left unencrypted which are listed here:

Excluded folders
Boot Windows Windows.old Tor Browser
Internet Explorer Google Opera Opera Software
Mozilla Mozilla Firefox $Recycle.Bin ProgramData
All Users
Excluded files
autorun.inf index.htmlΒ  boot.ini bootfont.bin
bootsect.bak bootmgr bootmgr.efi bootmgfw.efi
desktop.ini iconcache.db ntldr ntuser.dat
ntuser.dat.log ntuser.ini thumbs.db #recycle
Excluded extensions
.hta .html .exe .dll .cpl .ini
.cab .cur .cpl .drv .hlp .icl
.icns .ico .idx .sys .spl .ocx

LockFile avoids files and folders, containing those sub-strings:

Excluded sub-strings
Windows NTUSER LOCKFILE .lockfile

In addition to that, there is a list of 788 file types (extensions), which won’t be encrypted. Those include .exe, but also .jpg, .bmp and .gif. You may noticed that some of them are included repeatedly.

The ransomware generates RSA-4096 session keypair for each victim. Its private part is then stored in the ransom note file, encrypted by the master RSA key (hardcoded in the binary). A new AES-256 file key is generated for each file. This key is then encrypted by the session RSA key and stored at the end of the encrypted file, together with original file size.Β 

Each encrypted file contains a ransom note file with one of the names:

  • README-FILE-%ComputerName%-%TimeStamp%.hta
  • LOCKFILE-FILE-%ComputerName%-%TimeStamp%.hta

Encrypted files can be recognized by the .ATOMSILO or .lockfile extension:Β 

When the encryption process is complete, the ransom note is shown to the user. Each strain’s ransom note has its own look:

AtomSilo Ransom Message
LockFile Ransom Message

How to use the Decryptor

To decrypt your files, please, follow these steps:

  1. Download the free decryptor. The single EXE file covers both ransomware strains.
  2. Simply run the EXE. It starts in form of wizard, which leads you through configuration of the decryption process.
  3. On the initial page, you can see a list of credits. Simply click β€œNext”
  1. On the next page, select the list of locations which you want to be decrypted. By default, it contains a list of all local drives.
  1. On the third page, you can select whether you want to backup encrypted files. These backups may help if anything goes wrong during the decryption process. This option is turned on by default, which we recommend. After clicking β€œDecrypt”, the decryption process begins.
  1. Let the decryptor work and wait until it finishes.

Credits

We would like to thank JiΕ™Γ­ Vinopal for sharing analysis of both ransomware strains.

IOCs

SHA filename
d9f7bb98ad01c4775ec71ec66f5546de131735e6dba8122474cc6eb62320e47b .ATOMSILO
bf315c9c064b887ee3276e1342d43637d8c0e067260946db45942f39b970d7ce .lockfile

The post Avast releases decryptor for AtomSilo and LockFile ransomware appeared first on Avast Threat Labs.

Decoding Cobalt Strike: Understanding Payloads

Intro

Cobalt Strike threat emulation software is the de facto standard closed-source/paid tool used by infosec teams in many governments, organizations and companies. It is also very popular in many cybercrime groups which usually abuse cracked or leaked versions of Cobalt Strike.Β 

Cobalt Strike has multiple unique features, secure communication and it is fully modular and customizable so proper detection and attribution can be problematic. It is the main reason why we have seen use of Cobalt Strike in almost every major cyber security incident or big breach for the past several years.

There are many great articles about reverse engineering Cobalt Strike software, especially beacon modules as the most important part of the whole chain. Other modules and payloads are very often overlooked, but these parts also contain valuable information for malware researchers and forensic analysts or investigators.

The first part of this series is dedicated to proper identification of all raw payload types and how to decode and parse them. We also share our useful parsers, scripts and yara rules based on these findings back to the community.Β 

Raw payloads

Cobalt Strike’s payloads are based on Meterpreter shellcodes and include many similarities like API hashing (x86 and x64 versions) or url query checksum8 algo used in http/https payloads, which makes identification harder. This particular checksum8 algorithm is also used in other frameworks like Empire.

Let’s describe interesting parts of each payload separately.

Payload header x86 variant

Default 32bit raw payload’s entry points start with typical instruction CLD (0xFC) followed by CALL instruction and PUSHA (0x60) as the first instruction from API hash algorithm.

x86 payload
Payload header x64 variant

Standard 64bit variants start also with CLD instruction followed by AND RSP,-10h and CALL instruction.

x64 payload

We can use these patterns for locating payloads’ entry points and count other fixed offsets from this position.

Default API hashes

Raw payloads have a predefined structure and binary format with particular placeholders for each customizable value such as DNS queries, HTTP headers or C2 IP address. Placeholder offsets are on fixed positions the same as hard coded API hash values. The hash algorithm is ROR13 and the final hash is calculated from the API function name and DLL name. The whole algorithm is nicely commented inside assembly code on the Metasploit repository.

Python implementation of API hashing algorithm

We can use the following regex patterns for searching hardcoded API hashes:

We can use a known API hashes list for proper payload type identification and known fixed positions of API hashes for more accurate detection via Yara rules.

Payload identification via known API hashes

Complete Cobalt Strike API hash list:

API hash DLL and API name
0xc99cc96a dnsapi.dll_DnsQuery_A
0x528796c6 kernel32.dll_CloseHandle
0xe27d6f28 kernel32.dll_ConnectNamedPipe
0xd4df7045 kernel32.dll_CreateNamedPipeA
0xfcddfac0 kernel32.dll_DisconnectNamedPipe
0x56a2b5f0 kernel32.dll_ExitProcess
0x5de2c5aa kernel32.dll_GetLastError
0x0726774c kernel32.dll_LoadLibraryA
0xcc8e00f4 kernel32.dll_lstrlenA
0xe035f044 kernel32.dll_Sleep
0xbb5f9ead kernel32.dll_ReadFile
0xe553a458 kernel32.dll_VirtualAlloc
0x315e2145 user32.dll_GetDesktopWindow
0x3b2e55eb wininet.dll_HttpOpenRequestA
0x7b18062d wininet.dll_HttpSendRequestA
0xc69f8957 wininet.dll_InternetConnectA
0x0be057b7 wininet.dll_InternetErrorDlg
0xa779563a wininet.dll_InternetOpenA
0xe2899612 wininet.dll_InternetReadFile
0x869e4675 wininet.dll_InternetSetOptionA
0xe13bec74 ws2_32.dll_accept
0x6737dbc2 ws2_32.dll_bind
0x614d6e75 ws2_32.dll_closesocket
0x6174a599 ws2_32.dll_connect
0xff38e9b7 ws2_32.dll_listen
0x5fc8d902 ws2_32.dll_recv
0xe0df0fea ws2_32.dll_WSASocketA
0x006b8029 ws2_32.dll_WSAStartup

Complete API hash list for Windows 10 system DLLs is available here.

Customer ID / Watermark

Based on information provided on official web pages, Customer ID is a 4-byte number associated with the Cobalt Strike licence key and since v3.9 is embedded into the payloads and beacon configs. This number is located at the end of the payload if it is present. Customer ID could be used for specific threat authors identification or attribution, but a lot of Customer IDs are from cracked or leaked versions, so please consider this while looking at these for possible attribution.

DNS stager x86

Typical payload size is 515 bytes or 519 bytes with included Customer ID value. The DNS query name string starts on offset 0x0140 (calculated from payload entry point) and the null byte and max string size is 63 bytes. If the DNS query name string is shorter, then is terminated with a null byte and the rest of the string space is filled with junk bytes.

DnsQuery_A API function is called with two default parameters:

Parameter Value Constant
DNS Record Type (wType) 0x0010 DNS_TYPE_TEXT
DNS Query Options (Options) 0x0248 DNS_QUERY_BYPASS_CACHE
DNS_QUERY_NO_HOSTS_FILE
DNS_QUERY_RETURN_MESSAGE

Anything other than the default values are suspicious and could indicate custom payload.

Python parsing:

Default DNS payload API hashes:

Offset Hash value API name
0x00a3 0xe553a458 kernel32.dll_VirtualAlloc
0x00bd 0x0726774c kernel32.dll_LoadLibraryA
0x012f 0xc99cc96a dnsapi.dll_DnsQuery_A
0x0198 0x56a2b5f0 kernel32.dll_ExitProcess
0x01a4 0xe035f044 kernel32.dll_Sleep
0x01e4 0xcc8e00f4 kernel32.dll_lstrlenA

Yara rule for DNS stagers:

SMB stager x86

The default payload size is 346 bytes plus the length of the pipe name string terminated by a null byte and the length of the Customer ID if present. The pipe name string is located right after the payload code on offset 0x015A in plaintext format.

CreateNamedPipeA API function is called with 3 default parameters:

Parameter Value Constant
Open Mode (dwOpenMode) 0x0003 PIPE_ACCESS_DUPLEX
Pipe Mode (dwPipeMode) 0x0006 PIPE_TYPE_MESSAGE, PIPE_READMODE_MESSAGE
Max Instances (nMaxInstances) 0x0001

Python parsing:

Default SMB payload API hashes:

Offset Hash value API name
0x00a1 0xe553a458 kernel32.dll_VirtualAlloc
0x00c4 0xd4df7045 kernel32.dll_CreateNamedPipeA
0x00d2 0xe27d6f28 kernel32.dll_ConnectNamedPipe
0x00f8 0xbb5f9ead kernel32.dll_ReadFile
0x010d 0xbb5f9ead kernel32.dll_ReadFile
0x0131 0xfcddfac0 kernel32.dll_DisconnectNamedPipe
0x0139 0x528796c6 kernel32.dll_CloseHandle
0x014b 0x56a2b5f0 kernel32.dll_ExitProcess

Yara rule for SMB stagers:

TCP Bind stager x86

The payload size is 332 bytes plus the length of the Customer ID if present. Parameters for the bind API function are stored inside the SOCKADDR_IN structure hardcoded as two dword pushes. The first PUSH with the sin_addr value is located on offset 0x00C4. The second PUSH contains sin_port and sin_family values and is located on offset 0x00C9 The default sin_family value is AF_INET (0x02).

Python parsing:

Default TCP Bind x86 payload API hashes:

Offset Hash value API name
0x009c 0x0726774c kernel32.dll_LoadLibraryA
0x00ac 0x006b8029 ws2_32.dll_WSAStartup
0x00bb 0xe0df0fea ws2_32.dll_WSASocketA
0x00d5 0x6737dbc2 ws2_32.dll_bind
0x00de 0xff38e9b7 ws2_32.dll_listen
0x00e8 0xe13bec74 ws2_32.dll_accept
0x00f1 0x614d6e75 ws2_32.dll_closesocket
0x00fa 0x56a2b5f0 kernel32.dll_ExitProcess
0x0107 0x5fc8d902 ws2_32.dll_recv
0x011a 0xe553a458 kernel32.dll_VirtualAlloc
0x0128 0x5fc8d902 ws2_32.dll_recv
0x013d 0x614d6e75 ws2_32.dll_closesocket

Yara rule for TCP Bind x86 stagers:

TCP Bind stager x64

The payload size is 510 bytes plus the length of the Customer ID if present. The SOCKADDR_IN structure is hard coded inside the MOV instruction as a qword and contains the whole structure. The offset for the MOV instruction is 0x00EC.

Python parsing:

Default TCP Bind x64 payload API hashes:

Offset Hash value API name
0x0100 0x0726774c kernel32.dll_LoadLibraryA
0x0111 0x006b8029 ws2_32.dll_WSAStartup
0x012d 0xe0df0fea ws2_32.dll_WSASocketA
0x0142 0x6737dbc2 ws2_32.dll_bind
0x0150 0xff38e9b7 ws2_32.dll_listen
0x0161 0xe13bec74 ws2_32.dll_accept
0x016f 0x614d6e75 ws2_32.dll_closesocket
0x0198 0x5fc8d902 ws2_32.dll_recv
0x01b8 0xe553a458 kernel32.dll_VirtualAlloc
0x01d2 0x5fc8d902 ws2_32.dll_recv
0x01ee 0x614d6e75 ws2_32.dll_closesocket

Yara rule for TCP Bind x64 stagers:

TCP Reverse stager x86

The payload size is 290 bytes plus the length of the Customer ID if present. This payload is very similar to TCP Bind x86 and SOCKADDR_IN structure is hardcoded on the same offset with the same double push instructions so we can reuse python parsing code from TCP Bind x86 payload.

Default TCP Reverse x86 payload API hashes:

Offset Hash value API name
0x009c 0x0726774c kernel32.dll_LoadLibraryA
0x00ac 0x006b8029 ws2_32.dll_WSAStartup
0x00bb 0xe0df0fea ws2_32.dll_WSASocketA
0x00d5 0x6174a599 ws2_32.dll_connect
0x00e5 0x56a2b5f0 kernel32.dll_ExitProcess
0x00f2 0x5fc8d902 ws2_32.dll_recv
0x0105 0xe553a458 kernel32.dll_VirtualAlloc
0x0113 0x5fc8d902 ws2_32.dll_recv

Yara rule for TCP Reverse x86 stagers:

TCP Reverse stager x64

Default payload size is 465 bytes plus length of Customer ID if present. Payload has the same position as the SOCKADDR_IN structure such as TCP Bind x64 payload so we can reuse parsing code again.

Default TCP Reverse x64 payload API hashes:

Offset Hash value API name
0x0100 0x0726774c kernel32.dll_LoadLibraryA
0x0111 0x006b8029 ws2_32.dll_WSAStartup
0x012d 0xe0df0fea ws2_32.dll_WSASocketA
0x0142 0x6174a599 ws2_32.dll_connect
0x016b 0x5fc8d902 ws2_32.dll_recv
0x018b 0xe553a458 kernel32.dll_VirtualAlloc
0x01a5 0x5fc8d902 ws2_32.dll_recv
0x01c1 0x614d6e75 ws2_32.dll_closesocket

Yara rule for TCP Reverse x64 stagers:

HTTP stagers x86 and x64

Default x86 payload size fits 780 bytes and the x64 version is 874 bytes long plus size of request address string and size of Customer ID if present. The payloads include full request information stored inside multiple placeholders.

Request address

The request address is a plaintext string terminated by null byte located right after the last payload instruction without any padding. The offset for the x86 version is 0x030C and 0x036A for the x64 payload version. Typical format is IPv4.

Request port

For the x86 version the request port value is hardcoded inside a PUSH instruction as aΒ  dword. The offset for the PUSH instruction is 0x00BE. The port value for the x64 version is stored inside MOV r8d, dword instruction on offset 0x010D.

Request query

The placeholder for the request query has a max size of 80 bytes and the value is a plaintext string terminated by a null byte. If the request query string is shorter, then the rest of the string space is filled with junk bytes. The placeholder offset for the x86 version is 0x0143 and 0x0186 for the x64 version.

Cobalt Strike and other tools such as Metasploit use a trivial checksum8 algorithm for the request query to distinguish between x86 and x64 payload or beacon.Β 

According to leaked Java web server source code,Β  Cobalt Strike uses only two checksum values, 0x5C (92) for x86 payloads and 0x5D for x64 versions. There are also implementations of Strict stager variants where the request query string must be 5 characters long (including slash). The request query checksum feature isn’t mandatory.

Python implementation of checksum8 algorithm:

Metasploit server uses similar values:

You can find a complete list of Cobalt Strike’s x86 and x64 strict request queries here.

Request header

The size of the request header placeholder is 304 bytes and the value is also represented as a plaintext string terminated by a null byte. The request header placeholder is located immediately after the Request query placeholder. The offset for the x86 version is 0x0193 and 0x01D6 for the x64 version.

The typical request header value for HTTP/HTTPS stagers is User-Agent. The Cobalt Strike web server has banned user-agents which start with lynx, curl or wget and return a response code 404 if any of these strings are found.

API function HttpOpenRequestA is called with following dwFlags (0x84600200):

Python parsing:

Default HTTP x86 payload API hashes:

Offset Hash value API name
0x009c 0x0726774c kernel32.dll_LoadLibraryA
0x00aa 0xa779563a wininet.dll_InternetOpenA
0x00c6 0xc69f8957 wininet.dll_InternetConnectA
0x00de 0x3b2e55eb wininet.dll_HttpOpenRequestA
0x00f2 0x7b18062d wininet.dll_HttpSendRequestA
0x010b 0x5de2c5aa kernel32.dll_GetLastError
0x0114 0x315e2145 user32.dll_GetDesktopWindow
0x0123 0x0be057b7 wininet.dll_InternetErrorDlg
0x02c4 0x56a2b5f0 kernel32.dll_ExitProcess
0x02d8 0xe553a458 kernel32.dll_VirtualAlloc
0x02f3 0xe2899612 wininet.dll_InternetReadFile

Default HTTP x64 payload API hashes:

Offset Hash value API name
0x00e9 0x0726774c kernel32.dll_LoadLibraryA
0x0101 0xa779563a wininet.dll_InternetOpenA
0x0120 0xc69f8957 wininet.dll_InternetConnectA
0x013f 0x3b2e55eb wininet.dll_HttpOpenRequestA
0x0163 0x7b18062d wininet.dll_HttpSendRequestA
0x0308 0x56a2b5f0 kernel32.dll_ExitProcess
0x0324 0xe553a458 kernel32.dll_VirtualAlloc
0x0342 0xe2899612 wininet.dll_InternetReadFile

Yara rules for HTTP x86 and x64 stagers:

HTTPS stagers x86 and x64

The payload structure and placeholders are almost the same as the HTTP stagers. The differences are only in payload sizes, placeholder offsets, usage of InternetSetOptionA API function (API hash 0x869e4675) and different dwFlags for calling the HttpOpenRequestA API function.

The default x86 payload size fits 817 bytes and the default for the x64 version is 909 bytes long plus size of request address string and size of the Customer ID if present.

Request address

The placeholder offset for the x86 version is 0x0331 and 0x038D for the x64 payload version. The typical format is IPv4.

Request port

The hardcoded request port format is the same as HTTP.Β  The PUSH offset for the x86 version is 0x00C3. The MOV instruction for x64 version is on offset 0x0110.

Request query

The placeholder for the request query has the same format and length as the HTTP version. The placeholder offset for the x86 version is 0x0168 and 0x01A9 for the x64 version.

Request header

The size and length of the request header placeholder is the same as the HTTP version. Offset for the x86 version is 0x01B8 and 0x01F9 for the x64 version.

API function HttpOpenRequestA is called with following dwFlags (0x84A03200):

InternetSetOptionA API function is called with following parameters:

Python parsing:

Default HTTPS x86 payload API hashes:

Offset Hash value API name
0x009c 0x0726774c kernel32.dll_LoadLibraryA
0x00af 0xa779563a wininet.dll_InternetOpenA
0x00cb 0xc69f8957 wininet.dll_InternetConnectA
0x00e7 0x3b2e55eb wininet.dll_HttpOpenRequestA
0x0100 0x869e4675 wininet.dll_InternetSetOptionA
0x0110 0x7b18062d wininet.dll_HttpSendRequestA
0x0129 0x5de2c5aa kernel32.dll_GetLastError
0x0132 0x315e2145 user32.dll_GetDesktopWindow
0x0141 0x0be057b7 wininet.dll_InternetErrorDlg
0x02e9 0x56a2b5f0 kernel32.dll_ExitProcess
0x02fd 0xe553a458 kernel32.dll_VirtualAlloc
0x0318 0xe2899612 wininet.dll_InternetReadFile

Default HTTPS x64 payload API hashes:

Offset Hash value API name
0x00e9 0x0726774c kernel32.dll_LoadLibraryA
0x0101 0xa779563a wininet.dll_InternetOpenA
0x0123 0xc69f8957 wininet.dll_InternetConnectA
0x0142 0x3b2e55eb wininet.dll_HttpOpenRequestA
0x016c 0x869e4675 wininet.dll_InternetSetOptionA
0x0186 0x7b18062d wininet.dll_HttpSendRequestA
0x032b 0x56a2b5f0 kernel32.dll_ExitProcess
0x0347 0xe553a458 kernel32.dll_VirtualAlloc
0x0365 0xe2899612 wininet.dll_InternetReadFile

Yara rule for HTTPS x86 and x64 stagers:

The next stage or beacon could be easily downloaded via curl or wget tool:

You can find our parser for Raw Payloads and all according yara rules in our IoC repository.

Raw Payloads encoding

Cobalt Strike also includes a payload generator for exporting raw stagers and payload in multiple encoded formats. Encoded formats support UTF-8 and UTF-16le.Β 

Table of the most common encoding with usage and examples:

Encoding Usage Example
Hex VBS, HTA 4d5a9000..
Hex Array PS1 0x4d, 0x5a, 0x90, 0x00..
Hex Veil PY \x4d\x5a\x90\x00..
Decimal Array VBA -4,-24,-119,0..
Char Array VBS, HTA Chr(-4)&”H”&Chr(-125)..
Base64 PS1 38uqIyMjQ6..
gzip / deflate compression PS1
Xor PS1, Raw payloads, Beacons

Decoding most of the formats are pretty straightforward, but there are few things to consider.Β 

  • Values inside Decimal and Char Array are splitted via β€œnew lines” represented by β€œ\s_\n” (\x20\x5F\x0A).Β 
  • Common compression algorithms used inside PowerShell scripts are GzipStream and raw DeflateStream.

Python decompress implementation:

XOR encoding

The XOR algorithm is used in three different cases. The first case is one byte XOR inside PS1 scripts, default value is 35 (0x23).

The second usage is XOR with dword key for encoding raw payloads or beacons inside PE stagers binaries. Specific header for xored data is 16 bytes long and includes start offset, xored data size, XOR key and four 0x61 junk/padding bytes.

Python header parsing:

We can create Yara rule based on XOR key from header and first dword of encoded data to verify supposed values there:

The third case is XOR encoding with a rolling dword key, used only for decoding downloaded beacons. The encoded data blob is located right after the XOR algorithm code without any padding. The encoded data starts with an initial XOR key (dword) and the data size (dword xored with init key).

There are x86 and x64 implementations of the XOR algorithm. Cobalt Strike resource includes xor.bin and xor64.bin files with precompiled XOR algorithm code.Β 

Default lengths of compiled x86 code are 52 and 56 bytes (depending on used registers) plus the length of the junk bytes. The x86 implementation allows using different register sets, so the xor.bin file includes more than 800 different precompiled code variants.

Yara rule for covering all x86 variants with XOR verification:

The precompiled x64 code is 63 bytes long with no junk bytes. There is also only one precompiled code variant.

Yara rule for x64 variant with XOR verification:

You can find our Raw Payload decoder and extractor for the most common encodings here. It uses a parser from the previous chapter and it could save your time and manual work. We also provide an IDAPython script for easy raw payload analysis.

Conclusion

As we see more and more abuse of Cobalt Strike by threat actors, understanding how to decode its use is important for malware analysis.

In this blog, we’ve focused on understanding how threat actors use Cobalt Strike payloads and how you can analyze them.

The next part of this series will be dedicated to Cobalt Strike beacons and parsing its configuration structure.

The post Decoding Cobalt Strike: Understanding Payloads appeared first on Avast Threat Labs.

  • There are no more articles
❌