Malware source code investigation: BlackLotus - part 2
BlackLotus is a UEFI bootkit that targets Windows and is capable of evading security software, persisting once it has infected a system, bypassing Secure Boot on fully patched installations of Windows 11, and executing payloads with the highest level of privileges available in the operating system.
The source code for the BlackLotus UEFI bootkit has been published on GitHub on July, 12, 2023
.
We are continue our small research and today investigate the another modules of BlackLotus and highlights the main features.
Bot logic: Install
As we wrote before, a bot in this context refers to a device with the Agent installed.
First of all, we paid attention to files install.h
and install.c
:
The provided source code appears to be part of a malware installation process. This code is designed to install a bot or malicious payload onto a system and set up persistence. Let’s break down the logic step by step:
GenerateBotFileName(PDWORD Seed)
- This function generates a bot filename based on a given seed using a simple pseudo-random number generator algorithm. It multiplies the seed by a constant value (1664525) to calculate the new seed, which is then returned:
GetBotFileName(PDWORD Seed)
- Calls the GenerateBotFileName function to obtain the bot filename seed. Converts the seed to hexadecimal format and constructs the bot filename as a string. Returns the bot filename as a wide string (LPWSTR)
:
GetBotDirectory()
- Calls GetBotFileName
to get the bot filename. Constructs the bot directory path by concatenating the AppData directory path with the bot filename. Returns the bot directory path as a wide string (LPWSTR)
:
GetBotPath()
- Calls GetBotFileName
to get the bot filename. Calls GetBotDirectory
to get the bot directory path. Constructs the full bot executable path by concatenating the bot directory path, a backslash, the bot filename, and ".exe"
. Returns the full bot executable path as a wide string (LPWSTR)
:
IsSystemInfected()
- Calls GetBotPath
to get the bot executable path. Compares the obtained bot executable path with a global variable g_BotInstallPath
. Returns whether the system is infected (TRUE
) or not (FALSE
) based on the comparison:
InstallBot()
- Calls GetBotDirectory
to get the bot directory path. Calls GetBotPath
to get the full bot executable path.Converts DOS-style paths to NT-style paths using DosPathToNtPath
function. Creates the bot directory using FileCreateDirectory
. Copies the current process executable (malware) to the bot executable path. Sets a global variable g_BotInstallPath
to the bot executable path. Returns TRUE
if installation is successful, otherwise FALSE
:
Finally, UninstallBot()
- This function is incomplete and does not contain the logic for uninstallation.
Bot logic: Anti-debug
Next files, are antidebug.h
and antidebug.c
:
The provided source code seems to be focused on anti-debugging techniques, specifically checking whether the current process is being debugged. Also it’s obvious from the file name:
IsBeingDebuggedAlt()
- This function is intended to check whether the current process is being debugged by an external debugger. It retrieves the Process Environment Block (PEB) using the GetPeb()
function. It checks the BeingDebugged
field within the PEB to determine whether the process is being debugged.
IsBeingDebugged()
- This function is an entry point for checking if the current process is being debugged. It calls the IsBeingDebuggedAlt()
function to perform the check. If the IsBeingDebuggedAlt()
function returns TRUE
, indicating that the process is being debugged, the function returns an error code ERROR_UNSUCCESSFULL
. If the process is not being debugged, the function returns NO_ERROR
.
This code attempts to detect if the current process is being debugged using an alternative method by examining the BeingDebugged
field in the Process Environment Block (PEB). Anti-debugging techniques like this are used by malware authors to make it harder for security analysts and researchers to analyze or debug their malicious code.
Bot source code: Communication functionality
The source code of http.c
appears to be related to HTTP communication functionality for the BlackLotus bootkit. This code seems to handle HTTP
connections, sending POST
requests, and receiving data from a remote server. Let’s break down the code:
HttpConnect(LPSTR UserAgent, LPSTR Host, WORD Port, DWORD Flags)
- This function establishes an HTTP connection to a specified host. It uses InternetOpenW
API to create an internet session with the specified user agent. Then, it uses InternetConnectA
to establish a connection to the specified host on the specified port (port 80
in this case):
HttpRequest(HINTERNET Connect, BOOL bPost, LPSTR URI, LPSTR Referrer, PCHAR Data, DWORD Flags)
- This function sends an HTTP
request using the established connection. It uses HttpOpenRequestA
to create an HTTP
request to a specified URI. The method used can be POST
or GET
depending on the bPost
parameter. It uses HttpSendRequestA
to send the request with the specified headers and data:
HttpReceiveData(HINTERNET Request, PCHAR Buffer)
- This function receives data from the HTTP
response. It uses InternetReadFile
to read data from the response into a buffer. The function appends the received data to the provided buffer.
HttpPostRequest(PCHAR Host, PCHAR URI, PCHAR Data, PCHAR Response)
- This function is a higher-level wrapper for making an HTTP POST
request. It calls HttpConnect to establish an HTTP
connection to the specified host. Then, it calls HttpRequest to send a POST
request with the provided data. After sending the request, it calls HttpReceiveData
to read and store the response data:
Overall, this code appears to facilitate communication between the malware and a remote server through HTTP requests. It’s worth noting that malware often employs HTTP communication to send and receive commands, upload or download files, and exfiltrate data. Analyzing such code requires an understanding of HTTP communication mechanisms and malware analysis techniques.
Bot: Injection techniques
The next file is called inject.c
and it’s interesting. It’s started from function GetImageBase(LPVOID ProcessAddress)
which is used to retrieve the base address of a module within a process. It iterates backward in memory from the provided ProcessAddress
to find the base address of the module by locating the IMAGE_DOS_HEADER
and IMAGE_NT_HEADERS
structures. The function returns the base address of the module:
ProcessRelocation(PIMAGE_BASE_RELOCATION Relocation, DWORD ImageBase, DWORD Delta, DWORD Size)
- This function performs relocations for a loaded module within a process:
Function InjectData(HANDLE Process, LPVOID Data, DWORD Size)
allocates memory in a remote process and writes data into that memory:
As we can see, it uses “classic” combination VirtualAllocEx
, WriteProcessMemory
.
InjectCode(HANDLE Process, LPVOID Function)
- function injects a code block into a remote process. It obtains the base address and size of the function’s module, creates a file mapping, maps the remote process’ memory, and performs relocations:
It calculates the new address of the injected function and returns it.
InjectBot(DWORD ProcessId, LPTHREAD_START_ROUTINE Thread)
function injects a thread into a remote process, starting execution at the specified Thread function:
It opens the target process with specific access rights. It calls InjectCode
to inject the Thread function into the remote process. It creates a remote thread within the target process to execute the injected code.
Bot: Reporting
The provided source code appears to be related to the reporting functionality of the BlackLotus bootkit. This module is responsible for sending periodic reports to command and control (C2) servers about the status and information of the infected system:
ProcessServerResponse(CONST PCHAR Response)
function processes the response received from the C2 server after reporting:
ReportThread()
- This function is executed as a thread and handles the periodic reporting to C2 servers. It constructs a report data string using information such as infection type, bot GUID, OS, architecture, and username.
Function StartReportThread()
starts the reporting thread by creating a new thread that executes ReportThread
:
That’s all today. In the next part we will investigate the most interesting part: Bootkit logic.
We hope this post spreads awareness to the blue teamers of this interesting malware techniques, and adds a weapon to the red teamers arsenal.
By Cyber Threat Hunters from MSSPLab:
References
Malware source code investigation: BlackLotus - part 1
https://github.com/ldpreload/BlackLotus
https://malpedia.caad.fkie.fraunhofer.de/details/win.blacklotus
https://twitter.com/threatintel/status/1679906101838356480
https://twitter.com/TheCyberSecHub/status/1680044350820999168
Thanks for your time happy hacking and good bye!
All drawings and screenshots are MSSPLab’s