On a recent red team scenario engagement, Horangi was tasked to evaluate the organisation’s susceptibility to ransomware attacks. On a subset of the employees’ laptops, CrowdStrike Falcon, was installed as a defence mechanism against malware, including ransomware. CrowdStrike Falcon is a solution that unifies next-generation antivirus, endpoint detection and response, cyber threat intelligence, managed threat hunting capabilities, and security hygiene. This technical blog posts documents the steps Horangi took to circumvent the detection and prevention capabilities for CrowdStrike, to allow execution of arbitrary code, on a laptop protected with CrowdStrike Falcon.
Before we begin, we will give a brief overview of how payloads sent by attackers work in general. Do note that the tactics, techniques, and procedures implemented by different groups of adversaries will vary to a certain extent.
One of the common attack vectors used by attackers is via email phishing link attacks, where a victim is persuaded to visit a malicious link, in which a malicious file will be downloaded. Once the victim runs the file, the malicious payload will be executed, where it first connects to a command and control server, which pushes subsequent commands, such as encryption of files within the operating system, to be executed by the downloaded file.
For this particular engagement, the primary challenge we faced was that CrowdStrike Falcon was able to detect our initial payload, which was already customized to circumvent the detection capabilities of Windows Defender, accurate as of September 2020. Upon detection, execution of the payload would be aborted by the CrowdStrike Falcon solution and the file would be quarantined.
This effectively stopped us from proceeding on with the rest of the engagement. Our first, and eventually also the biggest challenge, was to determine how our payload was being detected, and to bypass the detection mechanisms implemented by CrowdStrike Falcon.
Hypothesis And Bypass
Our first hypothesis was that the Windows API functions commonly used for loading of shellcode, such as “VirtualAlloc”, “VirtualProtect” and “CreateRemoteThread”, were used as a detection mechanism by the CrowdStrike Falcon solution. To understand how this detection could have been performed, it is necessary to understand on a high level the structure of payloads, or also known as executable files, and how these files function.
The following diagram depicts on a high level the structure of an executable file.
As depicted in the diagram above, the executable file format contains a number of headers (DOS, COFF, optional) followed by the various sections. Each section usually comes with it’s own memory protection; for example the “.text” section usually holds code and is mapped as “execute/readonly”, the “.data” section usually holds global variables and is mapped as “non-execute/read-write”.
The “Import Address Table” (IAT), located within the data directory (subsection of the optional headers), functions as a lookup table when the executable calls functions loaded within other DLL (shared library) files. The Windows API functions “VirtualAlloc”, “VirtualProtect” and “CreateRemoteThread” are all located within “kernel32.dll”, and the IAT is referenced to obtain their addresses for use. Thus the hypothesis was that when an attempt was made to lookup these functions via the IAT, this lookup would be detected and further process execution halted forcibly by CrowdStrike Falcon.
To test this hypothesis, we will require a means to call the “VirtualAlloc”, “VirtualProtect” and “CreateRemoteThread” Windows API functions without looking up IAT in its default state. One method this can be performed will be via the “LdrGetProcedureAddressForCaller” function, which is located in “ntdll.dll” files from version 6.2 onwards.
“LdrGetProcedureAddressForCaller” is called by “GetProcAddress”, which is used to retrieve the address of an exported function or variable from the specified DLL. As “LdrGetProcedureAddressForCaller” is located in “ntdll.dll”, we will first need to identify the base address of “ntdll.dll”. Once this has been identified, we can enumerate the address range to identify the exact offset for “LdrGetProcedureAddressForCaller”. Using the Thread Information Block (TIB) within the FS segment register, the base address of “ntdll.dll” can be found by extending the branch. In the diagram below, the TIB exists at the “[0x18]” position in the FS segment, and a structure named “Ldr” can be found at the “0x00c” position of the TIB.
Within the “Ldr” structure, an “InInitialzationOrderModuleList” exists, and the names of the DLL can be found.
We have now established that “ntdll.dll” is located within the “Ldr” structure, and from this we will be able to derive the address of “ntdll.dll”.
The following code depicts the flow of accessing the TIB and locating the “Ldr” structure.
Once we have located “ntdll.dll”, we will access the export table to search for the “LdrGetProcedureAddress” function.
The following code snippet shows how the address of “LdrGetProcedureAddress” was located.
Finally, we have established the base address of “ntdll.dll” and the “LdrGetProcedureAddress” function.
Using the “LdrGetProcedureAddress” function, we can now find the addresses for “VirtualAlloc”, “VirtualProtect” and “CreateRemoteThread”, without referencing the IAT. This is because it has been proven that we can find the base address of all dlls by being able to find the ntdll.dll base address. For the three API functions mentioned, we can now acquire the starting address of kernel32.dll instead of ntdll.dll. Assuming our earlier hypothesis holds, this should prevent the detection mechanism from triggering.
We then proceed to define the three variables, “VirtualAlloc”, “VirtualProtect” and “CreateRemoteThread”, and set up the handles to them. The parameters for each associated function can be found online at docs.microsoft.com at the point of writing.
We will also encrypt the variable names as a defence in depth measure.
The handles will then be assigned to the associated functions “VirtualAlloc”, “VirtualProtect” and “CreateRemoteThread”.
Upon completion, the modified executable was tested and this was found to successfully circumvent the CrowdStrike Falcon solution installed on the laptop. The following video shows the attempted execution of the initial payload, and the modified payload.
Watch Binary Execution Video here: https://youtu.be/iU5EdPLpEvQ
Fortunately after this blocker was solved, we proceeded on with the remainder of the red team scenario engagement without any major hitches.
Most security solutions (antivirus and endpoint detection and response) now utilize machine learning and are built on cloud infrastructure. This was thus presumed that CrowdStrike Falcon would also utilise the same logic.
When the initial hypothesis was formulated, it was extrapolated that Microsoft APIs detection were prioritized over behavioral analysis, as the payload was blocked during execution. With some knowledge of how executable files work and how Windows APIs are called, it was possible to bypass the detection mechanisms of CrowdStrike Falcon.