HelloFire ransomware
Threat Landscape
The encryptor has hit the scene recently, but without any notable leak site from the threat actor or typical ransomware branding. The ransomware note is not unique in the wording used, but it is clear the threat actor is masquerading as a pentester. This tactic has been used by other threat actors in the past and is not going to fool the victim when they come across the ransomware note on an encrypted system.
The ransomware note, contains two email contacts. The first being 'keemail.me' and the second 'onionmail.org'. Both of these contacts have been used by a number of threat actors over the years, notably 'keemail.me' reaches back to 2013, but it is hard to take the attack as a legitimate pentest, when these types of domains are given by the attacker.
Given that the sample has had a limited run, an official name has not been used for the threat actor. At this time a leak site is not available. I have given the encryptor the name 'HelloFire' as a result of identifying a few common words used by the threat actor.
Lastly, some overlap with Babuk was found in terms of strings and structures.
On March 19th, PCRisk raised the alarms on the new .afire ransomware extension (link)
Note & PDB Path
First, the encryptor has a number of references to the word 'hello' in English and in Russian. Second, the file extension used for the encrypted files are changed to '.afire'.
The ransomware note contains the 'Hello' greeting and contact information. This will be located in the 'Restore.txt' file.
PDB file path contains the same 'Hello' greeting, but in Russian as 'Zdravstvuy'.
Key points
Masquerading as a pentest
Potential Russian threat actor
Extensive list of services, directories and files to target
Build information
SHA256 Hash: 3656c44fd59366700f9182278faf2b6b94f0827f62a8aac14f64b987141bb69b ( VirusTotal )
The encryptor was built using Visual C++ as a Windows PE 32bit with a file size of 49.5KB. The sample was first seen in VirusTotal on 2024-03-16.
Program Flow
The encryptor starts off by acquiring a cryptographic context, the Windows API is used to get a handle to both the context and the random number generator. A series of next steps are used to inhibit system recovery by deleting the Windows shadow copy, stop a list of services / programs and clear the recycle bin.
Once complete, a new thread will be created to handle the encryption routine and file discovery.
Before the file discovery and encryption threads are signaled to process the content, the encryptor will attempt to enumerate volume drives and file shares that are connected to the target machine.
Inhibit system recovery
The sample will dynamically get a handle to 'kernel32.dll' via the 'GetModuleHandle' function. This is needed to obtain an address to the 'IsWow64Process' function. The reason the encryptor needs this, is that they want to disable the WoW64 FS redirection. This will ensure the encryptor is successful in running commands and expecting certain directories are in the right location.
Deleting the Windows shadow copies is done by the typical 'vssadmin.exe' called via 'cmd.exe /c'. This is a typical command in ransomware and many EDR systems or behavioural analysis sytems will identify the action quickly.
During the initial program flow, the encryptor will attempt to kill each process in the configuration block (see below for a list). The encryptor starts by calling 'CreateToolhelp32Snapshot' to get a handle to a list of processes that can be iterated on. The iteration is done via the 'Process32First' and 'Process32Next' functions, upon each iteration the running processes 'szExeFile' member is inspected and compared to the list of executables in the encryptors configuration. If found, the 'OpenProcess' and 'TerminateProcess' calls are used to stop the process, causing it to exit immediatly.
A handle to the service control manager is used to enumerate services, either via the 'EnumDependentService' or communicate to the service via the 'ControlService' API. Once a handle is obtained, the encryptor sends a 'SERVICE_CONTROL_STOP' signal via the appropriate service manager API. The encryptor uses a list of services (see below for a list) by name to query, and stop. If the service is currently active, the encryptor uses the 'dwWaitHint' member of the service status info structure to determine how long to wait before attempting to identify if the service has successfully shutdown.
Configuration
Configuration is stored in non-encrypted blocks located in the .data section. A list of executables used in identification during the kill process function, contains common applications that typically hold a lock to a file handle, that would be considered valuable to the encryptor. Notable email clients, databases and even steam are listed.
Next the list of services are found adjacent to the list of executables. These services are typically found on corporate machines. Services such as veeam, quick books, other backup agents and sophos.
Lastly, the file and directories listings are located together. It contains typical files that should not be encrypted or the encryptor runs the risk of destabilizing the system prior to completing the full encryption routine.
File and directory discovery
The encryptor will attempt to get root path handles to a set of known drive letters. The letters are in plain text and not encrypted. Before the file and directory discovery can take place, the drive letter mappings are needed. By utilizing the Windows APIs for 'GetDriveType' and 'FindFirstVolume' the encryptor can iterate over the known list of expected drives. If a handle is obtained, the 'SetVolumeMountPoint' or 'GetVolumePathNamesForVolumeName' are used to gather the drive information.
Network file shares are discovered via the 'NetShareEnum' function, this is a typical API used by ransomware. Typically this will find shares currently mapped, but it wont find hidden shares. The encryptor does attempt to find the 'ADMIN$' share by inspecting the share info structures net name member.
Once local volumes and network shares are mapped, the encryptor will use the 'FindFirstFile' and 'FindNextFile' functions to inspect the filesystem. For each directory found, the same function is called to recursivly process the sub directory tree. This recursive call was done using tail recursion and careful ensures that the stack is not overflown.
Encryption
The encryption thread begins by setting the target file to 'FILE_ATTRIBUTE_NORMAL' via the 'SetFileAttributes' call, then setup the file extension for the file by appending '.afire' and calling the 'MoveFileEx' API. Now that the file name is modified, the restart manager is used to ensure that the file is not "locked" by another running process. Although the encryptor takes time to ensure common services and processes are killed, there maybe many other services or applications running on the target system that will keep a valuable file locked. This would leave the encryptor in a position where it could not fully encrypt a file. To combat this, the encryptor calls upon the typical restart manager APIs. The 'RmStartSession' is used to obtain an initial handle, then using the 'RmRegisterResource' API the encryptor can register the file for obtaining a list of processes using it, this is done by calling the 'RmGetList' API.
Once a list of processes are obtained, the 'OpenProcess' call is used to get a handle to the process which can then be passed to the function 'TerminateProcess' to close it. Effectivly removing the "lock".
Curve25519 algorithm was identified for the encryption process. This is common in Babuk malware, further indicating clear overlap between the two encryptors.
YARA
rule HelloFireRansomware {
meta:
description = "Rule to detect HelloFire ransomware"
author = "ShadowStackRe.com"
date = "2024-03-24"
Rule_Version = "v1"
malware_type = "ransomware"
malware_family = "HelloFire"
License = "MIT License, https://opensource.org/license/mit/"
Hash = "3656c44fd59366700f9182278faf2b6b94f0827f62a8aac14f64b987141bb69b"
strings:
$strExt = ".afire" wide
$strRestore = "Restore.txt" wide
$strShadowCopy = "vssadmin.exe delete shadows /all /quiet" wide
$strMutex = "MoreMoney"
$strPDBPath1 = "Zdravstvuy"
$strPDBPath2 = "e.pdb"
condition:
uint16(0) == 0x5A4D and
all of them
}