Kernel Blackout

Blue pill: stay in user mode, trust the documented APIs, and never question what lies beneath. Red pill: drop into kernel mode and see how deep the rabbit hole goes. - by h4sh3m00

The following post by 0xb0b is licensed under CC BY 4.0arrow-up-right


circle-exclamation

Scenario

In Kernel Blackout we are tasked to build a rootkit to hide an implant process. We need to confirm the process is hidden to get the flag. So we need to write a Windows Driver resulting in a .sys file that hides a specific process from the system's process list

In the scenario, we have a development machine called Malware Dev and a target machine called Target.

>Access instance data: http://10.200.150.10 @ Target http://10.200.150.20 @ MalwareDev

>Build a rootkit to hide the implant process on MalwareDev with the x64 Visual Studio console.

>Authenticate on MalwareDev via RDP using: User:Administrator / Password:KernelKernel2323

>Once ready, upload the rookit and deploy it to the Target machine.

>Confirm the process is hidden and get the flag!

Note that killing the implant process will not give you the flag.

Summary

chevron-rightSummaryhashtag

In Kernel Blackout we develop a Windows kernel-mode rootkit to conceal a running implant process. We analyze kernel memory structures via WinDbg, identifying the key _EPROCESS offsets for UniqueProcessId, ActiveProcessLinks, and ImageFileName. Using Direct Kernel Object Manipulation (DKOM), the driver iterates through the active process list, locates implant.exe, and unlinks it from the ActiveProcessLinks chain, rendering it invisible to standard enumeration tools like Task Manager. The driver is compiled via the Visual Studio 2022 Native Tools Command Prompt into blackout.sys, then deployed to the Target system. Upon loading the driver, the implant process disappears from process listings, confirming successful kernel-level concealment and yielding the final flag

Analysis

We open the web interface of the development machine and see an interface for OPERATION: KERNEL BLACKOUT. It's the same like on the target.

Among others we see a warning: CAUTION: Drivers may trigger BSOD

This warning means that the drivers we are about to use operate in kernel mode and can cause a Blue Screen of Death if they malfunction. Such drivers have unrestricted access to system memory and hardware, so even minor errors can crash the operating system. It is safest to proceed only in a controlled environment. So we will use the Malware Dev machine and not our own VMs / Hosts.

If we scroll down we find the implant.exe with the PID 4772 in this case.

Furthermore we have a windbg_console:

Development

According to Gemini to hide a process in Windows one method can be is to unhook it from the ActiveProcessLinks doubly-linked list. Windows tracks running processes using a circular linked list of _EPROCESS structures:

🛠️ The Strategy: DKOM (Direct Kernel Object Manipulation)

To hide a process in Windows, the most common method is to unhook it from the ActiveProcessLinks doubly-linked list. Windows tracks running processes using a circular linked list of _EPROCESS structures.

If you "snip" a process out of this list, tools like Task Manager or tasklist (which rely on traversing this list) won't see it, even though the process continues to run.

Fortunately from the WinDbg output we can identify the following offsets for UniqueProcessId +0x2e0, ActiveProcessLinks +0x2e8 and ImageFileName +0x450.

These offsets are required later to correctly locate and manipulate specific fields inside the _EPROCESS structure, allowing to identify the target process and unlink it from the kernel's active process list.

The UniqueProcessId stores the process identifier (PID) used by the kernel to uniquely identify a running process. It allows the operating system to associate threads, handles, and resources with the correct process and is commonly used to confirm that the correct _EPROCESS structure has been located.

The ActiveProcessLinks is a pair of forward and backward pointers that link the process into the kernel's global list of active processes. The operating system and userland tools traverse this list to enumerate running processes, so removing an entry causes the process to disappear from standard listings.

The ImageFileName contains the executable name of the process as stored in the kernel. It allows the driver to identify the target process by name before manipulating its kernel links.

Key Offsets from your WinDbg Output

Your dt nt!_eprocess dump provides the exact "map" for this specific Windows version (Windows 10 17763):

  1. UniqueProcessId: +0x2e0 (Used to find the correct process).

  2. ActiveProcessLinks: +0x2e8 (The _LIST_ENTRY we need to manipulate).

  3. ImageFileName: +0x450 (Useful for finding the process by name).

Offset Definitions

First we define the constant memory offsets that act as a map for the _EPROCESS structure in kernel memory. The OFFSET_ACTIVE_LINKS points to the doubly-linked list that the OS uses to track all running processes, while OFFSET_IMAGE_NAME points to the string containing the process's executable name.

The HideProcess Logic

This function performs Direct Kernel Object Manipulation (DKOM) by iterating through the circular list of active processes to find a specific name match. Once implant.exe is found, the code "snips" it out of the chain by connecting its predecessor directly to its successor, effectively making it invisible to system monitoring tools. To prevent a system crash, the target's own links are redirected to point back to itself rather than being left empty.

DriverEntry Execution

Here is the entry point of the kernel module, similar to a main() function in standard C programs. It logs a message to the debug console and immediately calls the HideProcess function with the target filename. Finally, it returns STATUS_SUCCESS, which allows the driver to stay resident in memory so the process remains hidden.

Build

On the machine we have besides Visual Studio Code also the Native Tools Command Prompt for Visual Studio Code on the Desktop. We make use native command prompt to compile the source.

We spawn a Native Tools Command Prompt for VS 2022 to be in an environment where the Visual Studio x64 toolchain is initialized.

We prepare a batchfile - here kbuild.bat - to compile the source with the following content:

call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" This sets up the Visual Studio 2022 64-bit build environment so cl.exe and link.exe work correctly. It is required to ensure the compiler, linker, and libraries match the x64 kernel target.

cl.exe /D_AMD64_ /I"...\km" /c hide.c This compiles the driver source file into an object file without linking. The _AMD64_ macro and kernel-mode include path are required so Windows kernel headers compile correctly for x64.

link.exe /DRIVER /RELEASE /SUBSYSTEM:NATIVE /ENTRY:DriverEntry hide.obj ntoskrnl.lib /LIBPATH:"...\km\x64" /OUT:blackout.sys This links the object file into a kernel-mode driver. The flags tell the linker it is a native x64 Windows driver, use DriverEntry as the entry point, and link against the Windows kernel.

On the Desktop we have the hide.c containing the source and the kbuild.bat.

We run the batch file to compile the source and end up with the blackout.sys file.

Next, we transfer the blackout.sys with the previous set up shared folder in Remmina.

Deploy and Execute

We upload the blackout.sys to the target at http://10.200.150.10. After a short duration we reload the page and get the flag.

Last updated

Was this helpful?