Enumerating Device Drivers: implementation and connection with APTs
The cyber security landscape is open enough to allow us acquire intelligence around threat groups and their tactics. However, when it comes to how exectly they did it - that is, the code they (might) have used to build their weapons - not much is shown. This post attempts to facilitate those who want to get an insight into how the high-level techniques are translated into code. It can also be useful to teams that simulate threat groups - the red teams, by showing them how they can do it on their own and expand their attack playbooks.
Hiding from each other
Recently, Checkpoint published a report [1] expanding on research about a threat group known as Nazar. The Shadow Brokers’ leak revealed that the Equation Group maintained a list of driver names that Checkpoint assesses are related to anti-virus products and security solutions. The purpose of this list is believed to serve evasion of detection by security solutions so the operations of this group are not disrupted. Likewise, red teams fighting against EDR (Event Detection and Response) and AV (Anti-Virus) solutions deployed in the environments they attack, they could add this evasion trick in their playbooks.
By fingerprinting the EDR or AV solution on a host red teams can choose to either stop their operation or - if they have the capability - execute custom payloads tailored to the identified security product. In this way, they can evade detections and at the same time make sure their payloads are executed.
The code
In this section you can find a C/C++ implementation of code capable to enumerate the device drivers of a host. We utilize the following APIs: EnumDeviceDrivers, GetDeviceDriverBaseNameA, GetDeviceDriverFileNameA. These APIs are part of the Psapi - the Process Status API - library.
Although around the web is suggested to use the following two in the project, we were able to compile the code without any issue:
#pragma comment(lib, "Psapi.lib")
#define PSAPI_VERSION 1
The first snippet implements the enumeration using the GetDeviceDriverFileNameA API. The output lists the names of the drivers (un-comment the printf line). The EnumDriversFileNameA() returns the number of identified drivers as well as an array that contains the driver names. You can call it in the following way:
VOID** lpImageBase = NULL;
DWORD cbNeeded = NULL;
EnumDriversFileNameA(lpImageBase, cbNeeded);
The implementation of EnumDriversFileNameA():
VOID EnumDriversFileNameA(VOID** lpImageBase, DWORD cbNeeded)
{
// get the size
EnumDeviceDrivers(NULL, NULL, &cbNeeded);
VOID** lpImageBase = new VOID*[cbNeeded / sizeof(LPVOID)];
// get the adddress of the module (ImageBase)
EnumDeviceDrivers(lpImageBase, cbNeeded, &cbNeeded);
for (unsigned int i = 0; i < cbNeeded / sizeof(LPVOID); i++)
{
CHAR* lpFilename = new CHAR[256];
if (GetDeviceDriverFileNameA(lpImageBase[i], lpFilename, 256))
{
//printf("%d: %s\n", i+1, lpFilename);
}
}
}
Here is the first few lines of the output from my host:
1: \SystemRoot\system32\ntoskrnl.exe
2: \SystemRoot\system32\hal.dll
3: \SystemRoot\system32\kdcom.dll
4: \SystemRoot\system32\mcupdate_GenuineIntel.dll
5: \SystemRoot\System32\drivers\msrpc.sys
In the next code snippet, the enumeration is achieved with a call to GetDeviceDriverBaseNameA API. The output lists the filenames of the drivers (un-comment the printf line). The EnumDriversBaseNameA() returns the number of identified drivers as well as an array that contains the driver names. You can call it in the following way:
VOID** lpImageBase = NULL;
DWORD cbNeeded = NULL;
EnumDriversBaseNameA(lpImageBase, cbNeeded);
The implementation of EnumDriversBaseNameA():
VOID EnumDriversBaseNameA(VOID** lpImageBase, DWORD cbNeeded)
{
// get the size
EnumDeviceDrivers(NULL, NULL, &cbNeeded);
VOID** lpImageBase = new VOID * [cbNeeded / sizeof(LPVOID)];
// get the adddress of the module (ImageBase)
EnumDeviceDrivers(lpImageBase, cbNeeded, &cbNeeded);
for (unsigned int i = 0; i < cbNeeded / sizeof(LPVOID); i++)
{
CHAR* lpFilename = new CHAR[256];
if (GetDeviceDriverBaseNameA(lpImageBase[i], lpFilename, 256))
{
//printf("%d: %s\n", i + 1, lpFilename);
}
}
}
Here is the first few lines of the output from my host:
1: ntoskrnl.exe
2: hal.dll
3: kdcom.dll
4: mcupdate_GenuineIntel.dll
Bench of Further
An additional step that can be added, is a function that will hash the strings/driver names and transmit them to the C2 server the red team is using.
References
[1] https://research.checkpoint.com/2020/nazar-spirits-of-the-past/
tags: #Windows API