Malware source code investigation: HelloKitty - part 1

HelloKitty ransomware represents a sophisticated strain of malicious software strategically designed for targeted attacks, demonstrating an evolved and nuanced approach within the realm of cybersecurity threats. First identified in November 2020, this ransomware variant distinguishes itself through its utilization of robust encryption algorithms, rendering victim files inaccessible and underscoring the formidable technical proficiency of its operators.

hellokitty

Threat Actor

It is well-known that the group breaches corporate networks, encrypts systems, and steals data. The compromised data and encrypted files are subsequently employed as leverage in double-extortion devices, where threat actors threaten data leakage in the absence of payment of a ransom.

HelloKitty is utilized by other ransomware operations and is notorious for launching numerous attacks, however, their most widely publicized assault occurred on CD Projekt Red in February 2021.

The threat actors claimed to have stolen and sold the source codes for Cyberpunk 2077, Witcher 3, Gwent, and other games during this attack.

Source code

A Microsoft Visual Studio solution for constructing the HelloKitty encryptor and decryptor, as well as the NTRUEncrypt library utilized by this variant of the ransomware to encrypt files, is included in the hellokitty.zip archive:

hellokitty

hellokitty

CRC32

First of all, the interesting things are in the crc32 folder:

hellokitty

This source code defines a CRC-32 (Cyclic Redundancy Check) algorithm in C for calculating the 32-bit CRC of a given data buffer. CRC is commonly used in error-checking mechanisms to detect errors in transmitted or stored data. The provided code includes a precomputed CRC-32 table for efficient computation.

This code serves as a standalone implementation of a CRC-32 algorithm.

Decryption

Next folder is decoder, where we can find files for decryption logic. This code decrypt files encrypted by the HelloKitty ransomware, and as we can see, extension is .kitty:

hellokitty

Function DWORD WINAPI decryptFile(file_to_decrypt *ftd): A function that handles the decryption of a file using NTRUEncrypt and AES.

void searchForFiles(PCWSTR widePath): Recursively searches for files in a given directory and queues them for decryption.

void searchForNetworkFolders(LPNETRESOURCEW pNetResource): Searches for files in network folders using Windows network APIs.

TOP_LEVEL_EXCEPTION_FILTER: Exception filter function.

bool CreateAndContinue(const wchar_t* _mutexName), void CloseMutex(): Mutex-related functions to prevent double process run.

void StopDoubleProcessRun(): Checks for existing instances of the decryption process.

The main function uses CommandLineToArgvW to parse command line arguments. Creates threads to decrypt files found on local drives or network folders. Implements a mutex mechanism to prevent double process run. Waits for thread completion and ensures all files are processed before exiting.

Innocent

The next folder with an interesting name Innocent:

hellokitty

First interesting thing is base64 implementation. This set of functions allows you to convert data between its binary representation and a human-readable base64-encoded format:

hellokitty

Next file with code is aesMbedTls.hpp:

hellokitty

This code defines a class, AES128MbedTls, that encapsulates the functionality for AES-128 encryption and decryption using the mbed TLS library.

This class abstracts AES functionality, making it easy to use AES-128 encryption and decryption with mbed TLS in a C++ program.

Encryption

The encryption logic is in the file Encryptor.cpp. Let’s start from function named DowngradeThreadTokenForThreadHandle.

This function is designed to downgrade the token of a thread by setting it to the linked token if the elevation type of the process token is full. It seems to be a part of privilege management or security-related logic.

Let us break it down for you:

void DowngradeThreadTokenForThreadHandle(PHANDLE hThread)

The function takes a pointer to a HANDLE (PHANDLE is typically used to represent a pointer to a HANDLE) as a parameter. This pointer (hThread) is presumably expected to hold the handle to a thread.

HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
  return;
}

Opens the process token for the current process in query mode (TOKEN_QUERY). If the process token cannot be opened, the function returns early.

Next:

union {
  TOKEN_ELEVATION_TYPE tet;
  TOKEN_LINKED_TOKEN tlt;
};

ULONG rcb;
if (!GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &rcb)) {
CloseHandle(hToken);
return;
}

Declares a union to hold either TOKEN_ELEVATION_TYPE or TOKEN_LINKED_TOKEN. Uses GetTokenInformation to retrieve information about the elevation type of the token (TokenElevationType). If the token information retrieval fails, the function closes the token handle and returns.

Next code is:

if (tet == TokenElevationTypeFull)
{
  if (GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
  {
    SetThreadToken(hThread, tlt.LinkedToken);
    CloseHandle(tlt.LinkedToken);
  }
}

Checks if the elevation type of the token is full (TokenElevationTypeFull). If true, it retrieves information about the linked token (TokenLinkedToken). Sets the thread token to the linked token using SetThreadToken. Closes the linked token handle.

Next function is BOOL IsWow64:

BOOL IsWow64()
{
  BOOL bIsWow64 = 0;
  using LPFN_ISWOW64PROCESS = BOOL(WINAPI*) (HANDLE, PBOOL);
  LPFN_ISWOW64PROCESS fnIsWow64Process;
  fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process");

if (nullptr != fnIsWow64Process)
{
if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
{
bIsWow64 = 0;
}
}
return bIsWow64;
}

This code defines a function named IsWow64 that checks if the current process is running on a 64-bit version of Windows with WoW64 (Windows on Windows 64-bit) support.

Next function is:

void removeShadows()

This function is designed to remove shadow copies using WMI. It utilizes the IWbemServices and IWbemContext interfaces to connect to WMI and perform operations on shadow copies:

hellokitty

The removeShadows function initializes WMI interfaces, configures context for 64-bit systems, connects to WMI, and deletes all instances of Win32_ShadowCopy. It ensures proper security settings and releases allocated resources, effectively removing shadow copies.

You can also see the commented code:

hellokitty

It seems to be a commented-out section containing a message to inform users about file encryption, its consequences, and instructions on obtaining a decryption key by paying a specified amount of money. This is a common approach in ransomware to communicate with victims.

The function SelfDelete2 is designed for the self-destruction of the malware executable.

void SelfDelete2() 
{
  WCHAR wMyPath[MAX_PATH * 2];
  GetModuleFileNameW(NULL, wMyPath, ARRAYSIZE(wMyPath));

WCHAR wMyFileName[MAX_PATH];
lstrcpyW(wMyFileName, PathFindFileNameW(wMyPath));

PathRemoveFileSpecW(wMyPath);
PathAddBackslashW(wMyPath);

WCHAR wCmd[MAX_PATH];
wsprintfW(wCmd, L"/C ping 127.0.0.1 & del %s", wMyFileName);
ShellExecuteW(0, L"open", L"cmd.exe", wCmd, wMyPath, SW_SHOWNORMAL);
}

Here’s a detailed breakdown:

Get Executable Path: Retrieves the full path of the running executable (wMyPath) and extracts the executable file name (wMyFileName).

Prepare Command: Constructs a command (wCmd) using the ping command to introduce a delay and the del command to delete the executable file.

Execute Command: Uses ShellExecuteW to open a new command prompt (cmd.exe) with the constructed command. The working directory is set to the directory containing the executable.

Self-Deletion: The command prompt executes the ping command, introduces a delay (doing nothing with 127.0.0.1), and then deletes the malware executable using the del command.

This technique attempts to delete the malware after a slight delay, making it harder to trace or analyze the malware post-execution.

Function read_next_block, is responsible for asynchronously reading the next block of data from a file using overlapped I/O:

hellokitty

Here’s a detailed breakdown:

Read File Asynchronously: Uses ReadFile with an overlapped structure (o->tempbuff) to read the next block of data from the file (o->hFile).

Check Asynchronous Result: If the result is FALSE and the error is ERROR_IO_PENDING, the operation is still pending, and it returns true.If the result is TRUE and the error is 0, the read operation is successful, and it returns true.

Handle End of File (EOF): If the result is FALSE and the error is ERROR_HANDLE_EOF, it means the end of the file is reached. Sets the operation type to operation_write_eof in the overlapped structure. Posts a completion status to the completion port (h_Port) to signal the EOF.

Handle Errors: If the error is ERROR_INVALID_USER_BUFFER, ERROR_NOT_ENOUGH_MEMORY, or ERROR_NOT_ENOUGH_QUOTA, it may indicate memory-related issues.

Logging: Logs messages based on different conditions and errors.

This function is a crucial part of the file processing logic, ensuring that the malware efficiently reads data from files asynchronously and handles various scenarios, including reaching the end of a file.

The write_block function is responsible for asynchronously writing a block of data to a file at a specified offset:

hellokitty

This function is crucial for the malware’s file manipulation logic, allowing it to efficiently write data to files asynchronously and handle various scenarios, including pending operations and potential errors.

The ReadWritePoolThread function is a worker thread for asynchronous file I/O operations:

hellokitty

This function orchestrates the encryption process, managing read and write operations, encryption, and file handling.

The FreeFileBusyResources function attempts to free resources associated with a file that might be in use:

hellokitty

As you can see, this function utilizes the Resource Manager functions to manage and release resources associated with a specified file.

The EncryptFileIOCP function encrypts a file using IO Completion Ports (IOCP):

hellokitty

This function encapsulates the process of opening, configuring, and initiating the encryption of a file using IO Completion Ports.

That’s all today. In the next part we will investigate the another interesting part: NTRUEncrypt logic.

We hope this post spreads awareness to the blue teamers of this interesting malware techniques, and adds a weapon to the red teamers arsenal.

By Cyber Threat Hunters from MSSPLab:

References

https://malpedia.caad.fkie.fraunhofer.de/details/win.hellokitty
mbed TLS library

Thanks for your time happy hacking and good bye!
All drawings and screenshots are MSSPLab’s

Article Link: Malware source code investigation: HelloKitty - part 1 - MSSP Lab