Threat Hunting Process Injection With Jupyter Notebook and Sysmon

Process injection (T1055) refers to injecting code into other live processes. Adversaries use this technique to either evade detection based on process monitoring or to elevate privileges. In this blog, we’ll look at how Process injection works on Microsoft Windows.

Process injection was ranked 6th most used technique by adversaries in Red Canary’s 2021 Threat Detection Report.

Anyone that has used popular C2 frameworks such as Metasploit and Empire has most likely used process injection. Metasploit’s migrate and Empire’s psinject do classic process injection under the hood for injecting shellcode to another live process. Why classic? It’s because now there exist many process injection techniques such as process hollowing, PE injection, etc.

To be able to hunt simple shellcode injection, we first must understand how it is performed. There are many resources available on this topic. I highly recommend Secarma’s two-part blog series that nicely explains process injection with accompanying demo codes.

The generic shellcode injection process involves:

For invoking these aforementioned WinAPIs, we require three process-related access rights:

  • PROCESS_VM_OPERATION – Required to perform an operation on the address space of a process
  • PROCESS_VM_WRITE – Required to write to memory in a process
  • PROCESS_CREATE_THREAD – Required to create a thread in the process

For brevity, let’s name them our three amigos.

So, one way to hunt for shellcode injections is to detect the use of these access rights in a process access operation. So, how do we do this?

Many blue teamers might be familiar with Sysinternal’s Sysmon which nicely complements Windows’s native event logs. Sysmon provides Event ID 8 (Create Remote Thread) and Event ID 10 (Process Access) which just might do the job for us. The latter event provides the crucial access right used by the process that is accessing another process’s memory.

So, let’s hunt for migrate and psinject!



My testbed consists of a Windows 10 and a Kali Linux system. I use the community version of NxLog to write Windows and Sysmon events (in JSON) to a local file which I then import into my Jupyter notebook for analysis.

I use two popular open-source C2 frameworks for this test – Metasploit and Empire.

For anyone new to Sysmon, @SwiftOnSecurity[1] and @olafhartong[2] provide great starting baseline configurations. I will be using the former but keep in mind that Swift has disabled process access logging since it needs careful tuning to avoid log flooding. For this test, I have added rules that log all process access events of my C2 payloads.

The snippet of Sysmon config used during testing

Before diving ahead, I first need to convert the log file (that has JSON logs separated by newline) into a single JSON object that pandas can directly import.

perl -p -e 's/\r\n/,/' NxLog-Dump.txt > tmp.txt
perl -p -e 's/^/[/' tmp.txt > tmp2.txt
perl -p -e 's/\n/]/' tmp2.txt > NxLog-JSON.txt


Jupyter Notebook

To kick off our analysis, I will import the log file into a DataFrame. I use two lists – eid_8_columns and eid_10_columns that contain useful fields of EID 8 and EID 10 respectively.

Importing the log file

Analysis of EID 8 is simple. I first filter out EID 8 events to another DataFrame – eid_8_pdf. Then I will only keep necessary columns such as Hostname, SourceImage, TargetImage, etc.

Filtering EID 8 events and dropping unnecessary fields

I repeat the same for EID 10.

Filtering EID 10 events and dropping unnecessary fields

Let’s see the unique access masks in EID 10 events. To weed out the noise, I have ported PSGumshoe’s Get-SysmonAccessMask to python. I will use the parse_access_mask function to parse the access mask to its respective access rights and store that back to the DataFrame.

Parsing access masks

Next, I have defined a list – PERMISSIONS_OF_INTEREST – that consists of our three amigos access rights required for process injection. I will only keep those access masks that contain all these access rights. We can see this has reduced the unique access rights to two values – 0x1fffff and 0x147a.

Filtering out irrelevant access masks

Finally, I can correlate EID 8 and 10 for further validation. A simple inner-join operation between them on common fields like Hostname, SourceImage, TargetImage, etc. is all we require.

Correlating EID 8 and EID 10

This leaves us with our Metasploit payload msf.exe accessing svchost.exe with an access mask of 0x147a.

I did the same analysis for Empire and found that Empire’s psinject uses access mask of 0x1f3fff.


False Positives

I noticed a false-positive case for Empire’s psinject. The csharp_exe payload uses 0x1f3fff access mask for querying process information which is overkill. This means using ps command to enumerate running processes will generate false positives if the detection in question relies only on EID 10.



I will use Microsoft’s awesome open-source MSTICPY library for plotting the process tree created by process injection. I ran some reconnaissance commands after migrating to a svchost instance.

Process Tree visualization using MSTICPY


Contribute to the Community

I can contribute to the infosec community in the form of Sysmon and Sigma rule. For the former, in addition to access masks, I will look for any interesting artifacts in CallTrace.

Printing CallTrace column

The presence of the UNKNOWN module is an artifact shared by both Metasploit and Empire. Armed with this information, my Sysmon rule looks as follows.

<ProcessAccess onmatch="include">
  <!-- Process access pattern of Metasploit's migrate (0x147a) and Empire's psinject (0x1f3fff) -->
  <Rule groupRelation="and">
    <CallTrace condition="contains">UNKNOWN</CallTrace>
    <GrantedAccess name="T1055" condition="contains any">0x147a;0x1f3fff</GrantedAccess>

Looks like Olaf already had a Sysmon rule for possible DLL injections but is missing the 0x147a access mask. Olaf merged my small pull request (#126) for supporting this access mask.

The corresponding sigma rule is shown below.

title: Shellcode Injection
id: 250ae82f-736e-4844-a68b-0b5e8cc887da
status: experimental
description: Detects shellcode injection by Metasploit's migrate and Empire's psinject
author: Bhabesh Raj
date: 2022/03/11
    - attack.defense_evasion
    - attack.privilege_escalation
    - attack.t1055
    category: process_access
    product: windows
            - '0x147a'
            - '0x1f3fff'
        CallTrace|contains: 'UNKNOWN'
    condition: selection
    - Empire's csharp_exe payload uses 0x1f3fff for process enumeration as well
level: high

I have requested Florian to test for any false positives of this sigma rule (#2796). I will update this blog once there are any updates.



We can confidently say that adversaries are not forsaking this technique anytime soon. It is best for Defenders to detect this technique via a defense-in-depth approach. Always keep in mind that your deployed detections can be and will be bypassed by a determined adversary. Analyze your collected telemetries from your systems and assess your blindspots.

This is all I have for today. Au revoir and stay safe!


Guest Writer Bio

Bhabesh Raj Rai is a security researcher with a focus on detection engineering, cloud security, and threat hunting. He is currently working as a security analyst at Logpoint, a provider of SIEM, SOAR, and UEBA solutions with headquarters in Copenhagen, Denmark. Bhabesh joined Logpoint after finishing his B.E. degree in Electronics and Communication. He is a history buff with an interest in classical antiquity, Renaissance, and the Napoleonic era. Bhabesh’s personal blog can be reached at



Interested in threat hunting tools? Check out AC-Hunter

Active Countermeasures is passionate about providing quality, educational content for the Infosec and Threat Hunting community. We appreciate your feedback so we can keep providing the type of content the community wants to see. Please feel free to Email Us with your ideas!

Share this:
AC-Hunter Datasheet
AC-Hunter Personal Demo
What We’re up To