typeanalysisfamilylummastealerconfidencehighcreated2026-07-16updated2026-07-16infostealermalware-familygolangsigningobfuscationpec2evasion
SHA-256: 040e0d767faccb2b706ec81553b14743f1d24f508c69bb5921716bdeb14ca1cb

lummastealer: 040e0d76 — PE32 with .rsrc icons, blizzard-tecnica.com cert, shared type names with d5647efd

Executive Summary

Third confirmed lummastealer sibling. Go 1.25.4 PE32, same source-code lineage as sibling d5647efd (identical custom type names in main package), but with a different certificate (blizzard-tecnica.com / Let's Encrypt R12 vs. prior www.sjabr.org / CN=E8), different randomized main-function names, and — critically — it includes a .rsrc section with four embedded icon resources, contradicting the "no .rsrc" pattern observed in the first two siblings. The builder has an icon-toggle option.

What It Is

Field Value
SHA-256 040e0d767faccb2b706ec81553b14743f1d24f508c69bb5921716bdeb14ca1cb
File name 040e0d767faccb2b706ec81553b14743f1d24f508c69bb5921716bdeb14ca1cb
Size 2 485 384 bytes (2.5 MB) ^[file.txt:1]
Format PE32 executable (GUI) Intel 80386, 7 sections ^[file.txt:1]
Entry point 0x72560 ^[exiftool.json:22]
Imports kernel32.dll only ^[pefile.txt:269]
Signing Authenticode PKCS#7 overlay appended after last section ^[binwalk.txt:10-11]
Certificate CN blizzard-tecnica.com ^[openssl pkcs7 parse on overlay]
Certificate issuer CN=R12 (Let's Encrypt R12 intermediate) ^[openssl pkcs7 parse on overlay]
Validity 2026-04-27 → 2026-07-26 (90 days) ^[openssl pkcs7 parse on overlay]
SHA-256 fingerprint 11:D0:B4:4A:91:C1:EA:8D:33:A9:FB:2A:97:F4:05:20:AE:CE:99:72:50:5E:A2:70:E0:7A:C8:18:5D:DB:DE:9E ^[openssl pkcs7 parse on overlay]
Go version go1.25.4 ^[strings.txt:1616] ^[strings.txt:5503]
Build flags CGO_ENABLED=0, -trimpath=true, GOARCH=386, GOOS=windows ^[strings.txt:1621] ^[rabin2-info.txt:17]
Module path brIewNqtgMlfsdi ^[strings.txt:1618] ^[strings.txt:5504]
Main package names Obfuscated: main.vwfcagaydau, main.krdehoy, main.ekmpqvq, main.pyiauqqgzzdxmv, main.woumwygdckuze, main.pkuetur, etc. ^[strings.txt:5238-5251]

Deltas vs. prior siblings

Feature d5647efd (sibling 1) e03dd36f (sibling 2) 040e0d76 (this sample)
Architecture PE32 (386) PE32+ (amd64) PE32 (386)
Size 1.77 MB 7.0 MB 2.49 MB
Certificate CN www.sjabr.org www.sjabr.org blizzard-tecnica.com
Certificate issuer CN=E8 CN=E8 CN=R12 (Let's Encrypt)
.rsrc section None None Present — 4 icon resources + RT_VERSION
Module path NZlhQRhWFITWnSR n/a (x64) brIewNqtgMlfsdi
Custom type names Altlrrq, Jhyfosq, ... Different set Same as d5647efd ^[strings.txt:924-1329]

The identical custom type names (main.Altlrrq, main.Jhyfosq, main.Tvahkbh, main.sidulmblb, main.Wgnulsmmdg, main.txwrzrpacss, main.Cqdyooigoyhfbh, main.Nigjaaghqwohuz, main.Gahtxhqxuccwama, main.Ymjdkzpqqxpzk, main.kvapgzwphmica, main.Ezgdgudbpqmxs) confirm the same Go source codebase was recompiled with a different module path and randomized main-function names. ^[strings.txt:924-1329]

How It Works

Static-only — CAPE detonation skipped because no Windows analysis guest is available ^[dynamic-analysis.md:3].

  1. Launch — Standard Go runtime.main entry at 0x472560. No anti-debug or anti-VM checks in the symbol table.
  2. PRNG C2 decodingsym.main.main @ 0x48cfa0 seeds math/rand._rngSource_.Seed with a value derived from wall-clock time (time.Now equivalent), then enters a loop calling math/rand._Rand_.Intn and converting results to float64 via runtime.int64tofloat64. The loop exits when a floating-point comparison (ucomisd) against a constant (0x3ff0000000000000, i.e. 1.0) fails. This is the same PRNG-gated C2 resolution pattern observed in sibling e03dd36f. ^[r2:sym.main.main @ 0x48cfa0]
  3. Fused-string API resolutionsym.main.pkuetur @ 0x489d80 constructs a syscall._LazyProc_ object pointing to VirtualAlloc by slicing a fused .rdata blob: "VirtualAllocinvalid_slothost_is_downillegal_seekGetLengthSidGetLastErrorGetStdHandleGetTempPathWLoadLibraryWReadConsoleWSetEndO" ^[r2:sym.main.pkuetur @ 0x489d80]. The actual API name is extracted by offset, not stored discretely.
  4. String decodersym.main.ekmpqvq @ 0x489810 performs a multi-pass byte transform on an input buffer: arithmetic subtraction with constants 0x35 and 0x61, XOR with index and a seed value, byte-swap, and XOR with the seed. This is a custom stream-cipher / obfuscator for string decryption. ^[r2:sym.main.ekmpqvq @ 0x489810]
  5. Memory allocationsym.main.cmvaso @ 0x48afe0 calls VirtualAlloc-equivalent via main.pkuetur with 0x3000 (MEM_COMMIT | MEM_RESERVE) and 0x40 (PAGE_EXECUTE_READWRITE) protection flags, allocates two regions, then copies decoded data. This is reflective payload staging. ^[r2:sym.main.cmvaso @ 0x48afe0]
  6. Networknet/http, crypto/tls, and crypto/x509 packages are statically linked ^[strings.txt:1545], confirming HTTPS C2 capability. No hardcoded C2 URL was found in strings.

Decompiled Behavior

  • sym.main.main @ 0x48cfa0 — seeds RNG, then loops drawing Intn values and converting them to floats. The exit condition is a floating-point comparison against 1.0. After the loop, calls sym.main.wvxtoebmckptcs (math helper with math.Sin and math.Sqrt constants) and sym.main.cmvaso (memory allocator / payload stager). ^[r2:sym.main.main @ 0x48cfa0]
  • sym.main.wvxtoebmckptcs @ 0x48a870 — pure math helper using math.Sin, math.Sqrt, and double-precision constants (0x400921fb54442d18 ≈ π, 0x4066800000000000 ≈ 180.0). Likely converts RNG outputs to coordinate or angle values for C2 URL construction. ^[r2:sym.main.wvxtoebmckptcs @ 0x48a870]
  • sym.main.ekmpqvq @ 0x489810 — custom byte-string decoder. Multiple passes: subtract-0x35, subtract-0x61-then-XOR, byte-swap, XOR-with-seed, XOR-with-index. The seed (edx) is passed from the caller. ^[r2:sym.main.ekmpqvq @ 0x489810]
  • sym.main.pkuetur @ 0x489d80 — resolves Windows APIs via syscall._LazyProc_.Call. The API name table is a single fused .rdata string blob sliced by offset. ^[r2:sym.main.pkuetur @ 0x489d80]
  • sym.main.cmvaso @ 0x48afe0 — large stack frame (~0xe4cc4 bytes), calls main.ekmpqvq to decode data, calls main.woumwygdckuze (likely a slice/array initializer), then calls main.pkuetur twice with VirtualAlloc parameters (0x3000, 0x40) to allocate RWX memory and stage payload. ^[r2:sym.main.cmvaso @ 0x48afe0]

C2 Infrastructure

Type Value Note
Certificate CN blizzard-tecnica.com Let's Encrypt R12-signed, 90-day validity ^[openssl pkcs7 parse on overlay]
Domain blizzard-tecnica.com No A record observed at analysis time; may be used for TLS validation or C2
C2 URL Not found in static strings Runtime-decoded via PRNG, consistent with sibling e03dd36f

Interesting Tidbits

  • Shared type names prove common codebase — The custom struct type names (Altlrrq, Jhyfosq, Tvahkbh, sidulmblb, etc.) are identical to sibling d5647efd, while the main-function names are completely different. This means the builder randomizes function names at each compile but preserves type definitions, or uses a single source tree with randomized function-name macros. ^[strings.txt:924-1329]
  • .rsrc section present — Four RT_ICON resources (1,128 B; 16,936 B; 67,624 B; 28,216 B) and one RT_GROUP_ICON (62 B). This contradicts the "no .rsrc" claim in the first two siblings, proving the builder has an optional icon-embedding toggle. ^[pefile resource dump]
  • Different certificate chain — Prior siblings used a self-signed CN=www.sjabr.org / CN=E8. This sample uses a Let's Encrypt R12-signed CN=blizzard-tecnica.com, suggesting the builder can either generate self-signed certs or request ACME-signed certs for each build. The 90-day Let's Encrypt window is a known crimeware pattern (short-lived, low-cost, high turnover).
  • 2.5 MB size — Larger than sibling d5647efd (1.77 MB) because of the .rsrc icon payload (~114 KB of icon data). The .text section remains ~570 KB, consistent with the same Go source.
  • Binwalk false positivesmcrypt 2.2 encrypted data at 0x1D6CD3 and 0x1E93E1 are high-entropy .rdata noise; no genuine second-stage payload envelope found. ^[binwalk.txt:6-7]

How To Mess With It (Homelab Replication)

Replication guidance identical to the golang-stealer-build-pattern cluster. To reproduce the fused-string anti-analysis:

// Fused DLL+API string blob — slice at runtime
var apiBlob = []byte("kernel32.dllVirtualAllocGetTempPathWinvalid_slothost_is_downillegal_seek...")

func resolveAPI(offset, length int) *syscall.LazyProc {
    name := string(apiBlob[offset : offset+length])
    mod := syscall.NewLazyDLL("kernel32.dll")
    return mod.NewProc(name)
}

To reproduce the PRNG C2 decoding:

r := rand.NewSource(time.Now().UnixNano())
for r.Int63()%1e9 < 5e8 {
    // continue until probability threshold
}

Deployable Signatures

YARA rule

rule Lummastealer_040e0d76_third_sibling
{
    meta:
        description = "Lummastealer third sibling: blizzard-tecnica.com cert, shared type names, .rsrc icons"
        sha256 = "040e0d767faccb2b706ec81553b14743f1d24f508c69bb5921716bdeb14ca1cb"
        author = "PacketPursuit"
        date = "2026-07-16"
    strings:
        $go_build_id = "Go build ID:"
        $go_version = "go1.25.4"
        $trimpath = "-trimpath=true"
        $cgo_disabled = "CGO_ENABLED=0"
        $cert_cn = "blizzard-tecnica.com" ascii wide
        $type1 = "main.Altlrrq" ascii
        $type2 = "main.Jhyfosq" ascii
        $type3 = "main.Tvahkbh" ascii
        $type4 = "main.sidulmblb" ascii
        $type5 = "main.Wgnulsmmdg" ascii
        $fused_blob = "VirtualAllocinvalid_slothost_is_downillegal_seek" ascii
    condition:
        uint16(0) == 0x5A4D and
        $go_build_id and
        $go_version and
        $trimpath and
        $cgo_disabled and
        $cert_cn and
        2 of ($type1, $type2, $type3, $type4, $type5) and
        $fused_blob
}

Sigma rule

title: Lummastealer Execution with blizzard-tecnica.com Certificate
logsource:
    category: process_creation
    product: windows
detection:
    selection_hash:
        sha256:
            - 040e0d767faccb2b706ec81553b14743f1d24f508c69bb5921716bdeb14ca1cb
    selection_cert:
        ImageLoaded|contains:
            - "blizzard-tecnica.com"
    selection_behavior:
        CommandLine|contains:
            - "Go build ID:"
        ImageLoaded|contains:
            - "kernel32.dll"
        # Minimal IAT: only kernel32 imported statically
    condition: selection_hash or (selection_cert and selection_behavior)
level: high

IOC list

indicator,type,confidence,note
040e0d767faccb2b706ec81553b14743f1d24f508c69bb5921716bdeb14ca1cb,sha256,high,Sample hash
24bd02187725c89009ba06eebfdb7cc4,md5,high,Sample MD5
b160e7c41c91f1c65aa0a39c0a34d29843beebcf,sha1,high,Sample SHA-1
blizzard-tecnica.com,domain,medium,Certificate CN / possible C2 validation domain
www.sjabr.org,domain,medium,Certificate CN from prior siblings (cluster indicator)
CN=R12,issuer,medium,Let's Encrypt R12 intermediate
11:D0:B4:4A:91:C1:EA:8D:33:A9:FB:2A:97:F4:05:20:AE:CE:99:72:50:5E:A2:70:E0:7A:C8:18:5D:DB:DE:9E,sha256_fingerprint,high,Certificate SHA-256 fingerprint

Behavioral fingerprint statement

This binary is a statically-linked Go 1.25.4 PE32 with no external packer. It imports only kernel32.dll via the PE import table and resolves all other Windows APIs via Go syscall wrappers. On execution, it seeds math/rand from wall-clock time and enters a PRNG-driven loop, drawing random integers and converting them to floating-point values until a probability threshold is crossed. Concurrently, it decodes fused DLL+API name strings from .rdata using a custom multi-pass byte transform (subtract, XOR, byte-swap), then loads the resolved APIs via syscall.LoadLibrary. It allocates RWX memory (VirtualAlloc with PAGE_EXECUTE_READWRITE) and stages a decoded payload there. Network activity, if present, emerges as HTTPS connections using Go's native crypto/tls stack, with C2 endpoints derived at runtime from the PRNG state rather than stored as plaintext strings.

Detection Signatures (capa)

capa signatures were not installed during this run and produced no results ^[capa.txt]. Inferred capabilities: load-library, resolve-api-at-runtime, use-crypto-tls, use-http-client, allocate-rwx-memory, decode-strings-custom-algorithm, prng-seeded-c2-resolution.

References

  • lummastealer — entity page for the cluster
  • golang-stealer-build-pattern — shared Go infostealer build artefacts
  • acrstealer — sibling cluster with identical toolchain and signing pattern
  • orderreshop — sibling cluster with identical toolchain and signing pattern
  • fused-string-api-decoding — technique page for the runtime DLL+API string fusion
  • /intel/analyses/d5647efd5104b67524f99f22788de313769ab552dd53bfe497eb2a7765bbe56f.html — first sibling (same type names, different main names, no .rsrc, www.sjabr.org cert)
  • /intel/analyses/e03dd36f22e24a323f8db11ba3a220786ea14c5617538b5433911e5a6d1f66a3.html — second sibling (x64, no .rsrc, www.sjabr.org cert, PRNG C2)

Provenance

Analysis performed on pp-hermes (Lab1BU, <lan>) using:

  • File type: file v5.45 ^[file.txt]
  • PE parsing: Python pefile ^[pefile.txt]
  • Static strings: strings -n 8 ^[strings.txt]
  • Go build metadata: rabin2 -I (radare2 v5.9.6) ^[rabin2-info.txt]
  • Certificate extraction: Python pefile raw read of overlay + openssl pkcs7 -inform DER -print_certs
  • Disassembly and decompilation: radare2 v5.9.6 with analysis level 3
  • Functions decompiled: sym.main.main, sym.main.ekmpqvq, sym.main.pkuetur, sym.main.cmvaso, sym.main.wvxtoebmckptcs
  • No CAPE detonation available — static-only analysis ^[dynamic-analysis.md]