The threat actor using Crytox ransomware has been active since at least 2020, but has received significantly less attention than many other ransomware families. In September 2021, the Netherlands-based company RTL publicly acknowledged that they were compromised by the threat actor. The company paid Crytox 8,500 euros. Compared with current ransom demands, this amount is relatively low. Unlike most ransomware groups, the Crytox threat actor does not perform double extortion attacks where data is both encrypted and held for ransom.
The modus operandi of the group is to encrypt files on connected drives along with network drives, drop the uTox messenger application and then display a ransom note to the victim as shown in Figure 1.
Figure 1. Crytox ransom note
The ransom demand period is set to five days to pressure the victim into paying as soon as possible
The sample analyzed by ThreatLabz has the following SHA256 hash 32eef267a1192a9a739ccaaae0266bc66707bb64768a764541ecb039a50cba67. In most cases, Crytox samples are packed with UPX. Once decompressed, a sample usually weighs in around 1.23MB because the whole uTox client is embedded inside the malware.
Cryptox uses different techniques to thwart static analysis including the following:
Some parts of the malware look directly written in assembly. The most noteworthy thing is the use of a specific implementation of AES-CBC shown in Figure 2.
Figure 2. Crytox implementation of AES
The authors borrowed the AES code and modified some parts to meet their needs. They even added an alternative algorithm using Intel x86 AES instructions. Oddly enough, the authors chose to only implement the Rijndael_Encrypt routine to both decrypt their config and encrypt files. This means that when they embedded their configurations, they used the AES decryption routine to encrypt them. The key used for decrypting the Crytox configurations are either the first or second block of 32 bytes of the AES lookup table Te1 using a NULL initialization vector (IV).
The malware encrypts the first-stage configuration using the aforementioned implementation of AES-CBC. Here, the AES key is the first 32 bytes of the Te1 lookup table a5c6636384f87c7c99ee77778df67b7b0dfff2f2bdd66b6bb1de6f6f5491c5c5 as shown in Figure 3.
Figure 3. Crytox first-stage configuration after decryption
This configuration contains the following information:
After this configuration has been decrypted, the malware locally generates a 2048-bit RSA key pair using the CryptGenKey function. The generated RSA private key is then encrypted five times using the hardcoded public key.
Under the sub-key HKCR\.waiting\shell\open\command\, the ransomware stores the following value-data pair shown in Table 1.
|"en"||Generated RSA public key|
|"n"||Encrypted generated RSA private key|
Table 1. Crytox registry configuration
In order to make sure the ransom note is displayed on startup, the registry value open along with the data "C:\ReadMe.hta" are created under
Once the Crytox configuration is stored, the code proceeds to locate a process to inject the second-stage. A remote thread is created to execute the first piece of shellcode.
This stage decrypts a second configuration using AES-CBC with the following key 5060303003020101a9ce67677d562b2b19e7fefe62b5d7d7e64dabab9aec7676 (which is the second block of 32 bytes from the lookup table Te1). According to this decrypted configuration, the shellcode executes a batch file to delete shadow copies and remove events from the logs. Essentially the following commands are run::
|for /F "tokens=*" %%1 in ('wevtutil.exe el') DO wevtutil.exe cl "%%1"
vssadmin.exe Delete Shadows /All /Quiet
diskshadow.exe /s ../pghdn.txt
The file pghdn.txt contains the line "delete shadows all".
Given the following hashing algorithm, the second-stage searches for the process ID (pid) of the process for which the hash of its name corresponds to 0xDCF164CD (explorer.exe) or 0x561F1820 (svchost.exe).
|name = process_name + "\x00"
hash = 0
for c in name.upper():
hash = ROTR32(hash, 0xD) + ord(c)
Inside a new thread, the shellcode creates a mutex by concatenating a hardcoded 4-letter word (e.g., "CSWS") with some random characters based on the pid of the targeted process as shown in Figure 4.
Figure 4. Crytox mutex creation
The thread then decrypts the content from the resource section of the original malware using the same algorithm and key as for the second configuration. This resource contains another shellcode, which is the final stage. This shellcode is injected inside the targeted remote process.
Using the same encryption algorithm, with the first 32-bytes of the Te1 lookup table as the AES key, this final stage decrypts the main configuration containing the following information:
First, the code tries to retrieve the configuration that the first stage stored in the registry hive. If this configuration doesn't exist, Crytox will create it. The code proceeds to set a countdown variable in the ransom note followed by replacing the string YOUR ID in the ransom note template. The latter value is replaced with a unique victim ID that is generated by the following pseudo-algorithm based on the encrypted locally generated RSA private key:
Figure 5. Crytox victim ID generation algorithm
Before encrypting any files, the malware removes the SeBackupPrivilege and SeRestorePrivilege privileges. Using the functions WNetOpenEnumW and WNetEnumResourceW, the malware retrieves connected drives and for each drive found, a thread is created to encrypt files. The same process is applied for every logical drive using the function GetLogicalDrives. The malware then waits for a lock to be released before calling the ShChangeNotify function in order to change the icon and file association and to display the ransom note to the victim.
The algorithm to discover all the files is relatively standard and relies on a recursive approach. The Windows directory is excluded from the search along with the ransom note and files with the .waiting extension. In addition, Crytox will only encrypt files that are larger than 16 bytes, which is the size of a block for AES. If the size of a file is not an exact multiple of 16 bytes, the malware will not pad and encrypt the last block of data. For large files, only the first 1,048,576 (0x100000) bytes are read and encrypted to optimize encryption speed.
For each file, a new 256-bit AES key is generated and the content of the file is encrypted using AES-CBC. Crytox then creates the following structure in Figure 6.
Figure 6. Cryptox cipher footer structure
The BLOBHEADER structure is set like this:
|.bType = PLAINTEXTKEYBLOB
.bVersion = CUR_BLOB_VERSION
.aiKeyAlg = CALG_AES_256
Since the structure is not initialized, the padding structure is filled with random data.
This structure is encrypted with the locally generated RSA public key. The resulting cipher is concatenated to the end of the encrypted file followed by the encrypted generated RSA private key. The encrypted file is renamed by appending YOUR ID.waiting to the original filename with YOUR ID replaced by the victim ID computed as described previously.
A ransom note is written to every directory after encrypting all files that are present. A process flow chart for Crytox is illustrated in Figure 7.
Figure 7. Process flowchart for Cryptox encryption
As stated previously, a 256-bit AES key is generated for each file that is encrypted.
The following algorithm in Figure 8 is used for the key generation.
Figure 8. Crytox key generation algorithm
The custom pseudo random key generator functions relies on the variables below:
The last value is stored inside the malware's configuration. This value has been the same across samples analyzed by ThreatLabz. The only unknown value necessary to determine the AES key is the value of GetTickCount at the time of encryption. However, if some plaintext of a file is known, efforts to bruteforce the AES key are feasible.
Based on file magic values, one can divise a bruteforce program with the following logic:
Figure 8 shows an bruteforce program running on a machine with 16 logic cores. Here, the encrypted file was dotnet-sdk-3.1.416-win-x64.exe (SHA1: 83A53E8770EDD38EDDD37DED63CEF2253FC16979) and the known plaintext was the Windows PE (MZ) file header 4D5A9000.
Figure 9. Cryptox example bruteforce key recovery
The method relies on knowing a part of the plaintext at a specific offset. Thus, only specific file types may be decrypted. Because the seed is based on GetTickCount, if one has access to the master file table (MFT) and is able to locate and decrypt the first and last file encrypted, then the range of GetTickCount values can be deduced. Therefore, the bruteforce range can be greatly reduced to decrypt all files.
Crytox exposes some interesting features to hinder static analysis by self-decrypting itself several times, injecting shellcode inside different processes, encrypting its configurations and using API hashing. The main file encryption logic of Crytox is standard using a unique AES key per file that is protected with RSA. However, the author(s) chose to rely on a weak random generator to create new AES keys. Using a 32-bit integer as the seed is not sufficient with today's computational power.
Ransomware families have a lot in common due to their shared goals and most use secure encryption schemes. However, there may still be implementation weaknesses that enable file decryption without having access to a private key. The bruteforce methods described in this blog could be reused for similar scenarios.
In addition to sandbox detections, Zscaler’s multilayered cloud security platform detects indicators related to the campaign with the following threat name: