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.
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:
CRC32
First of all, the interesting things are in the crc32
folder:
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
:
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
:
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:
Next file with code is aesMbedTls.hpp
:
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:
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:
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:
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:
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:
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:
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):
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