typeanalysisfamilycoinminerconfidencemediumcreated2026-05-26updated2026-05-26compilerpemalware-familycryptominerdefense-evasionpython-pyinstaller
SHA-256: 39b67a790b89fc8170703baaa98b29e1453a63416f0320bb3ae0f2936306f184

coinminer: 39b67a79 — PyInstaller bootloader sibling, 4.3 MB with 94% zlib overlay

Executive Summary

Cluster sibling of the 801fbba1 PyInstaller coinminer dropper. Same MSVC 2015 toolchain, same compilation second (Sep 4 2018 14:43:33 UTC), same bootloader, but with a 3.8 MB zlib-compressed overlay — roughly seven times larger than its sibling. The outer binary is pure PyInstaller infrastructure; the coinminer logic, pool configuration, and likely an entire Python runtime live inside the compressed CFFI archive. No mining indicators are visible without overlay extraction.

What It Is

  • File: PE32 executable (GUI) Intel 80386, 6 sections ^[file.txt]
  • Size: 4,259,320 bytes (4.06 MB) ^[rabin2-info.txt]
  • Compiled: Tue Sep 4 14:43:33 2018 UTC ^[pefile.txt:34] ^[rabin2-info.txt]
  • Linker: MSVC 14.0 (Visual Studio 2015 RTM). Rich header shows Utc1810_CPP(40116) x172, Utc1900_C x17, Utc1900_CPP x29, Masm1400 x18 — nearly identical fingerprint to 801fbba1 ^[rabin2-info.txt]
  • Signed: false; checksum 0x00000000 ^[rabin2-info.txt]
  • ASLR / DEP: enabled (DllCharacteristics: 0x8140) ^[pefile.txt:74]
  • Subsystem: Windows GUI ^[file.txt]
  • Overlay: 3,823,976 bytes (94.1% of file) starting at raw offset 0x3CE00, zlib-compressed PyInstaller CFFI archive ^[binwalk.txt]

How It Works

Standard PyInstaller single-file bootloader flow ^[ghidra:entry-004079d3] ^[ghidra:FUN_00401000-00401000] ^[ghidra:FUN_00402520-00402520]:

  1. CRT initialisationentry() calls __security_init_cookie() and __scrt_common_main_seh(), then reaches main() ^[ghidra:entry-004079d3]
  2. Archive resolutionmain() calls FUN_004049d0 to resolve the executable path, then hands control to FUN_00402520 ^[ghidra:FUN_00401000-00401000]
  3. ExtractionFUN_00402520 allocates an ARCHIVE_STATUS struct (0x4078 bytes), checks _MEIPASS2, locates the CFFI archive appended past the PE sections, and decompresses it to %TEMP%\_MEI<XXXX> ^[ghidra:FUN_00402520-00402520] ^[strings.txt:115] ^[strings.txt:235]
  4. Python runtime bootstrap — loads python*.dll via LoadLibraryExW, resolves CPython API procs (Py_Initialize, PyMarshal_ReadObjectFromString, PyEval_EvalCode, etc.) ^[strings.txt:119-212]
  5. Script execution — unmarshals the embedded Python code object and runs __main__.py ^[strings.txt:104-111]
  6. Cleanup — deletes the temp directory on exit unless _MEIPASS2 is set ^[strings.txt:115]

The 3.8 MB overlay suggests the embedded payload is not just a miner script but likely a bundled Python interpreter plus dependencies (e.g., base_library.zip, compiled .pyc modules). Mining pool URLs, wallet addresses, and stratum configuration are compressed inside this archive and not statically recoverable from the PE sections.

Decompiled Behaviour

  • entry (0x004079d3): MSVC CRT entry point. Sets up security cookie and SEH, then calls main(). ^[ghidra:entry-004079d3]
  • FUN_00401000 (main): Fetches the archive status struct and argv via helper functions, then calls FUN_00402520 with the resolved paths. ^[ghidra:FUN_00401000-00401000]
  • FUN_00402520 (PyInstaller bootstrap core): Allocates ARCHIVE_STATUS, checks _MEIPASS2 env var, opens self as archive, iterates TOC entries, extracts files to temp, calls SetDllDirectoryW to the _MEI folder, then launches the Python VM. ^[ghidra:FUN_00402520-00402520]

No anti-debug checks, no VM detection, no API hashing — behaviour is entirely that of a stock PyInstaller bootloader circa 2018.

C2 Infrastructure

Not statically observable. The outer binary contains only generic PyInstaller error strings and MSVC CRT locale strings. No hardcoded IPs, domains, pool URLs, or wallet addresses are present in the PE sections or imports. Pool/C2 configuration is assumed to reside inside the compressed Python payload in the overlay. ^[strings.txt]

Interesting Tidbits

  • floss.txt is a tool-usage error (the triage script passed the sample path to --no instead of sample), yielding no decoded strings. ^[floss.txt]
  • capa.txt failed with missing default signature path — signatures were never installed on this station. ^[capa.txt]
  • binwalk.txt identified 40+ zlib-compressed blocks and a PNG icon in .rsrc, confirming the overlay is a multi-member CFFI archive. ^[binwalk.txt]
  • The .rsrc section contains 7 icon groups (typical of PyInstaller default Python icon inheritance). ^[pefile.txt:159-492]
  • Identical compilation timestamp to 801fbba1 (down to the second) and nearly identical Rich header strongly suggests both binaries were produced by the same build pipeline or builder on the same machine. ^[pefile.txt:34] ^[rabin2-info.txt]
  • The c:/PyI partial path fragment at line 1090 of strings.txt hints the build environment had a PyI directory (PyInstaller source). ^[strings.txt:1090]
  • Overlay ratio of 94.1% makes this one of the largest PyInstaller coinminers observed in the corpus; the bulk of the file is compressed payload, not executable code.

How To Mess With It (Homelab Replication)

  • Toolchain: Install PyInstaller (pip install pyinstaller==3.4 for 2018-era behaviour) on a Windows research VM with Python 2.7 or 3.6.
  • Build a onefile payload: pyinstaller --onefile --windowed --name=miner_stub your_script.py
  • Extraction test: Run the stub, then inspect %TEMP% for a _MEI folder. Or use pyinstxtractor.py against the built EXE to dump the embedded archive.
  • Verification: strings on the output EXE should show _MEIPASS, PyInstaller, Py_Initialize, and zlib blocks in the overlay. Compare entropy and section layout to this sample.
  • Learning outcome: Recognising that the outer binary is benign infrastructure and the actual threat lives in the overlay prevents triage teams from stopping analysis after a benign-looking import table. The size delta between siblings (800 KB vs 4.3 MB) is a useful heuristic for payload richness.

Deployable Signatures

YARA rule

rule PyInstallerBootloader_LargeOverlay_Coinminer {
    meta:
        description = "PyInstaller single-file bootloader with oversized zlib-compressed overlay (coinminer sibling)"
        author = "Titus"
        date = "2026-05-26"
        sha256 = "39b67a790b89fc8170703baaa98b29e1453a63416f0320bb3ae0f2936306f184"
    strings:
        $pyi1 = "PyInstaller: FormatMessageW failed." ascii wide
        $pyi2 = "_MEIPASS2" ascii wide
        $pyi3 = "pyi-runtime-tmpdir" ascii wide
        $pyi4 = "Installing PYZ: Could not get sys.path" ascii wide
        $pyi5 = "Failed to execute script %s" ascii wide
        $pyi6 = "base_library.zip" ascii wide
        $inflate = "inflate 1.2.8 Copyright 1995-2013 Mark Adler" ascii wide
    condition:
        uint16(0) == 0x5a4d and
        4 of ($pyi*) and
        $inflate and
        filesize > 3MB
}

Sigma rule

Not suitable — the outer binary exhibits no unique process-level behaviour beyond generic PyInstaller extraction. Sigma should target the child process: python.exe or renamed miner executable spawned from %TEMP%\_MEI* within seconds of parent launch.

IOC list

  • SHA-256: 39b67a790b89fc8170703baaa98b29e1453a63416f0320bb3ae0f2936306f184
  • Temp path pattern: %TEMP%\_MEI*\* (PyInstaller extraction directory)
  • Mutex / named pipe: not observed in outer binary
  • Registry: not observed in outer binary
  • Compilation timestamp: 2018-09-04 14:43:33 UTC (cluster indicator)

Behavioural fingerprint

PE32 GUI executable compiled with MSVC 2015, containing a >3.8 MB zlib-compressed overlay (94% of file size). At runtime extracts its payload to a _MEI-prefixed temp directory, loads python*.dll from that directory, and executes embedded marshalled Python bytecode. No suspicious imports in the parent process; threat behaviour manifests in spawned Python/miner child processes. Sibling of 801fbba1 by build fingerprint.

Detection Signatures

  • MITRE ATT&CK
    • T1059.003 (Windows Command Shell) — possible batch/powershell launcher in embedded payload; not confirmed statically
    • T1059.006 (Python) — execution via embedded Python interpreter
    • T1074.001 (Data Staged: Local Data Staging) — extraction to temp directory
    • T1105 (Ingress Tool Transfer) — self-contained payload delivery
    • T1574.002 (DLL Side-Loading) — loading Python DLL from _MEI path
  • Capa: non-functional (missing signatures). No ATT&CK mapping available.

References

  • Artifact ID: 0141549b-5e24-4f8e-bbc5-f1b8dbd6da77 ^[metadata.json]
  • OpenCTI labels: coinminer, exe, urlhaus ^[metadata.json]
  • Cluster sibling: /intel/analyses/801fbba19b4d4828191e87e7311480deaf81e84482dab70adf38d61afd01c1fa.html
  • Related wiki: coinminer
  • Related concept: pyinstaller-bootloader
  • Related technique: python-packed-payload

Provenance

  • file.txtfile command (PE32 executable)
  • strings.txtstrings (7822 lines)
  • rabin2-info.txt — radare2 rabin2 -I (binary metadata + rich header)
  • pefile.txtpefile Python module (sections, imports, resources)
  • binwalk.txtbinwalk (embedded artifacts / zlib blocks)
  • exiftool.json — ExifTool PE metadata
  • floss.txt — flare-floss (tool argument error, no decoded output)
  • capa.txt — flare-capa (signature path error, no output)
  • triage.json — triage tier assignment
  • metadata.json — artifact metadata from OpenCTI
  • dynamic-analysis.md — CAPE status (skipped, no Windows guest)
  • Ghidra decompilation — pyghidra MCP (entry, FUN_00401000, FUN_00402520)
  • radare2 decompilation — radare2 MCP (entry0, main, fcn.00402520)