Threat reportMalwareTL-2026-5357
laZzzy Donut: Multi-Stage Loader Chain (Kramer-obfuscated Python, Donut shellcode, laZzzy loader)
TrustedSec (Scott Nusbaum) unpacked a six-stage loader chain: a Kramer-obfuscated Python 3.13 .pyc carrying RC4-encrypted shellcode, a first Donut layer, the open-source laZzzy AES-CBC/XOR shellcode loader, a second Donut layer, and a .NET DLL with encrypted resources that are loaded into memory as shellcode.
Key facts for TL-2026-5357
| Identifiers | TL-2026-5357 |
|---|---|
| Also known as | laZzzy Donut |
| Severity | MEDIUM |
| Exploitation status | Public proof-of-concept exploit |
| Affected products | Microsoft Windows (x64) |
| Malware | laZzzy |
| Tools | Donut - S0695, Kramer |
| MITRE ATT&CK techniques | T1027 Obfuscated Files or Information, T1027.009 Obfuscated Files or Information: Embedded Payloads, T1027.013 Obfuscated Files or Information: Encrypted/Encoded File, T1055 Process Injection, T1055.003 Process Injection: Thread Execution Hijacking, T1055.004 Process Injection: Asynchronous Procedure Call, T1059.006 Command and Scripting Interpreter: Python, T1106 Native API, T1134.004 Access Token Manipulation: Parent PID Spoofing, T1140 Deobfuscate/Decode Files or Information, T1553.002 Subvert Trust Controls: Code Signing, T1620 Reflective Code Loading and 1 more in the attack flow |
| Motivation | UNKNOWN |
| Detection rules | 9 (1 shown free) |
| Sources | 8 cited sources |
Detection coverage for TL-2026-5357
As of 2026-11-06, Threadlinqs Intelligence publishes 9 detection rule(s) for TL-2026-5357 across Splunk SPL, Microsoft KQL and Sigma, covering 9 indicator(s) of compromise. The whole corpus is readable without an account. Every threat comes with one complete detection in Splunk SPL, Microsoft KQL and Sigma, free for everyone; a free account adds daily TL Creds to unlock the rest. Paid plans add a larger daily allowance, correlation and the MCP server. Threadlinqs MCP server · View plans.
Free detection: laZzzy Donut Stage 1 - Python Interpreter Injecting Into Another Process
Detects python.exe, pythonw.exe or py.exe creating a remote thread (Sysmon EID 8) or opening another process with write/operation access rights (Sysmon EID 10). In the TL-2026-5357 laZzzy Donut chain, the Kramer-obfuscated Python 3.13 stage decrypts RC4 shellcode, runs a Donut layer, and the embedded laZzzy loader then injects its payload into other processes. A scripting interpreter writing into foreign processes is rare in normal operations and is a high-signal behavioural indicator that does not depend on file hashes, which the source does not publish.
- Languages: Splunk SPL, Microsoft KQL, Sigma
- ATT&CK: T1059.006, T1055, T1620
- Data sources: sysmon:EventID8, sysmon:EventID10, index=sysmon sourcetype=XmlWinEventLog:Microsoft-Windows-Sysmon/Operational
- Severity: high
- Confidence: high
Splunk SPL
index=sysmon sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" earliest=-24h (EventCode=8 OR EventCode=10) (SourceImage="*\\python.exe" OR SourceImage="*\\pythonw.exe" OR SourceImage="*\\py.exe") | eval is_hit=if(EventCode=="8" OR (EventCode=="10" AND match(GrantedAccess, "(?i)^0x(1fffff|1f3fff|1f0fff|143a|1438|1478|28|2a|3a|38|1028|102a)$")), 1, 0) | where is_hit=1 AND lower(SourceImage)!=lower(TargetImage) | eval threat="TL-2026-5357 laZzzy Donut Python stage-1 injection" | stats count min(_time) as first_seen max(_time) as last_seen values(EventCode) as event_codes values(GrantedAccess) as granted_access values(TargetImage) as injected_targets by host, SourceImage, threat | convert ctime(first_seen) ctime(last_seen)Microsoft KQL
// TL-2026-5357 laZzzy Donut - Python interpreter performing cross-process injection primitives (Defender XDR)
let lookback = 24h;
DeviceEvents
| where Timestamp > ago(lookback)
| where ActionType in ("CreateRemoteThreadApiCall", "NtAllocateVirtualMemoryRemoteApiCall", "WriteProcessMemoryApiCall", "QueueUserApcRemoteApiCall", "SetThreadContextRemoteApiCall", "NtMapViewOfSectionRemoteApiCall")
| where InitiatingProcessFileName in~ ("python.exe", "pythonw.exe", "py.exe")
| summarize ApiCalls = make_set(ActionType), TargetProcesses = make_set(FileName), EventCount = count(), FirstSeen = min(Timestamp), LastSeen = max(Timestamp) by DeviceName, InitiatingProcessFileName, InitiatingProcessFolderPath, InitiatingProcessCommandLine, InitiatingProcessAccountName
| order by LastSeen descSigma
title: Python Interpreter Creating Remote Thread (laZzzy Donut Stage-1 Injection)
id: 1a7c9e30-5f42-4b8d-a6e1-3d0f82b97c54
status: experimental
description: Detects python.exe, pythonw.exe or py.exe creating a thread in another process. In the TL-2026-5357 laZzzy Donut chain the Kramer-obfuscated Python stage runs Donut shellcode which hands off to the laZzzy loader for process injection.
references:
- https://trustedsec.com/blog/unpacking-a-lazzzy-donut
- https://github.com/capt-meelo/laZzzy
author: AII-Detector — ThreadLinqs Intelligence
date: 2026/11/06
tags:
- attack.execution
- attack.t1059.006
- attack.defense-evasion
- attack.privilege-escalation
- attack.t1055
- attack.t1620
- attack.s0695
logsource:
category: create_remote_thread
product: windows
detection:
selection:
SourceImage|endswith:
- '\python.exe'
- '\pythonw.exe'
- '\py.exe'
condition: selection
falsepositives:
- Python-based debuggers, instrumentation or automation frameworks that legitimately inject threads
level: highKnown false positives:
- Python-based debuggers, profilers or instrumentation frameworks (e.g. frida, pydevd) opening or injecting into other processes
- Developer workstations running Python automation that uses ctypes/pywin32 against other processes
Tuning notes: Change index=sysmon to your Sysmon index. Sysmon EID 8 and EID 10 must be enabled in the Sysmon config (EID 10 targeted at write-capable GrantedAccess values). Exclude known developer hosts and sanctioned debugger/target pairs via a lookup on SourceImage+TargetImage. Add pyinstaller-style renamed interpreters if you see them in your estate.
- laZzzy Donut Stage 1 - Python Executing Oversized .py From User-Writable Path (Kramer Bytecode Masquerade) · unlock with TL Creds
- laZzzy Donut Stage 4-6 - Unbacked In-Memory .NET Module Load or CLR in Python · unlock with TL Creds
- laZzzy Donut Stage 3 - Remote Injection API Chain (Early-bird APC / Thread Hijack / Section Mapping) · unlock with TL Creds
- laZzzy Donut Stage 1 - Python ctypes Shellcode Runner Command Line · unlock with TL Creds
- laZzzy Donut Stage 2/4 - AMSI or WLDP Library Loaded Into Python or Utility Host (Donut Bypass) · unlock with TL Creds
- laZzzy Donut Stage 3 - Sacrificial Windows Host Process Launched Without Arguments · unlock with TL Creds
- laZzzy Donut Stage 2/4 - CLR Loaded Into Python or Non-.NET Host Process · unlock with TL Creds
- laZzzy Donut Stage 3 - Process Access With Unbacked CallTrace (Direct-Syscall / Shellcode Injection) · unlock with TL Creds
Indicators of compromise for TL-2026-5357
As of 2026-11-06, Threadlinqs Intelligence tracks 9 indicators of compromise for TL-2026-5357.
How laZzzy Donut works
The write-up shows every layer can be unwound statically, and releases a new extraction tool, LazZzy_Dump.
TrustedSec's 'Unpacking a laZzzy Donut' (published 2026-09-17) walks through a layered loader in which each stage hides the next, so that no single signature or format-specific tool covers the whole chain. The source does not name an actor, campaign, victim, delivery vector or C2 server, and it does not state in-the-wild exploitation; severity is analyst-assigned.
Stage 1 is a Python 3.13 bytecode file (.pyc, magic number 3571, compile timestamp Wed Jun 24 06:18:29 2026 UTC, recorded .py size 8,371,083 bytes) obfuscated with the public Kramer tool (billythegoat356/Kramer). Kramer output is compiled bytecode rather than source. The analyst decompiled it with depyo and recovered the key with a modified kramer_python_deobfuscator.py, cutting a brute-force step from hours to under a minute. The recovered payload is Base64-encoded, RC4-encrypted shellcode, which the script copies into an executable memory section and runs.
Stage 2 is shellcode generated by TheWover's Donut, a position-independent shellcode generator. Volexity's donut_decryptor was used to extract the Donut instance and module. The module wraps a complete Windows PE built with laZzzy (capt-meelo/laZzzy), which is a full PE rather than shellcode, so Donut tooling alone could not unwrap it.
Stage 3 is the laZzzy loader. It AES-CBC-decrypts an embedded payload (ciphertext length 0x12a50) and applies an additional rolling 16-byte XOR layer. The upstream project is a Windows x64 shellcode loader offering ten execution techniques (Early-bird APC queue, thread hijacking, KernelCallbackTable, section view mapping, thread suspension, LineDDA callback, EnumSystemGeoID callback, FLS callback, SetTimer, clipboard), direct syscalls with Nt* functions, IAT evasion, XOR-encrypted strings, PPID spoofing, blocking of non-Microsoft-signed DLLs, and optional icon/metadata cloning and spoofed code signing. The article does not say which injection method this sample used. Because no tool handled this stage, the author wrote LazZzy_Dump (trustedsec/LazZzy_Dump), which byte-pattern scans for the compiled AESDecrypt routine, resolves RIP-relative LEA/MOV operands to recover the AES key, IV, XOR key and ciphertext, and decrypts statically without a debugger or emulator.
Stage 4 is a second Donut layer, again unwrapped with donut_decryptor, yielding a .NET DLL (stage 5; analyzable with dnSpy/ILSpy). In stage 6 the DLL's resources are encrypted; their sections are parsed, loaded into memory as shellcode, and control is transferred to them. Donut itself, per its documentation, uses Chaskey encryption, optional aPLib/LZNT1/Xpress compression, patches AMSI, WLDP and ETW, can overwrite PE headers, and hosts .NET assemblies by loading the CLR into a new AppDomain. These are properties of the tool, not behaviors the article reports for this specific sample.
Defensive value: detection should be layered across behavior and memory (Python spawning executable memory and injecting, CLR loading into unexpected processes, laZzzy-style syscall injection), and the compiled-artifact patterns LazZzy_Dump uses can support YARA/memory hunting. No network IOCs or sample hashes are published, so BeaconBeagle correlation was not applicable.
MITRE ATT&CK techniques by tactic:
Defense Evasion
T1027 Obfuscated Files or Information; T1027.009 Obfuscated Files or Information: Embedded Payloads; T1027.013 Obfuscated Files or Information: Encrypted/Encoded File; T1055 Process Injection; T1134.004 Access Token Manipulation: Parent PID Spoofing; T1140 Deobfuscate/Decode Files or Information; T1620 Reflective Code Loading
Privilege Escalation
T1055.003 Process Injection: Thread Execution Hijacking; T1055.004 Process Injection: Asynchronous Procedure Call
Execution
T1059.006 Command and Scripting Interpreter: Python; T1106 Native API
defense-impairment
T1553.002 Subvert Trust Controls: Code Signing; T1685 Disable or Modify Tools
Remediation for laZzzy Donut
Immediate actions
- Hunt for Python 3.13 .pyc files renamed to .py, especially multi-megabyte ones, and for python.exe/pythonw.exe allocating RWX memory or spawning/injecting into other processes
- Hunt for CLR (clr.dll/mscoree) loading into processes that do not normally host .NET, a Donut hallmark
- Scan memory and suspect PEs with YARA built from the laZzzy AESDecrypt byte pattern described by TrustedSec
Workarounds
- Use application allowlisting to block user-writable-path Python and unsigned PE execution
- Use LazZzy_Dump and donut-decryptor to unpack samples statically and extract IOCs
Longer-term hardening
- Deploy EDR with memory scanning and detection for direct-syscall process injection (APC queue, thread hijack, callback-based execution)
- Enable AMSI, ETW and script-block telemetry and alert on tampering
- Restrict execution of unsanctioned Python interpreters and unsigned binaries with application control
Affected and fixed versions
- Microsoft — Windows (x64)
Timeline of laZzzy Donut
- The stage-1 Kramer-obfuscated Python 3.13 .pyc carries a compile timestamp of Wed Jun 24 06:18:29 2026 UTC (header timestamp; attacker-controllable, not confirmed as first-use).
- TrustedSec states LazZzy_Dump was validated against multiple independently generated locked-ioc-7 samples, indicating the byte-pattern approach generalizes beyond the analyzed sample.
- TrustedSec reports that no public static extraction tool existed for the locked-ioc-7 encryption layer at publication, motivating LazZzy_Dump.
- Article shows both locked-ioc-9 layers unwrapped with Volexity donut_decryptor and the final .NET DLL with encrypted resources analyzed in dnSpy/ILSpy, demonstrating the chain can be fully unpacked statically.
- Write-up describes a modified kramer_python_deobfuscator.py that reduces locked-ioc-8 key brute-forcing from hours to under a minute.
- TrustedSec presents LazZzy_Dump, a new static extractor that recovers locked-ioc-7 AES key, IV, XOR key and payload by byte-pattern signature and RIP-relative resolution.
- TrustedSec publishes 'Unpacking a locked-ioc-7 locked-ioc-9' by Scott Nusbaum, documenting the six-stage Python/locked-ioc-9/locked-ioc-7/.NET chain.
Sources cited for laZzzy Donut
- Binary Refinery kramer unit (Kramer deobfuscation) binref.github.io · Research
- donut-decryptor on PyPI pypi.org · Research
- capt-meelo/laZzzy - shellcode loader GitHub · Code repository
- trustedsec/LazZzy_Dump - static laZzzy payload extractor GitHub · Code repository
- billythegoat356/Kramer - Python obfuscator GitHub · Code repository
- TheWover/donut - position-independent shellcode generator GitHub · Code repository
- volexity/donut-decryptor - Donut config and module extractor GitHub · Code repository
- Unpacking a laZzzy Donut (Scott Nusbaum, TrustedSec) trustedsec.com · Blog · 2026-09-17
Drafted by the Threadlinqs research pipeline from 8 sources; published ; last updated —.