A Look into PlugX Kernel driver
In this blog I will talk about the Signed kernel driver that is used in a recent PlugX attack, the signed kernel drivers that were found on Virus Total are signed through Windows Hardware compatibility program (WHCP) and Sharp Brilliance Communication Technology Co., Ltd..
In summary the kernel driver act as user-mode loader which decrypt a 32-bit user-mode PE file and inject it inside Svchost.exe as child process for services.exe.
in this blog i focused my analysis on the sample “ab7ebc82930e69621d9bccb6698928f4a3719d29”
Driver Analysis:
the driver first registers a mini-filter callback functions and create a communication port with the name “\DtSfProtect{A71A0369-D7CA-4d4f-9EEE-01F8FE53C0D3}” to be able to communicate with the user-mode agent, the driver allows only for one user-agent to connect and accept connection from any process, and the port communication was not used by the user-client.
Figure 1: Register Mini-Filter Driver and create Port Communication.
Figure 2: Port Connection CallBack function.
Also, the registered filesystem callback pre-operation and post-operation does not do any monitor/protection and just return.
Figure 3: FileSystem Pre-Operation.
Then it creates Process Object Notifications for protection it monitors any attempt to open the user-mode process and forbids any attempt to access it from kernel drivers and user mode process, so the user-mode component can not be terminated either from user-mode and from kernel mode.
Figure 4: Register Process object Callback.
Figure 5: Pre-Process Callback Function.
After those initializations it creates a thread that will be responsible for resolving all the needed functions address and starting the main user-mode component.
It first tries to check if services.exe process started or not, it do that by using the NtQuerySystemInformation API to get information about the running process, and if services.exe still not running it will go in infinite loop until it starts before continue its operation.
Figure 6: Check if Services.exe process running.
Figure 7: Loop untill Services.exe start.
Then it reads configuration from the registry key “\Registry\Machine\SOFTWARE\DtSft\d1” and subkeys “M1” and the data is compared to the current system time, and based on the data in that registry “76 da 34 01” if the current time is after “Wednesday, March 9, 2033 8:07:29 PM” the driver will not continue operation and return and will not start the user-mode component
Figure 8: Read Attack time from registry.
Figure 9: check if current time before configured time.
Then the driver will read the decryption key from the registry subkeys “M3” under the key “\Registry\Machine\SOFTWARE\DtSft\d1*, the decryption key will be used to decrypt the PE module from the registry
Decryption_Key= “ec,a4,00,c4”
Figure 10: Read Decryption Key from Registry.
After that the driver will resolve the needed API functions from the windows kernel and from ntdll.dll and kernel32.dll the driver keeps the API information in the structure API_Info and it will do the following steps to fill in the structure fields:
- For ntdll.dll and kernel APIs
- Locate the KeServiceDescriptorTable (SSDT Table)
- Read ntdll.dll from hard disk.
- Manually Map ntdll.dll DLL to kernel memory.
- Search the export address table for the API it needs using the field “API_Info.API_Name” from the API struct.
- Extract the value that will be moved inside the EAX register before the sysenter instruction. It will be used as index in the SSDT table to resolve the Kernel API.
- Fill in the rest of the fields in the struct (kernel address, user-mode address, EAX value)
- For kernel32.dll APIs
- Read kernel32.dll from hard disk.
- Manually Map kernel32.dll DLL to kernel memory.
- Search the export address table for the API it needs using the field “API_Info.API_Name” from the API struct.
- Fill in the user-mode address field in the struct (the rest of the fields will be null values)
typedef Struct API_Info{
DWORD64 Kernel_API_Address; // will be null when used in resolving address in kernel32.dll
DWORD64 User_API_Address;
DWORD64 EAX_Value; //index of the function in the SSDT table, will be null in case kernel32.dll
char API_Name[80h];
}
Figure 11: Resolving API Address.
Locate the SSDT table:
To resolve the kernel API address the driver first locate the SSDT table, it does so by scanning the nt!ZwClose Function for the byte “0xE9” which is a JUMP instruction to “nt!KiServiceInternal”.
Figure 12: Locating the nt!KiServiceInternal function.
Figure 13: Locating the nt!KiServiceInternal function.
After locating “nt!KiServiceInternal” code the driver will search in it for the pattern “0x8D4C” which is “lea r11,[nt!KiSystemServiceStart]” to locate the address of the function “nt!KiSystemServiceStart”
Figure 14: Locating the nt!KiSystemServiceStart function.
Then search for the pattern “0x4c8d15” to locate the address of “lea r10,[nt!KeServiceDescriptorTable]” and from there it will have the address of KeServiceDescriptorTable to continue the operation to resolve Kernel API address.
Figure 15: Locating the KeServiceDescriptorTable Address.
After locating the SSDT table it will read the DLLs from disk and map it to memory to fill in the API_Info structure.
Figure 16: Reading and mapping ntdll.dll to kernel memory.
Figure 17: Filling the API_Info structure.
also the driver will resolve the functions address twice once to get the kernel API, and the second time to get the user-mode API from ntdll.dll and kernel32.dll, and the reason for that is because the services.exe process might not be fully initialized and the ntdll.dll and kernel32.dll DLLs might not be fully loaded yet.
Figure 18: Get Kernel API Address.
Figure 19: Get User API Address.
Then the driver will read the User-Mode component from registry subkeys “M2” under registry key “\Registry\Machine\SOFTWARE\DtSft\d1” and then XOR decrypt it.
Figure 20: Read User-mode component and decrypt it.
Then it confirms that the user-mode component is a 32-bit file and if not it will not start it, after that it will allocate memory and copy a ShellCode function which will be injected in services.exe to start the main user-component after that it will do a sequence of NtWriteVirtualMemory calls to write the ShellCode, path to Svchost.exe file and the User-mode component to the services.exe process.
Figure 21: Allocate Memory for the shellcode.
Figure 22: write the shellcode and svchost path to services.exe process.
Figure 23: change permission of memory to be able to write to it.
And to make the ShellCode gets executed it will hook the Ntdll!NtClose to make it jump to the ShellCode after the ShellCode gets execute it will restore the Ntdll!NtClose Function to its original state and make the process continue operation and normal
Figure 24: Hook Ntdll!NtClose to make the shellcode execute.
Figure 25: Hook Ntdll!NtClose to make the shellcode execute.
User-Mode Component
User-mode component is a simple code that injects another 32-bit PE module in svchost.exe process and monitors it if it gets terminated it will start it again.
Figure 26: User-Mode Component.
Yare Rule:
rule PlugX{
meta:
author = "Mahmoud Zohdy"
date_created = "2024-01-20"
description = "Kernel driver used in recent PlugX attack"
strings:
$string0 = "\\SystemRoot\\system32\\drivers\\DtSfProtect" wide ascii
$string1 = "\\DtSfProtect{A71A0369-D7CA-4d4f-9EEE-01F8FE53C0D3}" wide ascii
condition:
uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550 and any of ( $string* )
}
IOC:
SHA-1 Hash | Signer | Signing Date | Program Name |
---|---|---|---|
4307c1e76e66fb09e52c44b83f12374c320cea0d | Microsoft Windows Hardware Compatibility Publisher | 2023-03-23 | 淮南锋川网络科技有限责任公司 (Huainan Fengchuan Network Technology Co., Ltd.) |
b421c7fb5a041b9225e96f9c82b418b5637dd763 | Sharp Brilliance Communication Technology Co., Ltd. | 2023-08-27 | |
43e00adbbc09e4b65f09e81e5bd2b716579a6a61 | Microsoft Windows Hardware Compatibility Publisher | 2022-09-14 | 大连纵梦网络科技有限公司 (Dalian Zongmeng Network Technology Co., Ltd.) |
ab7ebc82930e69621d9bccb6698928f4a3719d29 | Microsoft Windows Hardware Compatibility Publisher | 2022-09-14 | 大连纵梦网络科技有限公司 (Dalian Zongmeng Network Technology Co., Ltd.) |
7e836dadc2e149a0b758c7e22c989cbfcce18684 | Microsoft Windows Hardware Compatibility Publisher | 2022-08-17 | 大连纵梦网络科技有限公司 (Dalian Zongmeng Network Technology Co., Ltd.) |
0dd72b3b0b4e9f419d62a4cc7fa0a7d161468a5e | Microsoft Windows Hardware Compatibility Publisher | 2023-03-22 | 淮南锋川网络科技有限责任公司 (Huainan Fengchuan Network Technology Co., Ltd.) |
097e32d2d6f27a643281bf98875d15974b1f6d85 | N/A | N/A | |
2084dd19a5403a4245f8bad30b55681d373ef638 | N/A | N/A | |
c4d4489ee16ee537661760879bd36e0d4ab35d61 | N/A | N/A | |
c98b3ce984b81086cea7b406eb3857fd6e724bc8 | N/A | N/A | |
7079c000d9d25c02d89f0bae5abfe54136daf912 | N/A | N/A | |
c4aa3e66331b96b81bd8758e5abcba121a398886 | Sharp Brilliance Communication Technology Co., Ltd. | 2023-08-23 | |
9883593910917239fc8ff8399e133c8c73b214bc | N/A | N/A | |
501114B39A3A6FB40FB5067E3711DC9389F5A802 | N/A | N/A |