SakDriver: Reversing a Kernel Driver Rootkit

Cobalt Strike is a paid penetration testing product that allows an attacker to deploy an agent named 'Beacon' on the victim machine. Beacon includes a lot of functionality to the attacker, including, but not limited to command execution, key logging, file transfer, SOCKS proxying, privilege escalation, mimikatz, port scanning and lateral movement. Beacon is in-memory/file-less, in that it consists of stageless or multi-stage shellcode that once loaded by exploiting a vulnerability or executing a shellcode loader, will reflectively load itself into the memory of a process without touching the disk. It supports C2 and staging over HTTP, HTTPS, DNS, SMB named pipes as well as forward and reverse TCP.
The Beacon implant has become popular amongst targeted attackers and criminal users as it is well written, stable, and highly customizable.
Technical Analysis
Upon having the cobalt strike beacon sample I ran basic checks and tools to gather initial information about the agent. It uncovered that what i am looking at is not a user land beacon but a kernel rootkit loading as a driver.

It actively hooks and patches the ETW(Event Tracing for Windows) structure in kernel which blinds the defense system before it even logs an event. It also hides and unhides process via DKOM(Direct Kernel Object Manipulation), hides the C2 communication port NSI_HIDE_PORT and unblocks IP WFP_UNBLOCK_IP using the WFP(Windows Filtering Platform) which is the core architecture behind the windows firewall.

It also has to do something with the time stamp counter in hypervisor likely when the rootkit is in a sandbox environment. The driver is named as SakDriver with a PDB path pointing to E:\tools\oldfilelaoda\x64\Release\CrackerDrv.pdb .

After loading it in IDA, in DriverEntry function which is the main entry function for a driver, it uses the DriverObject, created by I/O manager for each driver that has been installed and loaded which contains storage for entry points of driver routines such as DriverEntry, Unload, MajorFunction etc. and also information like DriverName .


Now further in function, it use the member DriverSection (pointer to _KLDR_DATA_TABLE_ENTRY in kernel mode containing information about the loaded module) and sets the unload routine to DriverUnload after which it loads the DLL name from offset 0x48 .


Now if the driver is already present in system it then iterates through the FullDllName->Buffer to find string \SystemRoot\System32\Drivers using custom loop rather than using kernel API’s and search for driver’s name.

Then it calls a function which takes the driver name and opens a handle to the \\Registry\\Machine\\System\\CurrentControlSet\\Services (store information about each service), allocating a Non-Paged memory region with NX(No-Execute) of 0x218 bytes with tag CvrD and then iterates over each subkey under Services and fills the PoolWithTag region with KEY_BASIC_INFORMATION .

It then opens handle for the intended driver and query its ImagePath (points to location of file) and stores path into a global state then it registers the driver as a minifilter driver (type of driver used to monitor, intercept and modify file system I/O requests) and sets IRP_MJ_DIRECTORY_CONTROL as MajorFunction to have control over directory I/O request to evade scans by AV softwares.


It then fingerprints OS information of the victimized user and prepares a POST request to send this info to a C2 server(43.160.247.24) at /api/event.php endpoint with a custom user-agent SakDriver 1.0.


In case, if it’s a fresh installation then it installs the driver into system at one of the paths\\Device\\HarddiskVolume%lu\\Windows\\System32\\drivers\\DRIVER_NAME , \\??\\C:\\Windows\\System32\\drivers\\DRIVER_NAME with naming sak_XXX.sys and the XXX is a random seed containing XOR’s of a memory region, frequency performance counter, PID and TID. Also it registers the service as msXXXX in registry at \\Registry\\Machine\\System\\CurrentControlSet\\Services\\SERVICE_NAME as Microsoft Player Service display name and Provides support for %04X operations as description to camouflage itself as a legitimate windows service and in case of error registers itself as SakDriver ( Not OPSec friendly :) ) and if everything goes right it sends install_success mark to C2 and deletes the dropper.


Now it queries system to get the memory address of ntoskrnl.exe , and then build its own Import Address Table(IAT) in memory to use kernel API’s by deobfuscating API names and resolving their address during runtime using sub_14000E34C which will take address of ntoskrnl and decrypted API name to manually parse PE header to find the Export Directory which will be used to get address of those API’s , it will also help to bypass EDR’s static scanning.

It then call CmRegisterCallbackEx to register a RegistryCallback routine, which is called every time a thread performs an operation on the registry. The registry callback function named Function performs function like If an AV software tries to modify the registry keys of rootkit, it catches that modification and throws STATUS_ACCESS_DENIED which will abort registry modification.

Here’s the interesting part, now what author did is, it is using its own registry callback to receive C2 commands from a user-mode agent. So if a user-mode agent writes the command in benign registry using user-land API’s it completely evades EDR scans, although for every write driver checks for a specific magic bytes 0x2625B7146B and extracts commands from it.
22661
Arbitrary Read/Write Process memory (attaches kernel thread to address space of target process, locks and map the memory to kernel then reads or writes into process memory)
13592
Performs KVA Shadowing bypass to inject and steal information from user-land process.
13129
Allocates memory in the victim process to perform Process Injection
13128
Free the allocated memory after thread finish execution
12937
Copies data between 2 different process i.e. rootkit and user-land
10246
Attach to user-land process and parse the dll to locate the API names address ex. VirtualAlloc.
9072
Forcefully deletes the protected or executing files
8784
Kernel to User-mode DLL injection via Kernel APC queuing (parses PEB to locate kernel32.dll and finds address of LoadLibrary by parsing dll)
8306
Defensive measure by setting a callback whenever system does OpenProcess or OpenThread and intercept handle, threads opened for rootkit and strips out permissions like PROCESS_TERMINATE, PROCESS_QUERY_INFORMATION and also it bypass access rights for low privilege process to higher ones(setting access to PROCESS_ALL_ACCESS) specifically for v8(still need to find out).
16964
Query memory for basic information about the region
22323
Read/Write to any physical memory address by read/writing to its virtual pointer which is obtained by building a custom page table with RW permissions and injecting that into empty space in PML4. Also copies data directly from physical address
13831
Resolves PID by process name
13664
Spoofs hardware to send packets in form of mouse or PS2 keyboard/mouse data and execute the callback with forged input.
\\Driver\\mouhid and \\Driver\\i8042prt
37720
Reads the memory of a process by querying PFN database and mapping addresses
39029
Changes the protection of page
38536
Returns process section base address from EPROCESS structure
38435
Walks PEB of a process to loop through loaded DLLs and match it with attacker supplied name eg. kernel32.dll and returns base address of DLL
37973
Extracts driver name
29557
29556
Spoofs hardware to send packets in form of keyboard or PS2 keyboard/mouse data and execute the callback. \\Driver\\kbdhid and \\Driver\\i8042prt
25736
Perform Hardware spoofing for Disk, Nvidia GPU, SMBIOS
25897
Executes a user thread with attackers DLL/ malicious function

It then applies a stealth layer on the registry to avoid any read or write query on the drivers service by AV and EDRs.
These are the things it perform as a callback function, and now coming back to Main entry it now perform Network filtering by blocking the requests made by security solutions to C2 IP and port. It also hides some ports like 6891, 12341, 12342, 12343, 6543, 7543, 9199 by hooking NSI driver.

Then it tries to create a WFP device object so that it can observe the network stack and adds just-do-it.icu and 91.99.165.207 to the blocking table.
IOCs
Domain and IP:
just-do-it.icu, 91.99.165.207, 43.160.247.24and these domain and IPs haven’t been flagged by VirusTotal yet and infra is still working.Ports: 6891, 12341, 12342, 12343, 6543, 7543, 9199



Endpoint:
/api/event.phpHash:
4e95aba17c1a423cda5cc9f9f04f7cf8db17e294eb31ed1aa85063601b82fe8dDriver name: SakDriver, sak_xxx.sys, ms_xxxx
Yara Rule
Last updated
Was this helpful?