How to Detect DLL Sideloading and DLL Hijacking Attacks

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
Windows application code trusts DLLs in the same directory as the executable by default: this is by design, allowing applications to ship with their own versions of shared libraries. Attackers exploit this trust by dropping a malicious DLL with the right name next to a legitimate signed binary, which then loads the malicious code automatically on execution.
The technique is especially effective against security tools because: (1) the process that loads the malicious DLL is signed by a legitimate vendor, making it appear trusted to AV/EDR; (2) many signed binaries from software vendors are commonly deployed, making them reliable carriers; and (3) it does not require exploiting a vulnerability: only write access to a directory where the carrier binary resides.
DLL Sideloading Mechanics
How DLL search order creates the vulnerability:
Windows DLL search order (SafeDllSearchMode ON):
1. Directories listed in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
2. The directory from which the application loaded
3. The system directory (C:\Windows\System32)
4. The 16-bit system directory
5. The Windows directory (C:\Windows)
6. The current directory
7. Directories listed in PATH environment variable
Step 2 (application directory) is the attack surface:
- If malware places malicious.dll in the same directory as legitapp.exe,
and legitapp.exe calls LoadLibrary("malicious.dll"),
Windows loads the malicious version from step 2 before finding the real one in step 3
Common vulnerable scenarios:
Scenario 1: Missing dependency:
legitapp.exe expects DWrite.dll from System32
Attacker creates C:\Users\Public\App\DWrite.dll (malicious)
Attacker also places legitapp.exe in C:\Users\Public\App\\n legitapp.exe loads malicious DWrite.dll from its own directory
Scenario 2: DLL phantom loading:
Some applications call LoadLibrary() for DLLs that don't exist on the system
The DLL load fails silently, but an attacker can create the DLL at the expected path
Process Monitor can identify phantom DLL loads (NAME NOT FOUND results for DLL lookups)
Scenario 3: PATH hijacking:
Application expects a DLL in a PATH directory
Attacker places malicious DLL earlier in PATH (writable directory)
# Identify vulnerable signed binaries:
# Use Process Monitor (Sysinternals) filtered on "NAME NOT FOUND" for .dll files
# These are candidates for phantom DLL hijacking
Detection: Sysmon Image Load Events
Sysmon Event ID 7 (Image Loaded) is the primary detection source: it logs every DLL load with the DLL path and signature status.
Enable Event ID 7 in Sysmon config:
<EventFiltering>
<ImageLoad onmatch="exclude">
<!-- Exclude known-good DLL load paths to reduce volume -->
<ImageLoaded condition="begin with">C:\Windows\System32\</ImageLoaded>
<ImageLoaded condition="begin with">C:\Windows\SysWOW64\</ImageLoaded>
<ImageLoaded condition="begin with">C:\Program Files\</ImageLoaded>
<ImageLoaded condition="begin with">C:\Program Files (x86)\</ImageLoaded>
<!-- Exclude signed Microsoft DLLs -->
<Signed condition="is">true</Signed> <!-- Caution: may miss signed-but-sideloaded DLLs -->
</ImageLoad>
</EventFiltering>
<!-- Note: Event ID 7 is high volume: tune exclusions carefully -->
Better approach: include only anomalous loads:
<ImageLoad onmatch="include">
<!-- Unsigned DLLs loaded from non-standard locations -->
<ImageLoaded condition="contains">\.dll</ImageLoaded>
<!-- The Signed field helps: unsigned DLL loaded by signed process = suspicious -->
</ImageLoad>
Sentinel KQL: unsigned DLL loaded by signed process:
// Detect sideloading: signed process loading unsigned DLL from unusual path
Event
| where Source == "Microsoft-Windows-Sysmon" and EventID == 7
| extend EventData = parse_xml(EventData)
| extend
Image = tostring(EventData.DataItem.EventData.Data[4]["#text"]), // Loading process
ImageLoaded = tostring(EventData.DataItem.EventData.Data[6]["#text"]), // DLL path
Signed = tostring(EventData.DataItem.EventData.Data[10]["#text"]),
SignatureStatus = tostring(EventData.DataItem.EventData.Data[12]["#text"])
| where Signed == "false"
// Exclude known-safe paths
| where ImageLoaded !startswith @"C:\Windows\\"
and ImageLoaded !startswith @"C:\Program Files\\"
// Flag unsigned DLLs loaded from user-writable locations
| where ImageLoaded matches regex @"(?i)(\\temp\\|\\appdata\\|\\users\\|\\downloads\\|\\public\\)"
| summarize
count(),
make_set(ImageLoaded)
by Image, Computer, bin(TimeGenerated, 1h)
| sort by count_ desc
Detect DLL loaded from same directory as suspicious executable:
Event
| where Source == "Microsoft-Windows-Sysmon" and EventID == 7
| extend EventData = parse_xml(EventData)
| extend
Image = tostring(EventData.DataItem.EventData.Data[4]["#text"]),
ImageLoaded = tostring(EventData.DataItem.EventData.Data[6]["#text"]),
Signed = tostring(EventData.DataItem.EventData.Data[10]["#text"])
// Extract directory of loading process
| extend ProcessDir = tostring(split(Image, "\\")[0..array_length(split(Image, "\\"))-2])
// Extract directory of loaded DLL
| extend DLLDir = tostring(split(ImageLoaded, "\\")[0..array_length(split(ImageLoaded, "\\"))-2])
// DLL is in same directory as the loading executable (sideloading pattern)
| where ProcessDir == DLLDir
and Signed == "false"
and ProcessDir !startswith @"C:\Windows"
and ProcessDir !startswith @"C:\Program Files"
| project TimeGenerated, Computer, Image, ImageLoaded, ProcessDir
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Finding Vulnerable Binaries and Hardening
Find sideloadable binaries in your environment:
# Use SigCheck (Sysinternals) to find signed executables that load unsigned DLLs:
SigCheck64.exe -accepteula -c -h C:\Tools\AppDirectory\
# Look for: signed executables that reference unsigned DLLs in the same directory
# Use Process Monitor during application testing:
# Filter: Result = NAME NOT FOUND; Path ends with .dll
# These are phantom DLL load attempts: if an attacker places a DLL with that name
# in the application directory, it will be loaded
# Tools for finding hijackable DLLs:
# DLLSpy: https://github.com/cyberark/DLLSpy
# RobbinHood (deprecated): scans for vulnerable DLL search paths
# PowerShell: find directories in PATH that are user-writable
$env:PATH -split ';' | ForEach-Object {
$dir = $_
try {
$acl = Get-Acl $dir -ErrorAction Stop
$writable = $acl.Access | Where-Object {
$_.FileSystemRights -match 'Write|FullControl|Modify' -and
$_.IdentityReference -match 'Users|Authenticated Users|Everyone'
}
if ($writable) { "WRITABLE: $dir" }
} catch { "NOT FOUND: $dir" }
}
# Writable PATH directories are DLL hijacking candidates
Hardening against DLL sideloading:
- Application directory permissions: Do not allow non-admin users to write to application installation directories. Use
icaclsto verify:
icacls "C:\Program Files\VendorApp" | \
Select-String "Users|Everyone" | \
Where-Object { $_ -match 'W|F|M' }
# Any write permission for Users/Everyone = DLL sideloading risk
-
Enable SafeDllSearchMode: The registry value
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeDllSearchMode = 1(default in modern Windows) moves the current directory search to after system directories: reduces hijacking risk but not sideloading from application directories. -
WDAC or AppLocker DLL rules: Configure Windows Defender Application Control to only allow signed DLLs from approved publishers. Note: DLL rules have high performance overhead and require thorough testing.
-
Vendor notification: If you identify a vendor application that sideloads vulnerable DLLs, notify the vendor: this is a vulnerability in their product that they should fix by using fully qualified paths or manifest-based loading.
The bottom line
DLL sideloading exploits Windows DLL search order: the application directory is searched before system directories, so attackers place malicious DLLs alongside legitimate signed executables. Detection requires Sysmon Event 7 (Image Load) monitoring for unsigned DLLs loaded from user-writable directories or loaded from the same directory as suspicious executables. Use Process Monitor filtered on "NAME NOT FOUND" for .dll files to proactively identify phantom DLL load opportunities in your deployed software. Harden by verifying application directory permissions (no user write access), enabling SafeDllSearchMode, and considering WDAC DLL rules for high-sensitivity environments.
Frequently asked questions
What is DLL sideloading and how does it differ from DLL hijacking?
DLL sideloading places a malicious DLL alongside a legitimate signed executable that loads DLLs from its own directory: the legitimate process loads the malicious DLL, running attacker code under a trusted process. DLL hijacking is broader: it includes placing DLLs in writable directories in the DLL search path (PATH hijacking) or creating DLLs that an application tries to load but cannot find (phantom DLL hijacking). Both exploit Windows DLL search order; sideloading specifically abuses the application-directory priority.
How do you detect DLL sideloading with Sysmon?
Enable Sysmon Event ID 7 (Image Load) and alert on unsigned DLLs (Signed = false) loaded from user-writable paths like %TEMP%, %APPDATA%, C:\Users\, and C:\Public\. A stronger signal: an unsigned DLL loaded from the same directory as the loading process, when that directory is not C:\Windows or C:\Program Files. Use Microsoft Sentinel KQL or Splunk to correlate Image (loading process path) and ImageLoaded (DLL path) to find cases where both files share the same directory and the DLL is unsigned.
What is DLL search order hijacking and how does it differ from sideloading?
DLL hijacking (search order hijacking) exploits the Windows DLL search order: when an application loads a DLL without a full path, Windows searches in a specific order starting with the application directory. If an attacker places a malicious DLL with the same name as a legitimate DLL in the application directory (which the attacker can write to), Windows loads the malicious DLL instead of the legitimate system DLL. DLL sideloading is a variant where the attacker deliberately places a legitimate application that loads DLLs alongside a malicious DLL of the expected name — the legitimate app becomes the launch vehicle. The difference: hijacking exploits a vulnerability in the application's search order; sideloading uses a legitimate application's expected behavior.
How do attackers use DLL sideloading for persistence?
DLL sideloading provides persistence by embedding malicious code in a DLL that runs every time a trusted application starts. Common technique: place a modified version of a DLL loaded by a legitimate Windows tool (OneDrive, Teams, or other trusted software) in a user-writable directory, then configure the legitimate application to launch at startup. The malicious DLL proxies all legitimate exports to the real DLL while also executing malware. Because the legitimate application is running and signed by a trusted publisher, it bypasses application allowlisting and reduces suspicion. APT groups including APT41, Lazarus Group, and TA505 have used DLL sideloading extensively in their operations.
How do I find DLL hijacking vulnerabilities in my environment?
Use Process Monitor (Procmon) from Sysinternals: filter for 'Result is NAME NOT FOUND' and 'Path ends with .dll' to capture DLL search misses — when an application looks for a DLL that does not exist in a user-writable path, that is a hijacking opportunity. Run Procmon as a standard user (not admin) to see hijacking opportunities available to low-privilege attackers. For automated analysis, the tool 'DLLHijackAudit' by @XPN (MDSec Research) automates this analysis across running processes. For a registry-based approach, check the KnownDLLs registry key: DLLs listed there are loaded from the System32 directory regardless of search order — adding your DLL to KnownDLLs prevents hijacking for that specific DLL.
How do you build a WDAC policy that blocks unsigned DLL loads from user-writable paths without breaking legitimate software?
Start by running WDAC in audit mode: deploy the policy via 'CiTool --merge-policy' with the policy option 'Enabled:Audit Mode Only' and collect CI audit events (Event 3076 and 3077 in the Microsoft-Windows-CodeIntegrity/Operational log) for two to four weeks across representative machines. Use the 'New-CIPolicy -ScanPath' cmdlet against directories containing your approved software to generate a baseline allow list from installed binaries. For the DLL block rule, add a deny rule scoped to unsigned DLLs loaded from paths matching %LOCALAPPDATA%, %TEMP%, and %PUBLIC% using the 'FilePathRule' element in the policy XML. Merge the deny rules with the allow rules using 'Merge-CIPolicy' and test the merged policy in audit mode before switching to Enforced. Monitor Event 3077 (enforcement block) for legitimate software collisions and add specific file hash or publisher allow rules for each blocked file before moving to production enforcement.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
