Minerva Labs undertook a detailed research of the Egregor ransomware, with the goal of providing an in-depth analysis of how it works to infect a target. Better knowledge of threat actor’s techniques can help security experts detect and mitigate novel threats, which is especially important considering the recent evolution of ransomware. In the scope of this research we have tried to determine what evasive techniques the malware uses.
The recent surge in Egregor ransomware and the code similarity to Sekhmet and Maze ransomware strains leads us to believe that they probably share the same code base. In addition, similar code obfuscation techniques are used by Maze and Egregor, which slow the analysis process and hinder researchers.
Blog Posts about Egregor detail varying degrees of obfuscation. In our case both the loader and the actual ransomware were highly obfuscated, which forced us to write deobfuscation scripts that ease the analysis process.
The ransomware we encountered is a DLL file named b.dll. The file was executed manually using the following command line:
A look at the disassembly of the code indicates that it is highly obfuscated with compiler-based techniques, which makes static analysis of the code quite time consuming.
For example, see below the function DllRegisterServer, which the attacker uses to launch the ransomware, in IDA’s graph view:
The obfuscation Egregor uses is similar to the one used in Maze ransomware. We were able to modify Blueliv’s Maze deobfuscation script (the blogpost and the original script can be found here) to fit Egregors obfuscation patterns, which allowed for easier analysis of the ransomware.
The loader checks for the command line “–nop” and exits if it exists.
As for further unpacking, a large blob of data is decrypted with the following steps:
- The blob is xor decoded with a hardcoded key (0x4 in our sample).
- The xor’ed data is then Base64 decoded using the windows API function CryptStringToBinaryA.
- A hardcoded key and IV is initialized for the ChaCha20 algorithm, which is then used for the final decryption of the payload. The malware authors decided to change the number of rounds of key rotations from the default of 20 to only 4.
After decrypting the second payload, a DLL file, it is copied to a new allocation that is created using VirtualAlloc with the page permissions RWX.
The last stage of the initial loader is the preparation of the payload in memory. The malware reflectively loads the decrypted payload and uses the function CreateThread to transfer execution to its next stage.
The next stage parses the command line, looking specifically for the parameter -p, which contains a password that is used for the decryption of the ransomware binary. The ransomware is decrypted using a stream cipher that shares some of its constants with Rabbit cipher:
The ransomware is compiled as a DLL file with only one export named “DllEntryPoint”. The function creates a thread that executes the main subroutine of the ransomware:
Before starting the ransomware’s malicious procedure, a function is called to determine the locale of the workstation. The ransomware uses three different Windows API functions to make sure it is not encrypting a computer located in Russia or any other CIS country:
Egregor will terminate if any of the following locales are found:
|0x843||Uzbek – Cyrillic|
|0x819||Russian – Moldova|
|0x440||Kyrgyz – Cyrillic|
|0x443||Uzbek – Latin|
|0x818||Romanian – Moldova|
After the locale check, the ransom configuration will be decrypted from a buffer located in the data section of the executable. The first 8 bytes of the encrypted configuration starts with a PNG header which is skipped by the parser before its decryption. The subsequent DWORD contains the size of the configuration to decrypt. Starting from offset 12, the configuration will be decrypted using round-modified ChaCha20 and a hardcoded key and IV.
The encrypted configuration in-memory:
Decompilation of the configuration class initialization function:
The configuration contains several interesting settings:
- The ransom note.
- List of processes to terminate.
- Blacklisted keywords for the services termination algorithm.
- A hardcoded RSA 2048-bit public key which is used for the file encryption scheme.
- Flags for the presence of remote addresses.
In order to create a fingerprint of the encrypted workstation Egregor uses several API functions to extract information about the machine:
The ransomware uses the API functions GetLogicalDriveStrings and GetDiskFreeSpace to identify the names and types of the logical disks connected to the device in addition to the amount of free space available in them.
The ransomware RSA public key in memory, stored in the encrypted configuration:
For each execution, a pair of private and public keys are generated. The public key is used for encrypting the symmetrical keys that would later be used for encrypting each file. A unique symmetrical key is generated for every file to be encrypted.
Egregor’s key generation scheme is as follows:
- A 2048-bit RSA key pair is generated using CryptGenKey – this is the session key.
- The key is then exported using the API CryptExportKey.
- The exported private key is encrypted with ChaCha using a randomly generated key and IV.
- The ChaCha keys are encrypted using the function CryptEncrypt and the configuration-embedded RSA public key.
- The encrypted ChaCha key and the encrypted session key are saved to disk in a hardcoded path, which in our case is %ProgramData%\dtb.dat.
It is worth noting that the ransomware encrypts the session key with the same protocol that is used to decrypt the ransomware payload (Rabbit Cipher).
The ransomware will stop certain processes and services before encrypting the machine. A list of hardcoded process names is stored in the encrypted configuration file and the malware uses NtQuerySystemInformation to enumerate the running processes and terminates them using the function NtTerminateProcess.
The list of processes that will be terminated, in our sample (a list will also be available in the IOCs section):
As for the service stopping algorithm, the ransomware configuration contains a list of strings that will be used to determine which service should be stopped. Services names will be enumerated using the API function EnumServicesStatus. Any service name that contains the blacklisted strings will be stopped using windows Service Control Manager API.
The list of services keywords in the configuration:
Egregor has the capability to contact hardcoded HTTP URLs. If the offset 0x3a31e and 0x32fb in the configuration does not contain 0, the ransomware will contact IP address/DNS names (which are also embedded in the configuration), and decode their content using the same modified-ChaCha20/Base64 combination used before.
The IDAPython deobfuscation script can be found here.
b9b71eb04d255b21e3272eef5f4c15d1c208183748dfad3569efd455d87879c6 (Egregor loader)
(unpacked ransomware from memory)
- ChaCha20 – https://en.wikipedia.org/wiki/Salsa20
- Rabbit Cipher – https://en.wikipedia.org/wiki/Rabbit_(cipher)
- Blueliv’s maze deobfuscation – https://www.blueliv.com/cyber-security-and-cyber-threat-intelligence-blog-blueliv/escape-from-the-maze/
- Minerva Labs Sekhmet – https://minerva-labs.com/blog/minervalabs-vs-sekhmet