HelloKitty Linux version malware analysis Published: 2021-07-17 · Archived: 2026-04-05 14:51:57 UTC 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 https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 1 of 17 Figure: Ransom note Technical Analysis CommandLine arguments Figure: Parsing commandLine arguments https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 2 of 17 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; } https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 3 of 17 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 https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 4 of 17 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= if it fails it uses a hard kill option esxcli vm process kill -t=hard -w= . 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 https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 5 of 17 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 https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 6 of 17 /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 https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 7 of 17 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. https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 8 of 17 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 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. https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 9 of 17 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 https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 10 of 17 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; https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 11 of 17 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 { // 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 https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 12 of 17 15 16 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 (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. https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 13 of 17 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 https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 14 of 17 Figure: File encrypted Finally it append again the structure + the sha256 of the file. https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 15 of 17 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: https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 16 of 17 $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*) } Source: https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html https://soolidsnake.github.io/2021/07/17/hellokitty_linux.html Page 17 of 17