Malware Analysis

What Is Fileless Malware?

HuntRule Team · · 7 min read

Smoke holding the rough shape of a figure with no solid body, lit from one side in a dark room
On this page

The name is wrong

Almost nothing is entirely fileless. The DoublePulsar backdoor that EternalBlue installs ends up in kernel memory with no file written, and that is the rare pure case. Everything else people call fileless still touches a file somewhere. The accurate claim is narrower: the malicious code never lands on disk as a file the scanner inspects. Microsoft splits this into three types, and only Type I performs no file activity at all.

That distinction matters because it tells you where to point telemetry. A team that only inspects files is not under-tooled. It is looking in the one place the code deliberately avoids.

Where the code actually lives

Memory-only injected code is the cleanest version. A loader allocates memory in another process, writes a payload, and starts a thread there (T1055). The payload is position-independent shellcode or a PE reflectively loaded into the process (T1620), so no module path ever appears on disk. Cobalt Strike beacons and Meterpreter both live here.

Registry-stored payloads keep the code in a value instead of a file. This is fileless storage (T1027.011): the Windows Registry, event logs, and the WMI repository are all non-file containers. Kovter is the textbook case. It writes its script into a registry value, sets a shell open verb for a junk file extension, and lets mshta.exe pull and run the script from the registry when the verb fires. The files Kovter drops contain junk. The payload is in the hive.

WMI event subscriptions persist with no payload file and no autorun key. The attacker registers an EventFilter, an EventConsumer (usually a CommandLineEventConsumer or ActiveScriptEventConsumer), and a FilterToConsumerBinding (T1546.003). When the filter condition fires, WMI runs the consumer's command. Poshspy stored a PowerShell command in the WMI repository and ran it on a schedule this way.

Scheduled tasks carry encoded commands in the task definition. A task action of powershell.exe -enc <base64> puts the whole payload in the task XML (T1053.005). The task is a file under C:\Windows\System32\Tasks\, but the interesting content is a blob most file scanners never decode.

Script interpreters loading remote content are the loudest and most common. powershell.exe, wscript.exe, and mshta.exe fetch a script over HTTP and run it in memory (T1059.001), so the second stage never hits disk.

Persistence is where the disk almost always gets touched. Memory-only code dies at reboot. To survive, most operators plant a run key, a task, or a WMI binding, and those writes are the detection opportunity.

HKCU\Software\Microsoft\Windows\CurrentVersion\Run
HKLM\Software\Microsoft\Windows\CurrentVersion\Run
HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce

That is T1547.001, and it lands in the registry every time.

A single wisp of smoke curling out of a sealed metal drawer left slightly open in the dark

The telemetry that works

None of these residences show up in file-integrity monitoring. Each has its own surface.

Script block logging is the strongest single source for interpreter abuse. When enabled, PowerShell writes Event ID 4104 to Microsoft-Windows-PowerShell/Operational at Verbose level, and it logs the deobfuscated content of every script block it runs. A -enc base64 command gets decoded and logged in cleartext, which is why script block logging beats reading the command line alone. Enable it here:

$base = 'HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging'
New-Item $base -Force | Out-Null
Set-ItemProperty $base -Name EnableScriptBlockLogging -Value 1

AMSI sits under the interpreter. Before a script block runs, the engine passes the plain text to AmsiScanBuffer in amsi.dll, and a registered engine like Defender scores it. AMSI catches content entered at the command line with no file to monitor, which is exactly the fileless case.

ETW carries some of the rest. The PowerShell provider feeds 4104. Sysmon goes lower, using its own kernel driver callbacks rather than ETW to surface the memory and injection events: Event ID 8 for CreateRemoteThread, Event ID 10 for ProcessAccess with the GrantedAccess mask and CallTrace, and Event ID 25 for ProcessTampering on hollowing. WMI persistence surfaces on Sysmon Events 19, 20, and 21 (filter, consumer, binding).

Registry writes surface on Sysmon Event ID 13. One honest limit: Event 13 records the written value only for DWORD and QWORD types. A payload stored as a large REG_SZ or REG_BINARY value registers that a write happened, not the blob itself.

Command lines are the cheapest surface and the easiest to evade. powershell -enc, mshta http..., rundll32 with a URL. Useful, but the operator controls the string, so treat it as a starting filter, not the whole detection.

One rule, and what slips past it

This rule keys on script block logging (4104) and fires when a single script block both fetches remote content and executes it in memory.

title: PowerShell in-memory download and execute
id: 8a1f0c2d-9b7e-4c33-a1f5-2e6d4b8c0a71
status: experimental
logsource:
    product: windows
    category: ps_script
    definition: 'Requirements: Script Block Logging (Event ID 4104) enabled'
detection:
    download:
        ScriptBlockText|contains:
            - 'DownloadString'
            - 'DownloadData'
            - 'Net.WebClient'
            - 'Invoke-WebRequest'
            - 'Invoke-RestMethod'
    execute:
        ScriptBlockText|contains:
            - 'IEX'
            - 'Invoke-Expression'
            - '[Reflection.Assembly]::Load'
            - '.Invoke('
    condition: download and execute
falsepositives:
    - Package managers and deployment tooling (Chocolatey, PSWindowsUpdate)
    - Admin scripts that pull and run remote modules from an internal repo
level: high

What it catches: staged loaders that download a stage and run it without writing it, the most common fileless pattern in commodity intrusions. Because 4104 logs deobfuscated text, base64 and string-concatenation obfuscation get decoded before the match runs.

What slips past it, and this is the point of the post:

  • Registry-stored payloads run by mshta or rundll32 never invoke PowerShell, so no 4104 is generated.

  • WMI event subscription persistence that runs a CommandLineEventConsumer produces no script block. Catch it on Sysmon 19/20/21 instead.

  • Injected shellcode and reflectively loaded PEs (T1620) run no interpreter. Catch those on Sysmon 8 and 10.

  • Execution through the System.Management.Automation assembly, without powershell.exe, can run PowerShell logic while still generating 4104 in most builds, but a host that suppresses the provider evades it.

That last point is the arms race. Script block logging and AMSI both depend on the engine reporting honestly about itself, and both are in-process. Attackers tamper with the reporting: patching amsi.dll in memory so the scan always returns clean (Turla did this, T1685), or neutering the ETW write path in ntdll so 4104 never leaves the process. We are not publishing steps for either. The defensive takeaway is that these controls are strong until the process hosting them is trusted to grade its own homework, so pair them with out-of-process telemetry (Sysmon, EDR kernel callbacks) that the payload cannot silently switch off.

ATT&CK mapping

ID

Title

Residence

T1055

Process Injection

Memory of another process

T1620

Reflective Code Loading

Memory of the same process

T1027.011

Fileless Storage

Registry, event log, WMI repository

T1546.003

WMI Event Subscription

WMI repository binding

T1053.005

Scheduled Task

Task XML with encoded command

T1059.001

PowerShell

Interpreter loading remote content

T1547.001

Registry Run Keys

Registry (persistence)

T1685

Disable or Modify Tools

AMSI patch, ETW tamper

Summary

Fileless malware is a bad name for code that avoids the one file a scanner would read. The real residences are process memory, the registry, the WMI repository, scheduled tasks, and remote-loading interpreters, and persistence usually writes to disk somewhere, which is where you get your shot. Script block logging, AMSI, ETW, and process-access telemetry each cover part of the map, and no single one covers all of it. Treat AMSI and script block logging as tamperable in-process controls, and back them with telemetry the payload cannot quietly disable.

If you want detections built for the residences a file scanner cannot see, start with the Windows set at /rules?domain=windows, and the memory-and-injection background is in what is malware analysis.

collect_for_fileless_hunting:
  - PowerShell/Operational 4104   # script block logging (deobfuscated)
  - Sysmon 8                      # CreateRemoteThread
  - Sysmon 10                     # ProcessAccess (GrantedAccess, CallTrace)
  - Sysmon 13                     # RegistryEvent value set
  - Sysmon 19,20,21               # WMI filter, consumer, binding
  - Sysmon 25                     # ProcessTampering (hollowing)
  - Security 4698                 # scheduled task created

Related articles