BYOVD in 2026 Is Eating EDR Alive: A Systematic Teardown of DragonForce’s Five-Driver Kill Chain

A ransomware crew walked into an environment in December 2025, dropped five separate kernel drivers, and turned off Defender, SentinelOne, and Kaspersky one after another before a single file got encrypted. Three of those drivers were legitimately signed and carried public CVEs. One was a Huawei audio driver nobody had ever flagged as dangerous. The last one was built from scratch to look like a Palo Alto product. The endpoint agent never stood a chance, because the fight was over before it reached the part of the kernel where the agent could see anything.

That intrusion is DragonForce’s Backdoor.Turn campaign, and it is the cleanest field demonstration yet that the Bring Your Own Vulnerable Driver model has outgrown the defenses built to stop it. The blocklist is a museum catalog of yesterday’s drivers. Attackers are now shopping from a supply of millions of signed binaries and, when that runs dry, building their own. This post tears the whole chain apart, driver by driver, primitive by primitive, then rebuilds it into a detection strategy you can actually deploy.


Why BYOVD works at all: the ring-0 trust problem

Modern EDR is hard to kill from user mode by design. Defender’s scanning engine MsMpEng.exe runs as a Protected Process Light (PPL) with a Windows-signer protection level. SentinelOne and most serious agents register as PPL or rely on an Early Launch Anti-Malware (ELAM) driver to seed their protection before any third-party code loads. The practical effect: a token running as SYSTEM in user mode still cannot call OpenProcess with PROCESS_TERMINATE against a PPL target. The Object Manager’s access check fails on the protection-level comparison, not on the token. You are administrator and you still get ACCESS_DENIED.

That boundary is enforced from kernel mode. So the obvious move, the one every BYOVD operator makes, is to get into kernel mode and ask the kernel to do the killing. Inside ring 0 there is no PPL check on ZwTerminateProcess. The kernel trusts itself. A signed driver running in IRP_MJ_DEVICE_CONTROL context is the kernel for all practical purposes, and if that driver exposes process termination through an unauthenticated IOCTL, any user-mode caller who can open its device handle inherits a SYSTEM-over-PPL kill primitive.

This is the entire game. BYOVD is not an exploit of Windows. It is an abuse of the trust Windows extends to a validly signed kernel driver, multiplied by the fact that Microsoft cannot retroactively un-sign the thousands of vulnerable drivers already in circulation.


Hierarchy diagram showing how a user-mode attacker is blocked by PPL from killing Defender directly but bypasses the check by routing through a vulnerable kernel driver in ring 0
The PPL boundary only holds at ring 3 – once the attacker delegates the kill to a signed kernel driver, the protection-level check is never consulted.

The Backdoor.Turn intrusion anatomy

Before the kernel work, the operational context. Symantec and Carbon Black disclosed this in June 2026 from a December 2025 incident, and the kill chain is worth walking because every driver below served a specific stage.

StageTechniqueDetail
Initial accessLikely SQL/MSSQL server flawFoothold on an internet-facing database host
StagingZIP archive dropLegitimate VirtualBox/DbgView binary plus a malicious sideload DLL
ExecutionDLL search-order hijackMalicious DLL loaded by the trusted signed executable
Defense evasionFive-driver BYOVD sequenceHWAuidoOs2Ec.sys, wsftprm.sys, GameDriverX64.sys, K7RKScan.sys, Abyss Worker
C2Backdoor.Turn (Go RAT)Injected into DbgView64.exe; anonymous Teams visitor token, Microsoft TURN relay, QUIC session to real C2
ImpactDragonForce ransomwareEncryption begins once EDR is dead

The C2 design alone deserves a mention because it is genuinely clever tradecraft: Backdoor.Turn grabs an anonymous Microsoft Teams visitor token from Skype-backed identity services and tunnels its real traffic through a legitimate Microsoft TURN relay before opening a QUIC channel to the operator. The beacon looks like Teams. But the part that actually decides whether the victim lives or dies is the five-driver teardown of endpoint protection, so that is where we go deep.


Flowchart showing the five-stage DragonForce driver kill chain from initial SQL access through five successive kernel drivers to ransomware deployment
Each driver in the chain served a precise role – together they dismantled three separate EDR products before a single file was encrypted.

WDM internals: the attack surface in three structures

Every driver in this chain follows the same Windows Driver Model skeleton, and you cannot reason about the vulnerabilities without it.

When a driver loads, the kernel calls its DriverEntry. Inside, the driver creates a device object with IoCreateDevice, exposes it to user mode with IoCreateSymbolicLink, and wires its dispatch table so that incoming IOCTLs land on a handler:

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    UNICODE_STRING devName, symLink;
    PDEVICE_OBJECT deviceObject;

    RtlInitUnicodeString(&devName, L"\\Device\\HWAudioDevX64");
    RtlInitUnicodeString(&symLink, L"\\DosDevices\\HWAudioX64");

    IoCreateDevice(DriverObject, 0, &devName, FILE_DEVICE_UNKNOWN,
                   0, FALSE, &deviceObject);
    IoCreateSymbolicLink(&symLink, &devName);   // now reachable as \\.\HWAudioX64

    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDeviceControl;
    DriverObject->MajorFunction[IRP_MJ_CREATE]         = DispatchCreate;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]          = DispatchClose;
    return STATUS_SUCCESS;
}

That symbolic link is the front door. A user-mode process does CreateFile("\\\\.\\HWAudioX64", ...), gets a handle, and fires DeviceIoControl against it. The kernel packages the request into an IRP and routes it to DispatchDeviceControl. Inside the handler, the driver pulls the IOCTL code and buffers out of the current IRP stack location:

NTSTATUS DispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
    ULONG ioctl = stack->Parameters.DeviceIoControl.IoControlCode;
    // ... vulnerable handlers branch on `ioctl` here, with no caller check
}

The vulnerability class across all three CVE-tied drivers is identical: the handler branches on the IOCTL code and performs a privileged kernel action with no validation of who is calling. No SeTokenIsAdmin. No PPL gate. No check that the caller has any business killing a process. The dispatch routine treats a request from a guest user the same way it treats a request from its own user-mode service.

Decoding the IOCTL codes

Every observed IOCTL is a packed CTL_CODE. The macro layout is DeviceType << 16 | Access << 14 | Function << 2 | Method. Decode the real ones and a pattern jumps out:

DriverIOCTLDeviceTypeFunctionMethodAccess
wsftprm.sys0x22201C0x220x007BUFFEREDANY
GameDriverX64.sys0x2220400x220x010BUFFEREDANY
K7RKScan.sys0x2220180x220x006BUFFEREDANY
HWAuidoOs2Ec.sys0x2248DC0x220x1237NEITHERANY

Access is FILE_ANY_ACCESS on all four. That field is supposed to be the first line of defense: set it to FILE_WRITE_ACCESS and the handle must be opened with write rights. Every one of these drivers shipped the most permissive setting. That is the original sin that makes them weaponizable, and it is exactly the kind of mistake static analysis can flag at scale.


Driver 1: wsftprm.sys, breaking PPL with an anti-fraud driver

wsftprm.sys 2.0.0.0 ships inside Topaz Antifraud, signed by TPZ SOLUCOES DIGITAIS LTDA. CVE-2023-52271, CVSS 6.5, scope-changed because the kill crosses the PPL trust boundary.

The mechanism is brutally simple. Send IOCTL 0x22201C with a 1036-byte input buffer where the first four bytes are the target PID as a DWORD. The driver reads that PID, opens the process from kernel context, and calls the imported ZwTerminateProcess. Because the call originates in ring 0, the PPL protection-level check that blocks user mode never fires. Defender’s MsMpEng.exe, a PPL process, dies.

The detail that should make you uncomfortable: as of mid-2026, this driver is still not on Microsoft’s recommended driver blocklist. A publicly documented, CVE-assigned, PPL-killing driver has been usable for over two years and the central defensive mechanism still does not cover it. Hold that thought for the appendix.


Driver 2: GameDriverX64.sys, anti-cheat repurposed as anti-EDR

GameDriverX64.sys is the anti-cheat driver for Hotta Studio’s Tower of Fantasy. CVE-2025-61155, versions up to 7.23.4.7, with a “fixed” 8.26.2.9 that researchers report is still vulnerable.

This one is more interesting than wsftprm because the authors tried to add an access check and built a bad one. The driver installs as a SERVICE_KERNEL_DRIVER, creates the symbolic link \\.\E64, and exposes IOCTL 0x222040. But before terminating, it checks the input buffer for a magic cookie: 0xFA123456. That is not authentication. It is a hardcoded constant in a binary anyone can decompile. The “secret” is sitting in the .text section as a cmp instruction:

// reconstructed handler logic
if (*(DWORD*)inputBuffer == 0xFA123456) {       // "authentication"
    DWORD targetPid = *(DWORD*)(inputBuffer + 4);
    // PsLookupProcessByProcessId -> ZwTerminateProcess
}

The user-mode side enumerates processes with CreateToolhelp32Snapshot, matches the kill list, prepends the magic value, and sends each PID through. Two operational notes that matter for hunting: the driver refuses to run if it detects a hypervisor (it CPUIDs for VBOX, VMWare, XenVMM, and KVM signatures and bails), which frustrates naive sandbox detonation. And the Interlock ransomware group ships the same binary renamed to UpdateCheckerX64.sys in a tool they call “Hotta Killer.” DragonForce kept the original Tower of Fantasy filename. Filename is not identity. Hash, signer, and the 0xFA123456 constant are.


Driver 3: K7RKScan.sys, the vendor that kept half the bug

K7RKScan.sys from K7 Computing is the best teaching case in the chain because it shows you exactly how vendors get driver remediation wrong.

Version 15.1.0.6 carried CVE-2025-1055. Device \Device\NTK7RKScnDrv, symbolic link \DosDevices\DosK7RKScnDrv, IOCTL 0x222018. No caller validation at all, so it was a double-barreled bug: local privilege escalation and a BYOVD process killer. The public PoC simply targets MsMpEng.exe and watches Defender disappear.

K7 then shipped version 23.0.0.10 and assigned the fix CVE-2025-52915. They added an admin check and process filtering. Read that carefully: they fixed the privilege escalation path so a low-priv user can no longer use it to climb. But an attacker who is already admin (which is the whole premise of BYOVD, where you bring and load a driver) sails right through the admin gate and still gets kernel-mode process termination. The functionality that made the driver attractive to ransomware operators was preserved.

This is the structural reason the blocklist will always lag. Vendors patch the CVE that embarrasses them (privilege escalation, the thing pen testers report) while keeping the kernel capability their product needs. The driver stays signed, stays useful to attackers, and the “fixed” version is arguably more dangerous because it carries a clean bill of health.


Driver 4: HWAuidoOs2Ec.sys, the Havoc Process Terminator

Now the novel one, and the most instructive. HWAuidoOs2Ec.sys is a genuine Huawei audio driver, signed by Huawei Device Co., Ltd., version info reading Huawei Audio Driver. DragonForce dropped it to %TEMP%\Havoc.sys and registered it as a kernel service named Havoc.

Internals: device \Device\HWAudioDevX64, symbolic link \DosDevices\HWAudioX64, user handle \\.\HWAudioX64, IOCTL 0x2248DC taking a 4-byte PID. The tooling around it (HwAudKiller, with a console banner that literally reads “Havoc Process Terminator” and a HwAudKiller.pdb path) loops every 100 milliseconds through the full process list, matching against 23 hardcoded security-product process names and firing each PID at the driver. The decompiled kernel side resolves the PID and opens it with PROCESS_ALL_ACCESS through a wrapper the analysts labeled mw_ZwOpenProcess_wrapper, then calls ZwTerminateProcess:

// reconstructed kernel kill path for IOCTL 0x2248DC
HANDLE hProc;
CLIENT_ID cid = { (HANDLE)targetPid, NULL };
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);

if (NT_SUCCESS(ZwOpenProcess(&hProc, PROCESS_ALL_ACCESS, &oa, &cid))) {
    ZwTerminateProcess(hProc, 0);    // ring-0, no PPL check
    ZwClose(hProc);
}

The 100ms loop matters operationally: it is not a one-shot kill, it is a persistent reaping. If the EDR’s watchdog tries to relaunch a terminated component, the loop catches the new PID within a tenth of a second and kills it again. The agent flatlines and cannot self-heal.

The delivery is just as deliberate. The killer arrives inside a multi-stage “FatMalloc” crypter that allocates roughly 2GB to evade sandboxes (many automated analysis environments cap memory and either skip or fail the allocation), then uses RtlDecompressBuffer to inflate the final payload at runtime so the killer never sits decrypted on disk.

Here is the part that should change how you think about the blocklist. At the time of the December 2025 attack, HWAuidoOs2Ec.sys was not in LOLDrivers, not in Microsoft’s blocklist, and not in any prior public research. Huntress only documented its exploitable status in March 2026, months after DragonForce had already used it in anger. The lead time for defenders was negative. You cannot blocklist a driver nobody has flagged, and Huawei ships a lot of laptops, which means this binary is plausibly already present and trusted in plenty of environments. The blocklist is a denylist in a world where attackers keep finding new things to allow.


Illustration of an automated audio driver chip firing repeated kill pulses destroying security process shields in a persistent loop
HWAuidoOs2Ec.sys polled every 100 milliseconds – not a one-shot kill but a persistent reaping loop that prevented any EDR watchdog from relaunching a terminated component.

Driver 5: Abyss Worker, when attackers stop borrowing

The fifth driver is a different species, and Symantec called it out specifically because it is rare. Abyss Worker is not BYOVD. It is a purpose-built malicious kernel driver carrying a code-signing certificate, crafted to masquerade as a legitimate Palo Alto Networks driver.

The distinction is the whole point of this section. Traditional BYOVD has a defensive answer, even if it is a slow one: enumerate vulnerable signed drivers, add their hashes to a blocklist, deny the load. That model assumes the dangerous driver is a legitimate product with a bug. Abyss Worker breaks the assumption. There is no innocent vendor to notify, no CVE to assign, no “fixed version” to ship. The driver is malicious from the first instruction, and it loaded anyway because the signature chain validated.

PropertyTraditional BYOVDCustom malicious signed driver
Driver originLegitimate, vulnerableAttacker-built, malicious by design
Defensive primitiveHash blocklist of known-bad driversCertificate revocation, behavioral kernel telemetry
CVE appliesYesNo
Vendor can fixYesN/A
Blocklist coverageEventuallyNever (no legitimate driver exists)

Abyss Worker is the canary. When a ransomware crew invests in building and signing its own kernel weaponry, the entire denylist paradigm becomes a rear-guard action. The future of this threat is allowlisting (WDAC) and kernel behavior monitoring, not a growing catalog of things to forbid.


Illustration of a forged signing key disguised among legitimate driver keys, representing a purpose-built malicious signed kernel driver masquerading as a trusted vendor
Abyss Worker carried a valid code-signing certificate crafted to impersonate Palo Alto Networks – when attackers forge the key itself, no blocklist of borrowed tools can help.

Beyond the kill switch: callback deregistration

DragonForce’s five drivers all reached for the same blunt instrument, ZwTerminateProcess. That is loud. The more sophisticated end of the BYOVD world (EDRSandblast, the various Kernel Callback Remover tools) does something quieter: it neuters the EDR without killing it.

EDR products see process and thread creation by registering kernel callbacks via PsSetCreateProcessNotifyRoutine and PsSetCreateThreadNotifyRoutine. Those callbacks live in a kernel array. An attacker with a kernel read/write primitive can enumerate the array and zero out the EDR’s entry. The agent process keeps running and looks healthy in your console, but it is blind. In WinDbg, the educational view of that array is one command:

kd> dq nt!PspCreateProcessNotifyRoutine L20

Each non-null entry points to an _EX_CALLBACK_ROUTINE_BLOCK (the low bits are flags, mask them off to get the pointer). Mapping a slot back to a driver tells you which agent owns it. The same idea extends to ObRegisterCallbacks (handle-stripping callbacks EDRs use to protect their own processes) and to file-system minifilters that can be detached with FltUnregisterFilter. None of this was in the DragonForce chain. All of it is where mature tooling is heading, and a process-kill-only detection strategy will be deaf to it. Watch for both: the process that vanishes and the agent that goes quiet while still alive.

A note on practice: study these primitives against an intentionally vulnerable lab driver you compile yourself with the WDK, on a test-signing Windows 11 23H2 VM with KDNET debugging. Build a VulnIoctlDrv.sys with a FILE_ANY_ACCESS device-control handler that calls ZwTerminateProcess, register it with sc create VulnDriver type=kernel binPath=..., and exercise it against a throwaway process. That gives you the full mechanism with zero risk of weaponizing a real signed driver against a production estate.


Detection engineering: closing the visibility gap

The kernel work above is the why. This is the what to do. Map everything to MITRE ATT&CK T1562.001 (Impair Defenses: Disable or Modify Tools) and T1068 (Exploitation for Privilege Escalation), with the driver loads themselves under T1543.003 (Create or Modify System Process: Windows Service).

Telemetry anchors

SourceSignalHunt for
Sysmon Event ID 6DriverLoad (ImageLoaded, Hashes, Signature, SignatureStatus)Drivers loading from \Users\, \Temp\, \ProgramData\; signers like TPZ SOLUCOES, Hotta, Huawei on a server
Sysmon Event ID 1Process createsc.exe create ... type=kernel, sc.exe start, drivers staged then service-installed
System log 7045Service installedNew kernel service with a binary path in a writable directory
Sysmon Event ID 13Registry setHKLM\SYSTEM\CurrentControlSet\Services\<name>\ImagePath pointing to %TEMP%
ETW Microsoft-Windows-Kernel-ProcessSensitive process terminationA PPL process (MsMpEng.exe, agent processes) terminated by a non-PPL, non-system caller
EDR self-telemetry gapAgent silenceHeartbeat loss without a clean shutdown event = possible callback removal

A Sysmon driver-load rule worth deploying

title: BYOVD - Vulnerable or Anomalous Kernel Driver Load
logsource:
  product: windows
  category: driver_load
detection:
  staged_path:
    ImageLoaded|contains:
      - '\Users\'
      - '\Temp\'
      - '\ProgramData\'
      - '\AppData\'
  known_bad_signers:
    Signature|contains:
      - 'TPZ SOLUCOES DIGITAIS'
      - 'Hotta Studio'
      - 'Huawei Device Co'
  suspect_names:
    ImageLoaded|endswith:
      - '\wsftprm.sys'
      - '\GameDriverX64.sys'
      - '\UpdateCheckerX64.sys'
      - '\K7RKScan.sys'
      - '\HWAuidoOs2Ec.sys'
      - '\Havoc.sys'
  condition: staged_path or known_bad_signers or suspect_names
level: high
tags:
  - attack.t1562.001
  - attack.t1543.003

Treat names and signers as starting points, not gospel. Interlock renamed GameDriverX64.sys to UpdateCheckerX64.sys; DragonForce dropped Huawei’s driver as Havoc.sys. The durable indicators are the hash and the signing certificate thumbprint, plus the behavioral pair of “new kernel driver from a user-writable path” followed by “a protected security process dies.”

The structural fix: WDAC and CI.dll enforcement

Sysmon tells you it happened. WDAC stops it. The Microsoft Vulnerable Driver Blocklist (enforced through CI.dll code-integrity policy) is the right model executed with the wrong list. Two actions matter:

  1. Turn on the blocklist and keep it current. It is enabled by default on Windows 11 22H2+ and HVCI-enabled systems, but verify it on servers, which is where these intrusions land. It will not catch wsftprm.sys or the Huawei driver, but it catches the long tail of historical BYOVD.
  2. Move from denylist to allowlist. Deploy a custom WDAC policy that permits only the specific kernel drivers your environment actually needs, by signer and hash. This is the only control that would have stopped all five DragonForce drivers, including Abyss Worker, because it does not care whether a driver is “known bad.” It cares whether the driver is on your approved list, and none of these were.

The blocklist asks “is this driver known to be dangerous?” The allowlist asks “did I approve this driver?” Only the second question has a good answer for a Huawei audio driver weaponized three months before anyone documented it.


Key takeaways

  • The denylist model is structurally losing. wsftprm.sys has a public CVE and is still off Microsoft’s blocklist after two years; the Huawei driver was weaponized months before it was ever flagged. Negative lead time is the norm, not the exception.
  • PPL and ELAM protect against the wrong threat. They stop user-mode process kills. BYOVD enters from ring 0 where those checks do not apply, which is why a SYSTEM token still cannot kill Defender but a junk audio driver can.
  • “Fixed” does not mean safe. K7’s v23 patched the privilege-escalation CVE while leaving the kernel kill primitive intact for any caller already running as admin. Read the actual remediation, not the version bump.
  • Abyss Worker is the warning shot. When attackers build and sign their own kernel drivers, there is no vulnerable legitimate driver to blocklist. Custom malicious signed drivers are coming, and only allowlisting answers them.
  • Detect behavior, not filenames. Drivers get renamed (GameDriverX64 to UpdateCheckerX64, Huawei to Havoc.sys). Anchor on hash, signer thumbprint, load-from-writable-path, and the death of a protected security process.
  • WDAC allowlisting is the only complete control. Approve the handful of drivers you actually need and deny everything else by default. It is the single policy that would have stopped all five drivers in this chain.