HelloKitty Linux version malware analysis
Please read the disclaimer
Introduction
This report contains technical details of the new linux version of HelloKitty that targets VMware ESXi servers.
The encryption used by this variant is AES_CBC and Elliptic-curve Diffie–Hellman (ECDH) to protect the keys.
Encryption Overview
The malware generates an ECDH keypair, then using the hardcoded public key of the threat actor, it generates an ECDH secret key, then an AES KEY/IV are randomly generated at run time, this key will be used to encrypt a file.
Note: the AES KEY/IV are different for each file.
The AES KEY/IV is encrypted using a randomly generated IV and the previous ECDH secret key with AES algorithm.
Finally a structure is populated with the ECDH public key of the malware, the encrypted AES KEY/IV used for file encryption and other stuff.
The structure is appended with the ransom note and the encrypted file .crypt
The Threat actor can recover the ECDH secret to decrypt the encrypted AES KEY/IV used for encrypting the file with his Private ECDH key and the malware Public ECDH key.
Ransom Note
- Figure: Ransom note
Technical Analysis
CommandLine arguments
- Figure: Parsing commandLine arguments
option | Functionality |
---|---|
-k | Kill VM processes |
-d | Run as daemon |
-e | Encrypt VM files |
-v | Enable verbose |
Dynamically loading libcrypto API
The malware loads some OpenSSL API from libcrypto.so using dlopen/dlsym.
- Figure: Dynamically loading libcrypto API
Looking at the array, we can see that each entry is a structure of 3 pointers:
1
2
3
4
5
{
unsigned char* new_function_name;
unsigned char* old_function_name;
void* pointer_to_api;
}
First the ransomware tries to get the address of the function name stored in new_function_name, if not found (which means the library is old) it uses old_function_name, if the API was found , it’s pointer will be stored in pointer_to_api
- Figure: Array of libcrypto API names and addresses
We can rename the pointer_to_api with this pythonIDA script, it gets new_function_name string and it then set the name for pointer_to_api
1
2
3
4
5
start = get_name_ea(0, "array_func_name")
for i in xrange(0x39):
func_name = get_strlit_contents(Qword(start))
set_name(start+0x10, func_name)
start += 0x18
Ignores signals
The malware will ignore the following signals, so that it won’t be interrupted during encryption, this will prevent half encrypting files which leads to file corruption.
- SIGCHLD
- SIGTSTP
- SIGTTOU
- SIGTTIN
- SIGHUP
- SIGTERM
- Figure: Malware ignores some signals
List VM processes
It executes the command esxcli vm process list
to list every VirtualMachine processes currently running on the infected machine.
It then parses through the output to extract Process ID and Config File which is basically the path to the VMX file of the VM
This data is saved in a array of stucture of type
{
uint64_t Process_ID;
unsigned char *Vmx_Path;
}
kill VM porcesses
Using the previous array, the malware first tries to kill the processes with a soft kill esxcli vm process kill -t=soft -w=<Process_PID>
if it fails it uses a hard kill option esxcli vm process kill -t=hard -w=<Process_PID>
.
Recursive file search
It uses the paths given as command line arguments and explore recursively the directories using opendir readdir.
For each file read, it first checks if the file is not . or .. and does not contain the strings .crypt or .README_TO_RESTORE
Switch (file type)
case directory:
It checks if it is not one of the following directories:
- /bin
- /boot
- /dev
- /etc
- /lib
- /lib32
- /lib64
- /lost+found
- /proc
- /run
- /sbin
- /usr/bin
- /usr/include
- /usr/lib
- /usr/lib32
- /usr/lib64
- /usr/sbin
- /sys
- /usr/libexec
- /usr/share
- /var/lib
In case the check pass, it calls recursively the same function with the new directory path as argument.
- Figure: Recursive directory search
case file:
In case it was a file and the -e option was specified in command line arguments, it will check if the file does not contain the following strings .crypt, .tmp_, .README_TO_RESTORE, then checks if it contains one of the following strings
- .vmdk
- .vmx
- .vmsd
- .vmsn
if -e was not specified, it will check if the filename does not contain one of the following strings
- .crypt
- .READ_ME_TO_RESTORE
- .tmp_
- .a
- .so
- .la
Finally if the size of the file is bigger than 256 bytes, it saves the path to the file for later usage (encrypting it… of course)
switch to daemon process
If the -d option was specified in command line arguments the malware calls daemon to detach itself from the controlling terminal and run in the background as system daemons.
- Figure: Detach and run as daemon
start thread
it starts a thread at address 0x402AA2
then creates 2 strings, filename + .crypt
and filename + .tmp_
A function is called that tries to set a lock on the file using fcntl
, if it fails it will get the PID of the process that is currently locking the file
- Figure: Malware try to set a lock on the file
If the PID is greater than 10 (not a system process), it will kill it with the command kill -9 <PID>
The malware then rename the file to filename + .tmp_
then it will call a function (0x405D64) to encrypt the file.
In case of failure it will roll back to the original filename
In case of successful encryption it will rename the .tmp_
to .crypt
Encryption
Generation of keys
It derives an AES_256_CBC KEY and IV with libcrypto function EVP_BytesToKey with a randomly generated salt and data using RAND_bytes API that will be used for file content encryption.
- Figure: Generate AES KEY/IV
- Figure: Official documentation of OpenSSL API https://www.openssl.org/docs/man1.0.2/man3/EVP_BytesToKey.html
Afterwards it generates the malware ECDH private/public keys.
- Figure: Generate client ECDH keypair
Next it will call a function at address 0x4054F1 (I named it func_compute_secret) with the newly generate EC_KEY and the public key of the author
- Figure: Author public key
Then It calls libcrypto api ECDH_compute_key to generate an ECDH shared secret.
- Figure: Generate ECDH secret
After that it populate a custom structure (I named it custom_structure00)
of the following type with the AES KEY, IV and the size of the file.
1
2
3
4
5
6
7
{
unsigned char* save_AES_IV[0x10];
unsigned char* save_AES_KEY[0x20];
unsigned __int64 size_of_file;
unsigned int defined_constant;
unsigned int alignemnt;
} custom_structure00;
- Figure: Populate the above structure with AES key data
- Figure: Example of the structure populated with data
Then it encrypts the structure custom_structure00
using the ECDH secret and a randomly generated IV of 16 bytes with AES algorithm.
- Figure: Encrypt the above structure with the ECDH secret
After that, it populates yet another important structure custom_structure01
of following type:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
// The IV used to encrypt the custom_structure00 structure. | offset 0 - 0x10
unsigned char secret_IV[0x10];
// The size of custom_structure00 | offset 0x10 - 0x14
unsigned int size_of_custom_structure00;
// Encrypted custom_structure00 | offset 0x14 - 0x34
custom_structure00 encrypted_custom_structure00;
// Size of the client public key | offset 0x58 - x5c
unsigned int size_of_public_key ;
// Client public key | offset 0x5c - 0xa0
unsigned char public_key[0x44];
// Size of the sig | offset 0xa0 - 0xa4
unsigned int size of sig;
// Sig | offset 0xa4 - 0xf0
unsigned char sig[0x47];
} custom_structure01;
Finally it writes to filename + .README_TO_RESTORE
the ransomware note, then it append the previous structure at the end of the file.
It also append the SHA256 of the <original file content + appended data>
(see next)
Example:
.README_TO_RESTORE
file tail, showcasing the above structure(custom_structure01)
and the SHA256 hash of the file
It then appended the same structure (custom_structure01)
to the target file.
- Figure: Original content of the file +
(custom_structure01)
Finally it reads the data of the target file and uses the file encryption AES key to encrypt it.
- Figure: Malware encrypt the content of the target file
- Figure: File encrypted
Finally it append again the structure + the sha256 of the file.
- Figure: Encrypted file + custom_structure01 + sha256
Conclusion
This linux variant can target Virtual machines files, which can be crucial to companies without backup and replication.
The encryption scheme used utilize ECDH (Elliptic-curve Diffie–Hellman) algorithm, which means without the private key owned by the threat actor, it will be near impossible to decrypt the encrypted files.
YARA Rule
rule hellokitty_linux {
meta:
description = "YARA rule HelloKitty linux variant ransomware"
reference = "https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html"
author = "@soolidsnakee"
date = "2021-07-17"
strings:
$str1 = ".crypt"
$str2 = ".README_TO_RESTORE"
$str5 = "switch to daemon"
$str6 = "esxcli vm process kill -t=hard -w=%d"
$str7 = "work.log"
$str8 = "m:vdekc:"
condition:
all of ($str*)
}