DLL Sideloading Mechanics and Windows Search Order

▶ Watch (00:32)

DLL sideloading runs a malicious DLL inside a signed, legitimate process. Security tools sometimes trust those processes more than unsigned ones, so malicious code executing inside them gets less scrutiny. The attack works because Windows searches for DLLs in a specific order, and the application folder is on that list. If an executable loads a DLL by relative path instead of a full path like C:\Windows\System32\, an attacker who drops a same-named DLL into that folder wins.

Kucherin demonstrated this with the Avast-signed binary sideloading demo (01:36): Process Monitor showed a signed Avast binary searching for WSC.dll across several directories, and placing a malicious copy alongside the executable produced an immediate popup confirming injection into the process. A full absolute path blocks the attack entirely.

Why NTDLL Normally Cannot Be Sideloaded

▶ Watch (05:11)

NTDLL.dll is the first library loaded into every Windows process. It contains the startup code that every executable needs, so it is always present before any other DLL loads. Windows DLL search order checks the loaded-module list at step 4, before the application folder at step 7. Because NTDLL is already on that list, the search terminates at step 4 and the application folder is never reached.

Known DLLs add a second layer. Windows treats a fixed set of system libraries as canonical, loading them from System32 regardless of path. NTDLL sits on both lists. Windows internals veterans Kucherin consulted all reached the same conclusion: sideloading NTDLL by dropping a copy next to the executable cannot work.

APT41’s CLR Persistence Trick Against a Southeast Asia Game Studio

▶ Watch (08:55)

APT41 infected a Southeast Asia video game company running a new variant of PIPMON, malware ESET first documented in 2020 and attributed to the group. The interesting part was not the malware itself but where they planted it. They dropped a malicious ntdll.dll into C:\Windows\Microsoft.NET\, not System32 where the real one lives. Windows Server starts Server Manager automatically on every boot. Server Manager is written in .NET, and .NET’s CLR checks the Global Assembly Cache before the loaded-module list, bypassing the protection that normally blocks ntdll sideloading entirely. Server Manager persistence demo (09:50). No scheduled task. No service. No startup folder entry.

“it cannot be in fact detected through tools like auto runs you can only spot this DL with a researcher’s eye” — Georgy Kucherin

.NET’s Broken DLL Search Order and the Known-DLL Loophole

▶ Watch (13:41)

.NET Core skips both the loaded-module-list check and the known-DLL protection that block sideloading under standard Win32 rules. Drop a fake kernel32.dll into any .NET Core app folder and the runtime loads it instead of the system copy. kernel32.dll sideloading via .NET Core (13:41)

Microsoft knew. A prior researcher had reported a related flaw. Microsoft declined to patch, calling it below the bar for an immediate security update. Then APT41 used it.

“you see today you see ah it’s it’s a minor issue. We’re not we’re not we’re not going to fix it. Tomorrow APS use it in their attacks.” — Georgy Kucherin

A workaround exists: an opt-in registry value that enforces safe DLL search paths. Because it ships disabled, almost no .NET developers know it exists.

Real-World Vulnerable Targets: Discord and Avira

▶ Watch (16:34)

A YARA hunt on VirusTotal flagged Discord as vulnerable. Discord calls NtQueryInformationProcess from ntdll.dll on startup. Drop a malicious ntdll.dll into Discord’s folder, change the startup argument from --process-start to --process-start-and-wait, and the injection runs inside Discord on every boot.

“show me a cyber security analyst who knows by heart how discord is supposed to start with with what arguments it’s supposed to start” — Georgy Kucherin

Avira’s self-defense blocks writes to its own folder, even as admin. The bypass: drop ntdll.dll into the Global Assembly Cache, a folder Avira cannot protect. Avira self-defense bypass via Global Assembly Cache (20:40). On reboot, the injected code ran inside the Avira process before the desktop appeared, with SYSTEM privileges.

Q&A

Can injecting into the AV process before boot prevent the antivirus from detecting anything at all? Kucherin hadn’t tested it fully, but said the user-mode foothold inside the process could at minimum suppress alert popups, and that launching malware from inside the AV to test whether it detects itself needs further experimenting. ▶ 25:07

Would requiring signed DLLs solve this sideloading problem? Requiring signed DLLs via a Windows policy could help, but the real fix is developers always loading DLLs by full path and prioritizing the system directory, and even a signing policy can be bypassed. ▶ 26:20

How does EDR userland hooking of ntdll interact with CLR sideloading? The sideloaded ntdll loads alongside the system copy, so existing hooks in the system DLL stay intact, but once inside the process an attacker can unhook nearly every library, which is why kernel-side hooks are necessary for reliable detection. ▶ 28:16

Notable Quotes

you see today you see ah it’s it’s a minor issue. We’re not we’re not we’re not going to fix it. Tomorrow APS use it in their attacks. Georgy Kucherin · ▶ 15:31

it cannot be in fact detected through tools like auto runs you can only spot this DL with a researcher’s eye Georgy Kucherin · ▶ 11:41

show me a cyber security analyst who knows by heart how discord is supposed to start with with what arguments it’s supposed to start Georgy Kucherin · ▶ 19:30

in almost in every report about a Chinese speaking AP you see this trick being used. Georgy Kucherin · ▶ 27:25

Key Takeaways

  • Treat any .NET application loading DLLs by relative path as a sideloading candidate, regardless of known-DLL status.
  • Place NTDLL in the Global Assembly Cache to persist through reboots inside trusted processes without creating scheduled tasks or services.
  • Unpatched low-severity DLL search order flaws become APT weapons; fix them before threat actors weaponize them.

About the Speaker

Georgy Kucherin

Georgy Kucherin is a junior researcher at Kaspersky’s Global Research and Analysis Team and a master’s student at Moscow State University. He is passionate about analysis of complex malware and reverse engineering. His previous research includes attribution of the SolarWinds attack, as well as thorough investigations into APTs such as Operation Triangulation, Turla, FinFisher, APT41 and Lazarus.