The file named python311.dll sitting next to a copy of python.exe was not CPython at all. It was a 64-bit Go library that exported just four Python-compatible function names — enough to satisfy the executable's import requirements and hand control to malicious code. That library was HollowFrame, a previously undocumented modular loader framework identified by Blackpoint Cyber's Adversary Pursuit Group (APG) in research published July 30. (Source: Infosecurity-Magazine)
The technique is DLL sideloading: a legitimate signed binary is launched, and it obediently loads whatever DLL is sitting beside it with the expected filename. Because python.exe was started with no script or module argument, it did nothing but load the fake runtime and execute the attacker's Go code. For your security team, that means the process tree looks like a developer running Python.
The evasion started before any payload landed. The initial PowerShell stage prompted the user for administrator rights, then created Microsoft Defender exclusions covering both the staging directory and the process name python.exe. Only after that did it begin pulling down executable content.
Blackpoint said the actor had "effectively prepared a trusted looking execution lane" before the loader arrived.
HollowFrame was the delivery mechanism, not the end goal. It dropped a native loader that sideloaded a malicious version.dll beside a legitimate OneDrive updater, placing a pair of Rust backdoors tracked as Matryoshka inside a trusted Microsoft process. Blackpoint observed the intrusion on two endpoints at a law firm.
The exposure here is broader than Python shops. Any Windows environment where an attacker can convince a user to click through an elevation prompt is exposed, because the same pattern works with any signed executable that resolves DLLs from a user-writable directory. If your staff can write files to their own profile directories and run signed binaries from them — which describes most Windows endpoints — the plumbing for this attack already exists.
Attack Chain: DLL Sideloading, Certutil, and Hollow Process Injection
The intrusion started with a spear phishing email sent to several staff at the same law firm, with a link that routed recipients through an attacker-controlled redirector to an encrypted archive hosted on Mega. Inside was a shortcut file named Case Documents.lnk — the kind of filename that survives scrutiny in a practice that receives client documents all day.
Executing the shortcut wrote Base64 content to a temporary file, then used the built-in Windows utility certutil to rebuild that content into a working script. Certutil is a signed Microsoft binary present on every Windows host, so its use for decoding maps to MITRE ATT&CK T1140 (Deobfuscate/Decode Files or Information) and generates no unsigned-binary alert.
The rebuilt script launched an obfuscated PowerShell chain (T1059.001) that prompted the user for administrator rights. That prompt is the pivot point in the whole chain: a single click by a staff member handed the operator elevation.
Exclusions First, Payload Second
Once elevated, the stage created Microsoft Defender exclusions covering both a staging directory and the process name python.exe, and only then started downloading executable content. Blackpoint's Adversary Pursuit Group described the result as an "effectively prepared a trusted looking execution lane."
Ordering matters here. Because the exclusions existed before any payload touched disk, Defender never scanned the download, never scanned the write, and never evaluated the process that ran afterward — this is T1562.001, Impair Defenses: Disable or Modify Tools. For your environment, that means the endpoint agent was not bypassed by clever code; it was told, with local admin rights, to look away.
The archive it retrieved was named to resemble an official Python embedded distribution. One character gives it away: the filename read amd96 instead of amd64.
Injection Methods and Host Checks
The loader that ran from that bundle was modular, offering several execution methods: process ghosting, module stomping and manual PE mapping. Operators could pick a different method per host, so the same framework produced different telemetry on each endpoint — one reason two infected machines at the same firm may not look like the same incident to an analyst reviewing alerts.
Before executing, it checked host characteristics consistent with T1497 (Virtualization/Sandbox Evasion):
- System uptime
- Installed physical memory
- Cursor movement
Persistence came in three forms: a scheduled task (T1053.005), a WMI event subscription tied to new logon sessions (T1546.003), and the Startup folder. The WMI subscription is the quietest of the three, since it fires on logon without appearing in the task scheduler view most administrators check first.
Handoff to Matryoshka
The loader then dropped a native loader that placed a malicious version.dll beside a legitimate OneDrive updater. Command execution and network traffic for the first Matryoshka backdoor therefore originated inside a trusted Microsoft process.
A second Rust variant used wtsapi32.dll, proxying 41 Windows Terminal Services exports so the host application kept functioning normally. That variant skipped a custom C2 server entirely: each victim was assigned its own directory in a private GitHub repository holding beacon, command and result files (T1102, Web Service), and its requests carried a OneDrive user agent — an indicator Blackpoint listed explicitly.
Beyond shell access, it identified domain controllers, enumerated domain computers and privileged group membership, and inventoried network configuration, local privileges and installed software. That is the reconnaissance set an operator builds before moving from two endpoints to the domain itself.
Business and Operational Impact
The Defender exclusions are the part that outlives the intrusion. Once an elevated stage added an exclusion for the staging directory and for the process name python.exe, every file written to that folder and every process running under that name became invisible to your endpoint agent — not just the loader in this campaign, but anything dropped there afterward. If your team remediates the malware without auditing the exclusion list, you have removed the payload and left the blind spot.
That blind spot sat on two endpoints at a law firm, and the second-stage Matryoshka backdoor spent its time building an inventory of everything around it: domain controllers, domain computers, privileged group membership, network configuration, local privileges and installed software. Two compromised workstations produce a map of your entire environment, including which accounts hold the permissions worth stealing next. That reconnaissance is what turns a two-machine incident into a network-wide scoping exercise for your responders.
Your egress controls do not help here either. The Rust variant used per-victim directories in a private GitHub repository to hold beacon, command and result files, so tasking and file transfer rode ordinary HTTPS traffic to a service your developers and CI pipelines already reach. There is no attacker-owned domain to block and no novel C2 IP to feed into a watchlist, which means the usual "have we seen this indicator?" question returns nothing useful.
Command execution and network traffic also lived inside processes you would never flag by eye. The first Matryoshka backdoor arrived as a malicious version.dll sideloaded beside a legitimate OneDrive updater, and the GitHub variant's requests carried a OneDrive user agent. On a process tree, on a firewall log, in a screenshot from a worried user, it looks like Microsoft sync activity.
Several design choices raise the cost of investigating this on your own network:
- Variable execution methods — HollowFrame supports process ghosting, module stomping and manual PE mapping, so the same framework leaves different artifacts on different hosts. Findings from your first infected endpoint will not reliably scope the second.
- Anti-analysis checks — the loader inspects uptime, installed memory and cursor movement before running. A sample submitted to a sandbox can come back quiet, which gives your team false assurance about a file that is genuinely malicious.
- Three persistence routes — a scheduled task, a WMI event subscription tied to new logon sessions, and the Startup folder. Reimaging one workstation does not necessarily end the operator's access.
- A 41-export proxy DLL — the Rust
wtsapi32.dllforwards 41 Windows Terminal Services exports, so the host application keeps working normally and no user reports a problem.
For a professional services firm, the material at risk is client work product: case files, deal documents, litigation strategy and the credentials that reach the document management system. Confidentiality obligations to clients are contractual and, in regulated matters, statutory, and firms handling health records or payment data on behalf of clients inherit HIPAA and PCI DSS notification duties for those records as well.
The financial consequence tends to show up as forensic and legal hours rather than a ransom demand. When you cannot prove which files a backdoor read, notification decisions get made on the assumption that everything in reach was taken.
Detection and Forensic Indicators for HollowFrame
Start with a filesystem sweep for signed binaries that load a DLL from the same user-writable folder they sit in. In this intrusion the native second stage dropped a malicious version.dll next to a legitimate OneDrive updater, so the backdoor's command execution and outbound traffic came from a trusted Microsoft process. Any query you write should match on the load path, not the binary's signature — the signature is real.
Two specific artifacts belong in that sweep now:
version.dllpresent in any directory alongside a Microsoft-signed updater executable, particularly under user profile or temp paths.wtsapi32.dllthat is unusually large for a Terminal Services stub and proxies 41 exports — the Rust Matryoshka variant forwarded legitimate exports to stay functional while running its own code.- Archive or directory names containing the string amd96, the typo in the counterfeit Python embedded distribution filename. It is a cheap, high-fidelity string to grep across download folders and staging directories.
Once the file hunt is running, move to network and proxy logs. Blackpoint's clearest indicator is a OneDrive user agent on requests that did not come from OneDrive, because the GitHub-based variant borrowed that user agent for its tasking traffic. Correlate unexpected GitHub API connections from non-browser, non-developer processes with repeated requests for the same small set of files — the operator assigned each victim a directory in a private repository holding beacon, command and result files, which produces a polling rhythm rather than one-off pulls.
If your firm has no development function, GitHub API access from endpoints is noise you can remove entirely. Constraining it at the egress layer turns a covert channel into a blocked connection and an alert, and it costs a law practice nothing operationally.
Next, audit persistence. The loader offered three routes — a scheduled task, a WMI event subscription tied to new logon sessions, and the Startup folder. Pull your scheduled task inventory and WMI subscription registrations and look for update-themed names, since that is the naming convention the operator chose to blend with software maintenance. Logon-triggered WMI subscriptions are rare in a typical business environment, so any hit deserves a full look rather than a triage close.
For ongoing monitoring, assume hash-based detection will fail. The loader supported process ghosting, module stomping and manual PE mapping, which means the same framework produced different telemetry on each of the two affected endpoints. Behavioral rules hold up better: image-load events where a signed process loads an unsigned or freshly written DLL from a user-writable path, and process creations followed by remote thread activity into an already-running image. In environments Capstone manages, SentinelOne flags the endpoint-protection tampering and in-memory execution patterns behind this chain, including the exclusion-then-execute sequence that preceded the loader.
Be aware the loader also checked system uptime, installed memory and cursor movement before executing. If your analysis sandbox reports "no malicious activity" on one of these samples, treat that as inconclusive rather than clean, and confirm with extended runtime and simulated user input.
Finally, change how inbound attachments and links are handled. Detonate password-protected archives and shortcut files in a controlled environment before staff open them, and record the redirector domains and Mega links you see so your mail gateway blocks the next round. Document every artifact you find with hashes, paths and timestamps — the forensic record is what tells you whether a second endpoint was touched.
Immediate and Short-Term Mitigation Actions
Start by denying certutil.exe execution to non-administrative users. In AppLocker or Windows Defender Application Control (WDAC), create a deny rule for C:\Windows\System32\certutil.exe scoped to your standard user groups, then confirm nothing in your patching or certificate workflow depends on it. Legitimate business use of certutil on a receptionist's or paralegal's workstation is rare, and removing it breaks the decoding step that turns a shortcut file into a working script.
Next, tighten DLL search behavior so a binary cannot pick up a library from its own drop folder. Verify HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeDllSearchMode is set to 1, and set CWDIllegalInDllSearch under the same key to remove the current working directory from the search order for the process types you can safely constrain. Test this in a pilot group first — poorly packaged line-of-business applications sometimes rely on the loose search path.
Then sweep for unauthorized runtimes. Search user-writable locations for copies of interpreter executables and their expected companion libraries, and treat any embedded distribution living under %APPDATA%, %LOCALAPPDATA%, %TEMP%, or a user's Downloads folder as suspect until proven otherwise. Compare hashes against the official vendor release; in this campaign the archive name read amd96 instead of amd64, so filename inspection alone catches sloppy staging.
Two more actions belong in today's work. Review scheduled tasks and WMI event subscriptions for update-themed names, using Get-ScheduledTask and a query against the root\subscription namespace for __EventFilter and __EventConsumer instances — a subscription that fires on new logon sessions is not normal update behavior. Also restrict outbound GitHub API access from endpoints with no development role, since a private repository holding beacon, command and result files gives an operator tasking and file transfer without any custom infrastructure to block.
For the coming week, move from ad-hoc blocking to enforced code integrity:
- Deploy a WDAC policy in audit mode across a representative endpoint sample, review the generated events, then enforce publisher and path rules that prevent execution from user-writable directories.
- Enable the AppLocker DLL rule collection so signature and path checks apply to loaded libraries, not just executables. This is the control that stops a Go library masquerading as a Python runtime from ever being mapped.
- Turn on the Microsoft Defender attack surface reduction rule blocking executable files that fail prevalence, age, or trusted-list criteria, which raises the cost of freshly compiled loaders.
- Configure your email gateway and sandbox to detonate password-protected archives and shortcut files, and block
.lnkattachments and archive links from untrusted senders outright.
Isolate and rebuild any host where a fake runtime or a sideloaded library is confirmed. Reimaging is the practical choice here because the loader offered a scheduled task, a WMI subscription and a Startup folder entry as persistence, and clearing one leaves the other two in place.
Rebuilding also means restoring data cleanly, and N-able Cove keeps versioned, offsite copies in managed environments so a wiped workstation comes back from a restore point that predates the intrusion.
Because that reconnaissance targets privileged groups, force a password reset for any account that logged into an affected endpoint and review recent changes to Domain Admins and equivalent groups. Adlumin flags the authentication anomalies that follow domain enumeration, so a stolen credential used from an unexpected host surfaces as an alert rather than routine activity.
Preventing DLL Sideloading at Scale
The control that would have stopped this intrusion at the loader stage is application control that evaluates DLLs, not only executables. AppLocker's DLL rule collection and Windows Defender Application Control (WDAC) both check libraries at load time, and a 64-bit Go library exporting four Python-compatible function names carries no valid publisher signature — it fails a signer rule the moment the host process tries to import it.
Many organizations enforce EXE rules and skip the DLL rule collection because of the testing overhead. That is the gap sideloading depends on, so deploy your DLL rules in audit mode first, collect load events for two to four weeks, then flip to enforcement once you know which line-of-business applications load libraries from unusual places.
Pair that with hash and publisher allowlisting for interpreters and portable runtimes. If your firm needs Python, allow it from one managed install path under C:\Program Files and deny embedded or portable distributions everywhere else, so an archive that unpacks its own interpreter into a staging folder cannot execute at all. The same rule pattern covers Node, PowerShell 7 and any other runtime that ships as a self-contained folder.
Then separate write access from execute access across the paths users control:
- Deny execution from
%TEMP%,%APPDATA%,%LOCALAPPDATA%and Downloads using WDAC path rules, which breaks both the script-rebuild step and the loader drop in one policy. - Review ACLs on installed application directories so standard users cannot write a new DLL beside a signed binary that already sits there.
- Remove standing local administrator rights from staff accounts, since the elevation prompt in this chain only succeeded because the user could approve it.
Passportal stores and rotates local administrator credentials across managed environments, so IT can elevate when needed without leaving admin rights attached to the accounts that open client attachments all day.
Behavioral detection on the endpoint carries more weight here than sandbox analysis. HollowFrame checked uptime, installed memory and cursor movement before running, which means a detonation in an automated sandbox can come back clean while the same sample executes fully on a paralegal's workstation. Tune your EDR policy to alert on the execution methods the framework offers — process ghosting, module stomping and manual PE mapping — all of which produce executable memory regions with no matching file on disk, or a mapped image whose in-memory code no longer matches the file it came from.
Make DLL search behavior something you audit on a schedule rather than discover during an incident. Use image-load telemetry to build an inventory of signed applications in your estate that load libraries from their own installation folder, keep that list as a watchlist for your detection rules, and re-run the inventory after major vendor updates change which imports an executable resolves at startup.
If your firm builds or maintains any internal applications, give the developers three requirements: call SetDefaultDllDirectories with LOAD_LIBRARY_SEARCH_SYSTEM32, load every dependency by absolute path instead of relying on the implicit search order, and code-sign the DLLs you ship. Add the same questions to your vendor review process, because a third-party application that resolves imports from its working directory becomes a sideloading host on every endpoint you install it on.
Sequence the work: DLL rules in audit mode, execution denials on user-writable paths, admin rights removal, then the developer and vendor changes at the next release cycle.
Key Takeaway: DLL Sideloading Remains a Critical Gap
HollowFrame did not exploit a vulnerability. Windows DLL search order worked exactly as designed, and the loader only needed a file in the right folder with the right name — which is why this technique reaches organizations of every size and patch level. There is no CVE to remediate here and no vendor fix pending.
That changes what you are looking for. Your patch program, however current, does not close this gap, because nothing in the chain was unpatched. The gap is in what your endpoint agent trusts by default: a signed process, a filename it expects, and a directory it has been told to ignore.
Two properties of HollowFrame make signature-first hunting unreliable in your environment. It offers several interchangeable execution methods — process ghosting, module stomping and manual PE mapping — so the same framework produces different artifacts on two machines in the same office. It also checks system uptime, installed memory and cursor movement before executing, meaning a sample pulled from one of your hosts may simply do nothing when you detonate it.
Weigh this by what it costs when it lands. A law firm lost two endpoints to a chain that inventoried domain controllers, domain computers, privileged group membership, network configuration and installed software — the reconnaissance set that precedes credential theft and lateral movement across a client matter environment.
For your organization, the fastest path to closure is DLL hardening and behavioral monitoring: judge what a process loads and how it behaves, since the filename and the signature on it will both look correct.