DLL side-loading and DLL hijacking are two closely related techniques that abuse the Windows DLL search order to make a legitimate process load a malicious library. The result is code execution under a trusted process — signed, whitelisted, and typically excluded from aggressive monitoring. Nation-state actors including APT10, APT29, and Lazarus Group use them routinely. So does practically every RaaS affiliate that cares about defence evasion.
The techniques are distinct in their setup but share a detection surface:
-
DLL Hijacking (T1574.001): An attacker places a malicious DLL with a specific name in a directory that appears earlier in the Windows DLL search path than the legitimate DLL’s location. When the target application loads the DLL by name, it finds the malicious copy first.
-
DLL Side-Loading (T1574.002): The attacker uses a legitimate, typically signed executable and places both the binary and a malicious DLL in the same directory. The legitimate binary loads the DLL at runtime because it’s named to match what the binary expects — often exploiting the fact that Windows checks the application directory first.
The Windows DLL Search Order
Windows resolves DLL names in this order (with default settings):
- The application’s own directory
- The system directory (
C:\Windows\System32) - The 16-bit system directory (
C:\Windows\System) - The Windows directory (
C:\Windows) - The current working directory
- Directories listed in
%PATH%
Side-loading exploits step 1: putting both the legitimate binary and the malicious DLL in an attacker-controlled directory. Hijacking exploits the priority of earlier search locations over later ones — particularly the current working directory or writable %PATH% entries.
How Attackers Use These Techniques
APT side-loading pattern: Drop a legitimate signed executable (commonly a vendor tool, AV binary, or OS component) into a non-standard path alongside a crafted DLL. Execute the binary. Code runs inside a trusted process context. The Volt Typhoon WinBio persistence technique and numerous Lazarus campaigns both use this pattern.
Ransomware affiliate pattern: Place a DLL in a writable directory that gets searched before System32. A common target is C:\Python39\ or other scripting environment paths added to %PATH% during legitimate software installation. When a privileged process launches, the malicious DLL loads automatically.
Privilege escalation via UAC bypass: Several documented DLL hijacking chains exploit auto-elevated COM objects that search writable user-accessible paths before reading from System32. These allow a medium-integrity process to escalate to high integrity without a UAC prompt.
Key Artefacts
Sysmon Event 7 (ImageLoad): The primary telemetry source. This event records every DLL loaded by every process, including path, file hash, and whether the image is signed.
Event ID 4688 (Process Creation): Look for legitimate binaries running from unusual directories — %TEMP%, %APPDATA%, Downloads, or custom user paths.
Unsigned or poorly signed DLLs loaded by signed processes: A signed Microsoft binary loading an unsigned DLL from the same directory is anomalous.
Sigma Rules
Unsigned DLL loaded by a signed process from a suspicious directory:
title: Suspicious Unsigned DLL Loaded by Signed Process
id: a47f3b5e-1234-4abc-a123-9d8e4f1a2b3c
status: experimental
description: Detects a signed process loading an unsigned DLL from a user-writable or temp directory
logsource:
category: image_load
product: windows
detection:
selection:
Signed: 'true'
ImageLoaded|contains:
- '\Users\'
- '\Temp\'
- '\AppData\'
- '\Downloads\'
- '\ProgramData\'
filter_legit:
ImageLoaded|endswith:
- '\AppData\Roaming\Microsoft\Teams\'
- '\AppData\Local\Programs\'
filter_signed_dll:
SignatureStatus: 'Valid'
condition: selection and not filter_legit and not filter_signed_dll
fields:
- Image
- ImageLoaded
- Signed
- SignatureStatus
- ProcessId
falsepositives:
- Developer tools loading local DLLs during development
- Some legitimate software that ships unsigned libraries
level: medium
tags:
- attack.defense_evasion
- attack.persistence
- attack.privilege_escalation
- attack.t1574.001
- attack.t1574.002
Legitimate binary running from a non-standard path (side-loading indicator):
title: Known Binary Executed From Suspicious Path (DLL Side-Loading Setup)
id: b58c3d2f-5678-4def-b456-1e9f5a3c4d5e
status: experimental
description: Detects known signed binaries executed from paths outside their expected install location
logsource:
category: process_creation
product: windows
detection:
selection:
Image|endswith:
- '\OneDrive.exe'
- '\Teams.exe'
- '\DismHost.exe'
- '\wusa.exe'
- '\rdrleakdiag.exe'
filter_legit_path:
Image|startswith:
- 'C:\Program Files\'
- 'C:\Program Files (x86)\'
- 'C:\Windows\'
condition: selection and not filter_legit_path
falsepositives:
- Portable installations
level: high
tags:
- attack.defense_evasion
- attack.t1574.002
KQL for Microsoft Sentinel / Defender XDR
// DLL loaded by signed process from writable user path
DeviceImageLoadEvents
| where InitiatingProcessVersionInfoCompanyName != ""
and IsProcessTokenElevated == true
| where FolderPath contains @"\Users\"
or FolderPath contains @"\Temp\"
or FolderPath contains @"\AppData\"
or FolderPath contains @"\ProgramData\"
| where IsSigned != true or not(SignatureState == "SignedValid")
| where InitiatingProcessFolderPath !contains @"\Users\"
| project Timestamp, DeviceName, FileName, FolderPath, IsSigned,
SignatureState, InitiatingProcessFileName, InitiatingProcessFolderPath
| order by Timestamp desc
// Potential side-loading: process binary and DLL in same non-standard directory
DeviceImageLoadEvents
| where FolderPath == InitiatingProcessFolderPath
| where FolderPath !startswith @"C:\Windows\"
and FolderPath !startswith @"C:\Program Files\"
and FolderPath !startswith @"C:\Program Files (x86)\"
| summarize DllCount=count(), DllList=make_set(FileName) by
DeviceName, InitiatingProcessFileName, FolderPath, Timestamp=bin(Timestamp, 1h)
| where DllCount > 1
| order by Timestamp desc
Hunting for Known-Vulnerable Binaries
Maintain a local list of known DLL-hijackable binaries from sources like hijacklibs.net and lolbas-project.github.io. Cross-reference Sysmon Event 7 logs against the expected load paths for these binaries:
let VulnerableBinaries = datatable(BinaryName:string, ExpectedPath:string) [
"dfsvc.exe", @"C:\Windows\Microsoft.NET\Framework",
"rdrleakdiag.exe", @"C:\Windows\System32",
"OneDriveSetup.exe", @"C:\Windows\SysWOW64"
];
DeviceImageLoadEvents
| join kind=inner VulnerableBinaries on $left.InitiatingProcessFileName == $right.BinaryName
| where InitiatingProcessFolderPath != ExpectedPath
| project Timestamp, DeviceName, BinaryName, InitiatingProcessFolderPath,
ExpectedPath, FileName, FolderPath
Baseline and Reduce False Positives
This technique generates volume. Key baselining steps:
- Build a known-good image load inventory per application per host class. Developer boxes will have unusual DLL loads; production servers should not.
- Separate signed-from-signed (lower priority) from signed-from-unsigned (higher priority) detections.
- Focus the highest-priority alerting on DLLs loaded from
%TEMP%,%APPDATA%\Roaming, andDownloads— these paths should almost never be DLL load sources in production. - For side-loading specifically, the combination of a signed binary in an unusual path with an unsigned co-located DLL is a high-confidence indicator. Escalate those directly.
Response Checklist
When a DLL side-loading or hijacking alert fires:
- Confirm the hash of the loaded DLL against VirusTotal and your threat intel platform
- Check for persistence mechanisms (scheduled task, service, registry run key) that call the binary
- Identify how the binary and DLL arrived on the system (parent process, network connection, email delivery)
- Check for lateral movement from the compromised host in the ±2 hour window
- Pull the process tree for the loading process and look for child process spawning from unusual locations