Detecting process injection

Recently I’ve been working on some detection rules to identify process injection attempts.

Process injection (T1055) is a key technique used by adversaries to evade defenses and execute arbitrary code. New methods are regularly found, and there are numerous ATT&CK sub-techniques to categorise the activity.

With that in mind, this blog is not intended to be a comprehensive guide for detecting every type of process injection. Rather, it’s a mechanism to document my understanding of the technique and share my working process in developing these detections, while also offering a few solutions for detecting nefarious activity.

How do processes work?

To understand process injection, you must first have at least a rudimentary understanding of how processes are executed and more broadly how operating systems allocate resources to do this.

A process is a running instance of a program. When a process is running, an operating system manages its memory space, handles and thread execution.

  • Memory is typically allocated in chunks and stores things like the executable code, variables, libraries and stacks.
  • Handles are references that processes use to interact with resources like files, registry keys or other processes.
  • Threads are the smallest unit of execution within a process and are what actually executes the instructions, using the allocated memory to store variables, function calls, or downloaded payloads. While a process can contain just a single thread, modern software often uses multiple threads running concurrently (in the same memory space) to improve performance and responsiveness.

Processes request resources via system calls, such as CreateFile or VirtualAlloc, which we will touch on later. These system calls act as a gateway between user-mode applications (lower privilege applications which cannot access system resources directly), and the Windows kernel (which has unrestricted access to hardware resources).

What is process injection?

In the context of process injection, the normal behaviour outlined above is abused in a sequence generally resembling the following.

  • Gain access to the memory space of a legitimate process.
  • Allocate memory in that process.
  • Write malicious code into the allocated memory.
  • Execute it.

The following diagram illustrates this. Process injection diagram

Image source: Alshehri, 2021.

Detecting process injection

Any half decent EDR tool should detect (and ideally block) common process injection techniques. However, as per the principles of defence in depth, there’s no harm in having some redundant SIEM-based detection rules, particularly for important techniques like process injection.

There’s also the chance that an EDR could be bypassed, disabled or malfunctioning. Perhaps there is shadow IT that simply doesn’t have an EDR agent on it, but does have logging available that’s being sent to the SIEM.

What was observed?

A threat actor was observed to attempt process injection via PowerShell using .NET interops. PowerShell script block logging was enabled, and the entire process injection attempt was captured within a single log.

There were very limited attempts at obfuscation, indicating this may have been standard commodity malware or some other kind of relatively low skill threat actor. Thankfully for the blue team, this makes it relatively easy to detect.

The script block started with the Add-Type PowerShell cmdlet, used to inject custom C# code into the PowerShell session. This was followed by a series of [DllImport] declarations (.NET interop calls), which enabled PowerShell to invoke unmanaged Windows API functions directly from native DLLs.

At a high level, the script block looked something similar to this.

Add-Type -parameter1 'value1' -parameter2 'value2' -parameter3 -parameter4
[DllImport("kernel32.dll", EntryPoint="OpenProcess")]
[DllImport("kernel32.dll", EntryPoint="VirtualAllocEx")]
[DllImport("kernel32.dll", EntryPoint="WriteProcessMemory")]
[DllImport("kernel32.dll", EntryPoint="CreateRemoteThread")]
[DllImport("ntdll.dll", EntryPoint="RtlCreateUserThread")]
[DllImport("ntdll.dll", EntryPoint="RtlAdjustPrivilege")]
[DllImport("urlmon.dll", EntryPoint="URLDownloadToFile")]

Lets unpack this a little bit more.

kernel32.dll provides core Windows API functions for process and memory management. It acts as a bridge between user-mode applications and the Windows kernel.

  • OpenProcess opens a handle to a target process, allowing further operations on it.
  • VirtualAllocEx allocates memory within the address space of the target process.
  • WriteProcessMemory writes data into the allocated memory in the target process.
  • CreateRemoteThread creates a new thread in the target process to execute the injected code.

ntdll.dll contains low-level Windows Native API functions, providing more direct access to kernel services. These are possibly redundant or fallback attempts to execute the malicious code in case CreateRemoteThread fails.

  • RtlCreateUserThread is an alternative to CreateRemoteThread and creates a thread in the target process (potentially to bypass some security mechanisms).
  • RtlAdjustPrivilege adjusts the privileges of the current process or thread (potentially enabling elevated permissions required for injection).

urlmon.dll is used for URL-related operations, including downloading files from the internet.

  • URLDownloadToFile downloads a file from a specified URL and saves it locally.
  • In this case, it was fetching a GitHub raw.githubusercontent.com URL for an obscure, forked variant of a popular Tox chat client. Tox is a peer-to-peer chat client that is popular among ransomware affiliates and groups for setting up a line of communications to victims.

This pattern is consistent with process injection techniques used in fileless malware and is designed to evade traditional file-based detections.

Detection 1: Process injection via PowerShell API calls

The following Sigma rule detects the full sequence above. A true positive should be an open and shut example of an attempt at process injection, although I’d recommend some environment-specific baselining if this detection rule is implemented.

title: Process injection via PowerShell API calls
id: ad80a91b-0a5a-49e8-99a6-adb085eca2eb
description: Detects suspicious PowerShell script blocks where a full process injection sequence is observed.
references:
  - https://rigelnoble.com/blogs/cybersecurity/detecting_process_injection/
tags:
  - attack.defense_evasion
  - attack.privilege_escalation
  - attack.t1055
author: Rigel Noble
date: 2025/05/25
modified: 2025/07/07
logsource:
  product: windows
  category: ps_script
  definition: PowerShell script block logging must be enabled
detection:
  selection:
    EventID: 4104
    ScriptBlockText|contains|all:
      - OpenProcess
      - VirtualAllocEx
      - WriteProcessMemory
    ScriptBlockText|contains:
      - CreateRemoteThread
      - RtlCreateUserThread
  condition: selection
level: high

Detection 2: Potential attempted process injection via PowerShell API calls

In addition to detecting a full sequence of process injection, I also wanted to provide a layer of redundancy in the event that for some reason, not all API calls were present in a singular script block. I am unsure how likely this is to actually occur, but the first detection and execution sequence seemed somewhat primitive - it just seemed too easy to call it a day there.

This detection looks for situations where memory has been allocated and written to, but not necessarily executed. Perhaps the injection attempt failed, or is being staged for later.

title: Potential attempted process injection via PowerShell API calls
id: c1b7a949-31af-4f95-95f2-627002d6e29b
description: Detects suspicious PowerShell script blocks where a partial process injection sequence is observed, potentially indicating a failed attempt or staging.
references:
  - https://rigelnoble.com/blogs/cybersecurity/detecting_process_injection/
tags:
  - attack.defense_evasion
  - attack.privilege_escalation
  - attack.t1055
author: Rigel Noble
date: 2025/05/25
modified: 2025/07/07
logsource:
  product: windows
  category: ps_script
  definition: PowerShell script block logging must be enabled
detection:
  selection:
    EventID: 4104
    ScriptBlockText|contains|all:
      - VirtualAllocEx
      - WriteProcessMemory
  condition: selection
level: medium
falsepositives:
  - Administrative scripting or debugging activity.
  - Custom tooling used by IT or development teams.

Summary

Process injection is a key technique to understand and defend against as it’s used by adversaries to bypass security controls and execute malicious code. While modern EDR tools offer strong protection, having layered SIEM-based detections ensures broader visibility and resilience.

The two rules above identify a classic method of process injection and have the potential to be useful in identifying malicious activity originating from commodity malware.

If you have any comments, feel free to reach out via LinkedIn or email.

 

rigelnoble.com

Cyber security and detection engineering. Technology, privacy and more.


A key technique used to evade defences and achieve arbitrary code execution.

May 25 2025

tags: cyber security, detection engineering