As we push towards realizing the Cyber Mongol vision of teaming advanced automation with human defenders, it’s exciting to see pieces of our technology come to life from concept. Our most recent concept that went under the lightning rod and brought to life like Mary Shelley’s Frankenstein, was automated adversary behavior tagging. Essentially, any emerging tradecraft that hits our sensors and passes triage, will be automatically defined and categorized by the offensive behavior it represents – example process injection. While it seems common practice in the industry today to elude to the fact that your solution only uses the most advanced neural networks and ML models, that can be incredibly misleading as rule-based Regex is still a major component for effective detections. I’m not saying we don’t use ML within our pipeline, I’m just saying it works synergistically with the other logic to produce results. This blog post is a product of creating a small subset of Regex rule-based logic to detect process injection and I thought it might be helpful to share with the community.
We have been conducting extensive research in process injection, their variations and security solution API hook evasion over the last month. While none of these techniques are new, we are seeing an increased interest within the security community in regard to these vectors, based on our ingested signals. Additionally, these signals represent ways to execute malicious code on endpoints and evade AV/EDR solutions, emphasizing their significance in relation to the cyber Kill Chain. Process Injections, their variants and API hook evasion techniques like direct SYSTEM calls are heavily used by advanced actors such as nation states, criminal syndicates and hacktivists. I wanted to share a high-level overview of a few process injection variants and the common API calls they make. This post is not meant to be a deep technical dive into the internal workings of process injection, but to possibly aid in the comprehension of the technique to the unfamiliar, and enumerate some of the functions (common and uncommon) that we see being utilized for this tradecraft. Knowing which API calls are in vogue and may represent malicious activity, can definitely strengthen a threat hunting initiative.
So, what is a process injection? Process injections attempt to run arbitrary code in the memory space of targeted processes, executing malicious code that can evade endpoint protections. At the most basic level (and most well-known), a process injection works by getting access to a target process with the OpenProcess function, allocating memory within the process with VirtualAllocEx, writing malicious code to the allocated memory space with WriteProcessMemory, and then finally executing the malicious code with CreateRemoteThread. Alternatively, instead of opening an already existing process, an operator may choose to spawn a new process and inject malicious code into that process’s memory space with the CreateProcess function. These API’s are well known to be heavily exploited by injection techniques, and as such, security venders monitor (or hook) these functions so that they can inspect the code being executed. These security hooks (or inspections) happen in what’s called Userland (code that runs outside of the OS kernel) and can be bypassed with various techniques , . Additionally, there are different variations of process injection – Process Hollowing and Process Doppelganging are two variation examples.
Process Hollowing substitutes the memory space of a remote process with operator-controlled memory. This is achieved by creating a process in a suspended state with CreateProcess(CREATE_SUSPENDED), deallocating (hollowing) the suspended process’s memory space with NtUnmapViewOfSection, allocating memory for malicious use with VirtualAllocEx, write the malicious PE (portable executable) sections (data, text, etc…) to the newly allocated memory addresses with WriteProcessMemory, point the EAX register to the correct position in the text section by using SetThreadContext and finally, execution is achieved once the process is re-started with ResumeThread. This technique’s goal is to hide the execution of malicious code within a benign process.
Process Doppelganging looks to achieve execution of malicious code in the context of a legitimate process, use none of the suspicious API calls used in Process Hollowing (NtUnmapViewOfSection, VirtualProtectEx, SetThreadContext), to have the antivirus only scan clean files and to remain hidden from advanced forensic tools. A Doppelganger has 4 steps which are needed to successfully execute this type of injection. It needs to overwrite a legitimate executable with a malicious one (Transact), it needs to load a malicious executable (load), it needs to roll back to the original executable (rollback), and it needs to bring the Doppelganger to life (animate). The Transact step uses multiple Transactional NTFS functions to accomplish its goal. The CreateTransaction function will create an initial transaction, the CreateFileTransacted function is used to open a clean file (example svchost.exe) within the transaction and lastly, the clean file meant to evade endpoint protections will be overwritten with malicious code using the WriteFile function. Anything trying to inspect the file will not see the changes that have been made as they have occurred within the transaction, and the inspection is taking place outside of that transaction’s context. Additionally, after the rollback phase (which completes the NTFS transaction), the file will be reverted to its original clean state. The load phase will create a section object (a buffer of sorts) from the transacted file (svchost.exe) that points to a malicious executable with NtCreateSection. The Rollback step simply rolls back the changes made to the file system with RollbackTransaction but leaves the malicious section object that is pointing to the evil executable, intact. In the final step, the technique needs to create a process from this malicious section with NtCreateProcessEx(Process,..Section) which looks legitimate but runs malicious code inside of it, create a thread to the process using NtCreateThreadEx , create process parameters, copy parameters to the newly created process’s address space and start the Doppelganger’s execution using NtResumeThread.
Even if endpoint protections are bypassed with some of the techniques mentioned above, it still may be possible to enumerate suspicious API usage if you know what you are looking for – example ProcMon . Some EDR vendors also offer advanced hunting queries that may also be used to assist in locating malicious API calls.