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

alt text

  • Figure: Ransom note

Technical Analysis

CommandLine arguments

alt text

  • 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.

alt text

  • 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

alt text

  • Figure: Array of libcrypto API names and addresses

alt text

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

alt text

  • 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>.

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

alt text

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.

alt text

  • 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

alt text

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.

alt text

  • Figure: Detach and run as daemon

start thread

it starts a thread at address 0x402AA2 then creates 2 strings, filename + .crypt and filename + .tmp_

alt text

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

alt text

  • 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>

alt text

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.

alt text

  • Figure: Generate AES KEY/IV

alt text

  • 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.

alt text

  • 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

alt text

alt text

  • Figure: Author public key

Then It calls libcrypto api ECDH_compute_key to generate an ECDH shared secret.

alt text

  • 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;

alt text

  • Figure: Populate the above structure with AES key data

alt text

  • 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.

alt text

  • 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:

alt text

  • .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.

alt text

  • 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.

alt text

  • Figure: Malware encrypt the content of the target file

alt text

  • Figure: File encrypted

Finally it append again the structure + the sha256 of the file.

alt text

  • 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*)
}