PrideLocker - a new fork of Babuk ESX encryptor

Written by Théo Letailleur - 05/12/2022 - in CSIRT - Download
A few months after the leak of Babuk source code in September 2021, new ransomware families with very similar capabilities already seem to emerge. During an incident response, Synacktiv's CSIRT detected a new ESX encryptor dubbed PrideLocker that is based on Babuk ESX encryptor, with new additions. This article provides an in-depth analysis of PrideLocker, and a method using IDAPython to decrypt its strings, as well as tips to detect its encryption capabilities.

Introduction

During an incident response that occurred a few months ago, involving a ransomware attack, we encountered several pieces of malware.

  • Dagon Locker1, a ransomware targeting Windows assets. It seems to be the new variant of MountLocker. Its analysis reveals that the Dagon Locker sample involved in this case is quite similar to the analysis made by SecurityScorecard on Quantum Locker (the version based on MountLocker that precedes Dagon).2
  • Several CobaltStrike beacons and custom tools,
  • And an ELF ransomware targeting ESXi servers named PrideLocker, which we focus on in this article.

After analysis, we concluded that PrideLocker is most probably a derivative of the Babuk ESX ransomware whose source code leaked in September 202134. In May 2022, TrendMicro described another ESX ransomware named CheersCrypt that is even more similar to Babuk ESX leaked source code5.

Therefore, the purpose of this article is to describe the new additions brought by PrideLocker. Another purpose is also to help detect PrideLocker and variants based on Babuk source code in order to improve the qualification of such ransomware during incident response.

Basic information

PrideLocker basic information
File name encryptor.esx.bin
MD5 2dbf12d19306611b8deeb708bc61c18c
SHA1 a0e29552db15adc29ac60dc7e5fe757b42e07a76
SHA256 a312fef3873b96ce5829cc00b724291ece7a0fd238a007c52695dc4a3268e3c1
File type ELF 64-bit LSB executable x86_64 stripped
File size 165928
Threat ESX ransomware

 

Ransom note

What happened?
 All your files are encrypted on all devices across the network
 Huge volume of your data including financial, customer, partner and employees data was downloaded to our internal servers

What's next?
 If you don't get in touch with us next 48 hours, we'll start publishing your data

How do I recover?
 There is no way to decrypt your files manually unless we provide a special decryption tool
 Please download TOR browser (https://www.torproject.org/) and CONTACT US (http://<redacted>.onion/?cid=<redacted>) for further instructions

Ransom note

 

Quantum Secure Chat Support
Figure 1 - Quantum Secure Chat Support

Quantum Locker is the version preceding Dagon Locker, so it is not surprising to see it appearing on the *secure* chat linked to this PrideLocker sample ransom note, since Dagon Locker was deployed to encrypt Windows assets in this ransomware attack.

 

Code analysis

String Obfuscation

The PrideLocker sample we encountered obfuscated its strings. Let's cover in detail its obfuscation mechanism.

The picture below describes the beginning of the main function assembly code. The string loading pattern is always the same:

  1. Call a function that loads an encrypted stack string
  2. Call a function containing a unique key to decrypt the string
String loading pattern
Figure 2 - String loading pattern

Below is a function loading an encrypted stack string. The first byte is the length of the encrypted string.

Encrypted stack string loading
Figure 3 - Encrypted stack string loading

Next, the decryption function of such encrypted string loads a unique QWORD (8 bytes) as a key and calls another function that is the actual decryption routine.

Key loading and decryption
Figure 4 - Key loading and decryption

The decryption routine is quite simple. Each byte of the encrypted string is xor-ed with the shifted key.

Decryption routine
Figure 5 - Decryption routine

Below is the decryption routine translated in Python:

def decrypt_string(enc_str, qword_key):
    dec_str = bytearray(len(enc_str))
    for i in range(0, len(enc_str)):
        tmp = (qword_key >> (8 * (i&7)))&0xff
        dec_str[i] = enc_str[i] ^ tmp
    return dec_str

How to analyse such program with this kind of obfuscation? Each string has its own loading functions: one loading the encrypted stack string and one decrypting it. Therefore, there are hundreds of functions involved in loading and decrypting the strings. However, the pattern remains similar and the same decryption routine (sub_408D0C) is used.

I wanted to get all the strings decrypted in order to have a good understanding of this malware, without spending too much time debugging it. Therefore, I chose to develop an IDA Python script since I am more used to this framework. To help develop such a script, I recommend reading icecr4ck's IDA Python cheat sheet6 and of course the official documentation7.

The full script is available on Synacktiv's Github organization: https://github.com/synacktiv/pridelocker-analysis

The approach of this script is first to get all the cross-references (XrefsTo) from the unique decryption routine. That way, for each cross-reference, we can fetch the unique key and the length of the encrypted string. Then the instruction pointer is moved back until the Stack string loader function is found (check get_function_with_encrypted_string function in the script). Once this function is found, it is browsed until the stack string is loaded (get_encrypted_string function). Finally, the stack string is collected, decrypted, and added as a new IDA comment in both disassembly and decompiler views. Moreover, the obfuscation functions that have been met in the process are renamed. That way, I can focus on the relevant code of the ransomware, and leave the obfuscation part behind.

You can see below the beginning of the main function in both disassembler and decompiler views, after the execution of the script.

IDA view after the script execution
Figure 6 - IDA view after the script execution

As you can see in Figure 6, every string has been recovered correctly. Also, around 280 functions have been renamed in the process, so that's a lot of code I could skip. 😃

As a reminder, I made a list of a few IDA Python functions that are useful most of the time:

  • Moving around the assembly:
    • looping over a list of instruction based on the start and end addresses: idautils.Heads,
    • cross-references of an address: idautils.XrefsTo,
    • get the function that possesses a specific address: ida_funcs.get_func.
  • Decoding instructions:
    • current instruction pointed by an address: idautils.DecodeInstruction,
    • get the previous instruction: idautils.DecodePreviousInstruction,
    • get the text representation of the mnemonic (mov, call, etc.): idc.print_insn_mnem,
    • get the operand type (o_reg, o_displ, o_imm): idc.get_operand_type,
    • get the operand value: idc.get_operand_value,
    • get the text representation of the operand: idc.print_operand.
  • Getting data:
    • get bytes (e.g. a chunk of data to decrypt): idc.get_bytes,
    • get the string contents pointed by an address: idc.get_strlit_contents.
  • Setting comments:
    • In the disassembly : idc.set_cmt
    • In the decompiler, it's a bit more complicated, I recommend reading the function setCommentToDecompilation from my script, that I actually took from gdataadvancedanalytics' ida-python GitHub repository8.

Command line arguments

Babuk ESX Locker required to be executed with the target path to encrypt as a command line argument. It did not require other arguments.

On contrary, PrideLocker can be executed without any argument. Several optional arguments can also be specified from the command line. The following help note is printed when executed with the -h flag:

PrideLocker ESX Encryptor (HYDRA-220218) started

Usage: ./encryptor.esx.bin <arguments>

Arguments:
    -h, --help       Show this help message and exit
    -v, --verbose    (OPTIONAL) Verbose encryptor output
    -p, --path       (OPTIONAL) Path to encrypt (/vmfs/volumes by default)
    -d, --dryrun     (OPTIONAL) Run in dryrun mode (i.e. no real encryption)
    -n, --nostop     (OPTIONAL) Do not stop VMs
    -t, --threads    (OPTIONAL) Set number of threads

Help note

As you can see in the help note, the first line PrideLocker ESX Encryptor (HYDRA-220218) started indicates the name of the ransomware "PrideLocker ESX Encryptor" and probably some kind of build version "HYDRA-220218".

The default path to encrypt is /vmfs/volumes. It is actually the default path where virtual machines are stored in ESXi (the datastore browsable from vSphere client). Among the optional arguments, it is possible to specify the path to encrypt, to choose not to stop the virtual machines before encryption, to specify a number of threads, to enable the verbose mode, or even to run in dry-run mode. The dry-run mode does not stop the virtual machines and does not encrypt the files.

Time-limited license

At the very beginning of the execution, the current time is checked against one stored and encrypted in .data segment.

If the current timestamp is 14 days too late or too early to the one stored in the .data segment, the encryptor exits with the following error:

Err: LICEXPR => <c>1668595744 <l>[encrypted hardcoded timestamp] <d>[hardcoded timestamp]

<c>1668595744 being the current timestamp.

As you probably guessed, it is a very simple mechanism to prevent this specific sample from being executed outside the expected period of execution planned by the attackers.

This functionality is not present in the source code of Babuk ESX. So we think it is specific to PrideLocker.

Thread pool

The thread handling is the same as in Babuk ESX source code, except that the number of threads can be customised from the command line with option -t.

Virtual machines shutdown

PrideLocker creates a temporary file named /tmp/stopall.sh. It contains the following shell script:

for i in $(esxcli vm process list 2>/dev/null| grep 'World ID:' | grep -o '[0-9]*'); do esxcli vm process kill --type=force --world-id=$i; done;

This script is executed then removed.

It is common to see the use of esxcli command in ESXi encryptors in order to shutdown virtual machines before encrypting them9. However, this functionality was not present in Babuk ESX source code. By the way, both the script path /tmp/stopall.sh and the escxli command are in clear text in the data segment of the ransomware, so we can use that for the detection!

Encryption process

The ransom note is created in the specified path to encrypt (default is /vmfs/volumes). The filename is HOW TO RECOVER YOUR FILES.txt. Its content has been detailed in the Ransom note section.

Then PrideLocker recursively scans the path to encrypt. Only files with the following filename extensions are targeted:

.log
.vmdk
.vmem
.vswp
.vmsn
.vib
.vbm
.vbk

Contrary to Babuk ESX encryptor, PrideLocker adds three new filename extensions that targets Veeam Agent backup files10:

  • .vib: incremental backup files that store incremental changes of VM images,
  • .vbm: backup metadata files that store information about the backup job,
  • and .vbk: the full backup files that store copies of full VM images.

When a file matches one of these extensions, it is encrypted in a new thread.

Encryption algorithm

The encryption routine used by PrideLocker is the same as the one in Babuk ESX and CheersCrypt5 so I will not give a thorough description of the algorithm. It uses SOSEMANUK stream cipher to encrypt files. The ECDH (X25519) algorithm, combined with SHA256, are used to generate the SOSEMANUK key. In the picture below, the left side is the encryption routine as described in Babuk ESX source code, and the right side is the decompiled version of PrideLocker encryption routine.

PrideLocker vs Babuk encryption routine
Figure 7 - PrideLocker vs Babuk encryption routine

The encrypted files are renamed with the .encrypted extension.

The sample is stripped, so when I first analysed this piece of malware it was difficult to recognise the SOSEMANUK and ECDH algorithms. So I gave this piece of malware to my colleague Eloi Benoist-vanderbeken, who succeeded to determine the complete encryption routine. Thanks to that, the similarity with Babuk ESX became obvious. The detection section gives tips and rules to help detecting these encryption algorithms.

Stats

At the end of the execution, statistics about how it performed are printed, indicating some numbers. Below is an example of what is printed, if the verbose mode -v is enabled:

Stats:
        found files:             142
        encrypted files:         54
        skipped files:           72
        failed files:            06

Total encrypted volume: 123.0 GiB

This functionality was already present in Babuk ESX, the strings are just slightly different.

Detection

The following pattern helps detecting the ECDH (or Curve25519) algorithm:

These instructions clear the bits 0, 1, 2 of the first byte, clear the bit 7 of the last byte, and set the bit 6 of the last byte of the generated secret key. Its purpose is for security reasons of Curve25519 algorithm, as defined in David Bernstein's paper11.

Regarding the detection of SOSEMANUK, the constant 0x54655307 is used in order to update its finite state machine12. 0x54655307 is the hexadecimal representation of the first ten decimals of π. I did not find any use of this constant other than in SOSEMANUK. Moreover, there are two bytes arrays, which - I think - are related to polynomial multiplication used to update the shift register13. Anyway, they are great to detect SOSEMANUK encryption capability.

PrideLocker Yara rule

Below is a Yara rule whose purpose is to detect similar PrideLocker samples:

rule Linux_Ransomware_PrideLocker 
{
    meta:
        author = "Theo Letailleur, Synacktiv"
        source = "Synacktiv"
        status = "RELEASED"
        sharing = "TLP:WHITE"
        category = "MALWARE"
        malware = "PrideLocker"
        description = "Yara rule that detects PrideLocker ransomware"

    strings:
        // for i in $(esxcli vm process list 2>/dev/null| grep 'World ID:' | grep -o '[0-9]*'); do esxcli vm process kill --type=force --world-id=$i; done;
        $shut_down_esxi = {
            66 6F 72 20 69 20 69 6E 20 24 28 65 73 78 63 6C 69 20 76 6D 20 70 72 6F 63 65 73 
            73 20 6C 69 73 74 20 32 3E 2F 64 65 76 2F 6E 75 6C 6C 7C 20 67 72 65 70 20 27 57 
            6F 72 6C 64 20 49 44 3A 27 20 7C 20 67 72 65 70 20 2D 6F 20 27 5B 30 2D 39 5D 2A 
            27 29 3B 20 64 6F 20 65 73 78 63 6C 69 20 76 6D 20 70 72 6F 63 65 73 73 20 6B 69 
            6C 6C 20 2D 2D 74 79 70 65 3D 66 6F 72 63 65 20 2D 2D 77 6F 72 6C 64 2D 69 64 3D 
            24 69 3B 20 64 6F 6E 65 3B
        }
        // Load "/tmp/stopall.sh" string
        $shut_down_esxi_load_filename = {
            48 B? 2F 74 6D 70 2F 73 74 6F // mov     r64, 6F74732F706D742Fh
            48 89 ?? ??                   // mov     qword ptr [rbp+?], r64
            48 B? 70 61 6C 6C 2E 73 68 00 // mov     r64, 68732E6C6C6170h
            48 89 ?? ??                   // mov     [rbp+?], r64
        }
        $x25519_clear_set_bits = {
            0F B6 ?? [1-4]  // movzx   r32, [rbp+?]
            83 ?? F8        // and     r32, 0FFFFFFF8h
            88 ?? [1-4]     // mov     [rbp+?], r8
            0F B6 ?? [1-4]  // movzx   r32, [rbp+?]
            83 ?? 7F        // and     r32, 7Fh
            88 ?? [1-4]     // mov     [rbp+?], r8
            0F B6 ?? [1-4]  // movzx   r32, [rbp+?]
            83 ?? 40        // or      r32, 40h
            88 ?? [1-4]     // mov     [rbp+?], r8
            ??
        }
        // Multiplication by alpha: alpha * x = T32(x << 8) ^ mul_a[x >> 24]
        $sosemanuk_encrypt_mul_a = {
            00 00 00 00 E1 9F CF 13 6B 97 37 26 8A 08 F8 35 D6 87 6E 4C 37 18 A1 5F BD 10 59 
            6A 5C 8F 96 79 05 A7 DC 98 E4 38 13 8B 6E 30 EB BE 8F AF 24 AD D3 20 B2 D4 32 BF 
            7D C7 B8 B7 85 F2 59 28 4A E1 0A E7 11 99 EB 78 DE 8A 61 70 26 BF 80 EF E9 AC DC 
            60 7F D5 3D FF B0 C6 B7 F7 48 F3 56 68 87 E0 0F 40 CD 01 EE DF 02 12 64 D7 FA 27 
            85 48 35 34 D9 C7 A3 4D 38 58 6C 5E B2 50 94 6B 53 CF 5B 78
        }
        // DWORD array version
        $sosemanuk_encrypt_mul_a_4byte_array_le = {
            00 00 00 00 13 CF 9F E1 26 37 97 6B 35 F8 08 8A 4C 6E 87 D6 5F A1 18 37 6A 59 10 
            BD 79 96 8F 5C 98 DC A7 05 8B 13 38 E4 BE EB 30 6E AD 24 AF 8F D4 B2 20 D3 C7 7D 
            BF 32 F2 85 B7 B8 E1 4A 28 59 99 11 E7 0A 8A DE 78 EB BF 26 70 61 AC E9 EF 80 D5 
            7F 60 DC C6 B0 FF 3D F3 48 F7 B7 E0 87 68 56 01 CD 40 0F 12 02 DF EE 27 FA D7 64 
            34 35 48 85 4D A3 C7 D9 5E 6C 58 38 6B 94 50 B2 78 5B CF 53
        }
        // Multiplication by 1/alpha: 1/alpha * x = (x >> 8) ^ mul_ia[x & 0xFF]
        $sosemanuk_encrypt_mul_ia = {
            00 00 00 00 18 0F 40 CD 30 1E 80 33 28 11 C0 FE 60 3C A9 66 78 33 E9 AB 50 22 29 
            55 48 2D 69 98 C0 78 FB CC D8 77 BB 01 F0 66 7B FF E8 69 3B 32 A0 44 52 AA B8 4B 
            12 67 90 5A D2 99 88 55 92 54 29 F0 5F 31 31 FF 1F FC 19 EE DF 02 01 E1 9F CF 49 
            CC F6 57 51 C3 B6 9A 79 D2 76 64 61 DD 36 A9 E9 88 A4 FD F1 87 E4 30 D9 96 24 CE 
            C1 99 64 03 89 B4 0D 9B 91 BB 4D 56 B9 AA 8D A8 A1 A5 CD 65
        }
        // DWORD array version
        $sosemanuk_encrypt_kk_mul_ia_4byte_array_le = {
            00 00 00 00 CD 40 0F 18 33 80 1E 30 FE C0 11 28 66 A9 3C 60 AB E9 33 78 55 29 22 
            50 98 69 2D 48 CC FB 78 C0 01 BB 77 D8 FF 7B 66 F0 32 3B 69 E8 AA 52 44 A0 67 12 
            4B B8 99 D2 5A 90 54 92 55 88 31 5F F0 29 FC 1F FF 31 02 DF EE 19 CF 9F E1 01 57 
            F6 CC 49 9A B6 C3 51 64 76 D2 79 A9 36 DD 61 FD A4 88 E9 30 E4 87 F1 CE 24 96 D9 
            03 64 99 C1 9B 0D B4 89 56 4D BB 91 A8 8D AA B9 65 CD A5 A1
        }

    condition:
        uint32(0) == 0x464C457F and
        (
            $shut_down_esxi and $shut_down_esxi_load_filename
            and $x25519_clear_set_bits 
            and 2 of ($sosemanuk_encrypt_*)
        )
}

You can find the Yara rule on our Github repository: https://github.com/synacktiv/pridelocker-analysis

The conditions could be loosened up a bit (e.g. or between $shut_down rules and crypto rules) to detect more variants.

ECDH and SOSEMANUK CAPA rules

CAPA14 is a tool developed by Mandiant FLARE team that detects capabilities in executable files.

To detect ECDH algorithm, there is already a Mandiant's CAPA rule named encrypt data using Curve25519 written by Dimiter Andonov15 that helps detecting the encryption function.

To detect Sosemanuk algorithm, there is also a Mandiant's CAPA rule named encrypt data using Sosemanuk written by Andrew Williams16 that does a pretty good job detecting the encryption function. New CAPA rules to detect sosemanuk_schedule and sosemanuk_init function could be added.

Conclusion

PrideLocker ESX encryptor is a Babuk ESX encryptor fork that brings new features: string obfuscation, the shutdown of virtual machines using esxcli, optional command line arguments, and a time-limited "license". The ransom note shows that the affiliate using Dagon Locker and involved in our incident response, is also using PrideLocker to target ESXi servers, but we cannot say whether there is a link between the developers of both encryptors.

Based on this PrideLocker sample analysis, a methodology using IDAPython to automate the decryption of strings as been detailed.

Finally, tips to detect ECDH and Sosemanuk algorithms covered in this article can help detecting other recent ransomware families based on Babuk leaked encryptors.

 

Many thanks to Eloi, who helped us to determine PrideLocker encryption capabilities.