Safeguard
Application Security•

Detecting and Mitigating Reflective Code Loading (T1620)

Payloads that run straight from memory leave no file to scan. What MITRE documents about reflective loading on Windows, Linux and macOS, and how to hunt for it.

Safeguard Security Team
5 min read

Reflective Code Loading (T1620) is a Stealth technique on Windows, Linux and macOS. Adversaries load code into a process and run it from memory, rather than starting a thread or process that is backed by a file on disk.

The goal is concealment. A scanner that checks files, or a rule that watches for new processes, has little to look at.

How attackers use it

MITRE explains that reflective loading means allocating memory and then executing a payload directly inside the process. The payload may be a compiled binary, an anonymous file that exists only in RAM, or a small piece of fileless executable code such as position-independent shellcode. As one example, the Assembly.Load() method run from PowerShell can be abused to load raw code into the running process.

It is similar to Process Injection, with a difference: the code goes into the process's own memory, not another process's. That can hide the activity inside a legitimate looking process, avoid creating files, and let malware keep payloads encrypted or obfuscated until they run.

The MITRE page lists a long run of examples. Several illustrate the range:

  • During the 3CX Supply Chain Attack, AppleJeus used the open source project DAVESHELL to convert PE-COFF files to position-independent code and load a payload into memory.
  • During the SharePoint ToolShell Exploitation campaign, threat actors reflectively loaded payloads with System.Reflection.Assembly.Load.
  • APT38 configured payloads to load using the macOS NSCreateObjectFileImageFromMemory API, and loaded .NET DLLs reflectively to bypass disk based detection.
  • Cobalt Strike's execute-assembly command and SILENTTRINITY can run a .NET executable in the memory of a sacrificial process by loading the CLR.
  • Kimsuky used an Invoke-Mimikatz script to load a credential stealing DLL into memory.
  • Uroburos can load new modules directly into memory.

The common thread is that the loader is small and the real payload never touches disk in recognizable form.

How to detect it

MITRE provides one analytic per operating system family.

  • Windows: chains of memory allocation and execution within the same process, such as VirtualAlloc, then memcpy, then VirtualProtect, then CreateThread. Unlike process injection, there is no cross process memory write. The activity occurs within the process's own PID.
  • Linux: in-process mmap and mprotect followed by execve or execveat, where memory permissions change from writable to executable without a matching ELF file on disk.
  • Linux and macOS style dynamic loading: suspicious calls to dlopen, dlsym or mmap with read, write and execute flags in processes that do not usually load modules dynamically. Watch for anonymous memory regions being executed by user processes.

These are endpoint detection and response territory. Most teams will rely on an EDR product that watches memory behavior, and supplement it with logging:

  • PowerShell script block logging (Event ID 4104) records the code run through Assembly.Load.
  • Process creation logs with command lines (Security Event ID 4688 or Sysmon Event ID 1) show how the loader started.
  • On Linux, auditd rules on execveat and memory related calls can feed a SIEM, though volume needs tuning.

How to reduce the risk

MITRE states that this technique cannot easily be mitigated with preventive controls because it is based on abuse of system features. There is no single setting to flip.

General hardening still limits how a loader gets to run in the first place:

  1. Block or restrict script interpreters for users who do not need them, and use application control so only approved programs start.
  2. Turn on PowerShell logging and keep the logs somewhere central.
  3. Reduce the initial access paths: patch internet facing applications such as web servers, and filter attachments and downloads.
  4. Deploy endpoint detection that inspects memory behavior on servers as well as laptops.

These are general practices. The MITRE page does not list them as mitigations for this technique.

Check your own environment

Read-only example on Linux. Processes whose executable is an anonymous memory file show up with memfd in the link target. Run as root to see all processes:

ls -l /proc/*/exe 2>/dev/null | grep -i memfd

An empty result is the normal case. A hit is not proof of an attack, since some legitimate software uses memory backed files, but it is worth looking at.

On Windows, verify that script block logging is enabled and that event ID 4104 is reaching your log store. Check the same for process creation with command lines.

Then ask:

  • Which servers have EDR coverage that covers memory behavior?
  • Who may run PowerShell, and is script block logging on for them?
  • If a signed, trusted process started loading unknown code, would anything tell you?

Quick check

How does reflective loading differ from process injection?

Process injection writes code into another process. Reflective loading puts the code into the process's own memory, so there is no cross process write for a defender to see.

What does MITRE say about preventive mitigation?

That it cannot easily be mitigated with preventive controls, since it abuses system features.

Name one Windows indicator MITRE gives.

A chain of allocation and execution inside one process, for example VirtualAlloc, memcpy, VirtualProtect and CreateThread.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.