typeanalysisfamilylummastealerconfidencehighcreated2026-07-27updated2026-07-27infostealermalware-familygolangsigningobfuscationc2pe
SHA-256: 7b74bea75be45d0a798732cdb54674811c207a3b118960c2146e9e97fb35c94b

lummastealer: 7b74bea7 — Go 1.25.4 PE32, Let's Encrypt R12-signed, PRNG C2 decoder

Executive Summary

Fifth confirmed sibling in the lummastealer cluster. A Go 1.25.4 PE32 infostealer signed with a Let's Encrypt R12 certificate for blizzard-tecnica.com. Unlike sibling 90d54589, this sample does not carry the custom in-memory PE parser or multi-pass byte-transform decoder — it is a "standard" variant closer to sibling 040e0d76. C2 endpoints are runtime-resolved via a PRNG-seeded transform, leaving no hardcoded URLs in static strings. Static-only analysis (CAPE skipped — no Windows guest).

What It Is

  • SHA-256: 7b74bea75be45d0a798732cdb54674811c207a3b118960c2146e9e97fb35c94b
  • File type: PE32 executable (GUI) Intel 80386, 7 sections ^[file.txt]
  • Size: 2,378,376 bytes
  • Compiler: Go 1.25.4+ (GOARCH=386, GOOS=windows, CGO_ENABLED=0, -trimpath=true) ^[strings.txt:8] ^[rabin2-info.txt]
  • Build ID: p1o2BE1pQM-KyBn_mLBv/RZ0PZaOxvnbC6oqaRPaO/QGtrpmsU15wchfFoESH3/O3-4tHI9u4sxJ8Y8mi_4 ^[strings.txt:8]
  • Signing: Authenticode PKCS#7 (WIN_CERTIFICATE type 0x0002) at raw offset 0x244200. Certificate chain: CN=blizzard-tecnica.com, issuer CN=R12, validity Apr 27 10:54:10 2026 GMT → Jul 26 10:54:09 2026 GMT (90-day Let's Encrypt R12). ^[openssl x509 output from /tmp/cert.der]
  • Resources: .rsrc section present with 4 × RT_ICON entries (builder icon-toggle enabled) and RT_GROUP_ICON. No RT_VERSION. ^[pefile.txt] ^[binwalk.txt]
  • Timestamp: Zeroed (Thu Jan 1 00:00:00 1970 UTC), consistent with reproducible Go builds. ^[pefile.txt]

Family ascription: High-confidence lummastealer. Shared with siblings 040e0d76 and 90d54589: identical certificate chain, identical build stack, identical randomized-naming pattern. Delta from 90d54589: no custom in-memory PE parser, no multi-pass decoder — this is the lighter build variant.

How It Works

Standard Go runtime.main → sym.main.main entry. main.main seeds math/rand with hardcoded 64-bit constants (0xd7b17f80 / 0xd) and calls math/rand.Intn with bounds 0x1868f and 0x320, setting up a PRNG state used later for C2 decoding. ^[r2:sym.main.main @ 0x48cf90]

The heavy lifting happens in sym.main.xnxcdxjep (~830 KB stack frame), which:

  1. Copies a large embedded table from .rdata (0x4cc85c) to the stack.
  2. Calls sym.main.tlpyxxxjgyppsej (table init / decoder setup) and sym.main.fvylialhnhatwu (likely decryptor).
  3. Allocates RWX memory via sym.main.nvogeuyt → VirtualAlloc with PAGE_EXECUTE_READWRITE (0x3000, 0x40). ^[r2:sym.main.xnxcdxjep @ 0x48b0ec]
  4. Copies decoded payload into the RWX region via runtime.memmove.
  5. Transfers control through sym.main.fajcijbth → syscall.Syscall with arguments (1, 2, 3, 4) — a stub or dispatcher for the final payload. ^[r2:sym.main.xnxcdxjep @ 0x48b33d]

sym.main.tqyxuxut (called from xnxcdxjep) performs a byte-transform with switch-case dispatch (types 0, 1, 2, 3, 0xa) and multiple panicIndexU / panicunsafeslicelen guards — a multi-pass decoder similar in spirit to, but simpler than, the one in sibling 90d54589. ^[r2:sym.main.tqyxuxut @ 0x4890c0]

floss.txt and capa.txt both failed during triage (command-line error and missing signatures directory, respectively). No CAPE detonation was performed (no Windows guest). All behavior above is inferred from static reverse engineering.

Decompiled Behavior

Entry point: 0x00472560 → sym._rt0_386 → runtime.main → sym.main.main. ^[r2:entry0 @ 0x472560]

Notable functions:

  • sym.main.main (0x48cf90): PRNG seeding, calls sym.main.nvcolqmhqksrd then sym.main.xnxcdxjep. ^[r2:sym.main.main]
  • sym.main.xnxcdxjep (0x48afe0): Core orchestrator. Copies .rdata table, allocates RWX memory, decodes payload, and dispatches via syscall.Syscall. Float64 math with time.Now / time.UnixNano suggests time-derived key material or sleep-gated decoding. ^[r2:sym.main.xnxcdxjep]
  • sym.main.tqyxuxut (0x4890c0): Byte-transform decoder with 5-case switch and bounds-checked indexing. ^[r2:sym.main.tqyxuxut]
  • sym.main.nvogeuyt (0x489d80): Wrapper around VirtualAlloc (observed via xref analysis in xnxcdxjep). ^[r2:sym.main.xnxcdxjep xref to nvogeuyt]

Control-flow patterns:

  • Heavy use of runtime.morestack_noctxt prologue guards (standard Go).
  • Large local stack frames (>800 KB) in xnxcdxjep — unusual even for Go, indicating embedded lookup tables or buffers.
  • Float64 arithmetic chains using .rdata constants (0x3fe0000000000000, 0x4010000000000000, 0x4024000000000000, 0x4039000000000000, 0x404e000000000000, 0x4059000000000000, 0x4066800000000000, 0x4076800000000000, 0x408f400000000000, 0x40a7700000000000, 0x40e5d54000000000, 0x40f5180000000000) — PRNG transform multipliers. ^[r2:.rdata float constants]

C2 Infrastructure

  • No hardcoded C2 URLs recovered in static strings. C2 endpoints are runtime-decoded via the PRNG transform described above. ^[strings.txt full review]
  • Certificate CN: blizzard-tecnica.com — may indicate infrastructure overlap with staging or C2. Same domain observed in siblings 040e0d76 and 90d54589. ^[openssl x509 output]
  • Network APIs: net/http, crypto/tls, ws2_32.dll imports present in Go runtime strings, confirming HTTPS C2 client capability. ^[strings.txt:1542]

Interesting Tidbits

  • Same Let's Encrypt R12 certificate chain as siblings 040e0d76 and 90d54589; builder supports at least two certificate modes (self-signed www.sjabr.org and Let's Encrypt blizzard-tecnica.com). ^[entities/lummastealer.md]
  • .rsrc contains 4 embedded icons — builder has an icon-toggle option. Earlier siblings d5647efd and e03dd36f have no .rsrc. ^[pefile.txt]
  • Randomized main.* function names (tqyxuxut, fajcijbth, tlpyxxxjgyppsej, zriogdshhmos, fvylialhnhatwu, nvogeuyt, qgvkvuxcblhpk, ylvhvoawctsqsc, zpttqngnxhkp, aetoal, yibhsckbawuhvhd, ykhksuydh, heidsjj, ifnsjpkhngions, nvcolqmhqksrd, ktlhadobbokv, dyjnsq, wfgmyrdtqh, lopawdftbvrmz, iwbksuhi, bchrjhrpbjpoil, xnxcdxjep) — 12–22 character mixed-case alphanumeric, hindering symbol-based clustering. ^[strings.txt:5350-5377]
  • Randomized type names (Eiumwm, Vnogcjz, Wnzrdhoc, Efddfsoftp, Konucwswwgp, Lvmnukwlzlx, Nxxuvvovgjx, Xgvatabnmo, Aiokktsuqgoi, hlqnatrrazv, rfaybrdimqcxubk) — same pattern as sibling 040e0d76, confirming shared build pipeline. ^[strings.txt:840-1328]
  • main.xnxcdxjep copies 0x32900 bytes (208 KB) from .rdata to stack — likely the encoded payload or C2 parameter table. ^[r2:sym.main.xnxcdxjep @ 0x48aff8]
  • Standard Go static binary — no packer, no CLR, no Native AOT. .text entropy 6.20, .rdata entropy 7.21. ^[pefile.txt]

How To Mess With It (Homelab Replication)

Goal: Build a minimal Go PE32 that reproduces the static fingerprint (PRNG seeding + RWX alloc + no hardcoded C2 strings).

Toolchain: Go 1.25.4, Windows cross-compile from Linux:

GOARCH=386 GOOS=windows CGO_ENABLED=0 go build -trimpath -ldflags="-H windowsgui" -o repro.exe main.go

Working source snippet:

package main
import (
    "math/rand"
    "syscall"
    "time"
    "unsafe"
)
func main() {
    src := rand.NewSource(time.Now().UnixNano())
    r := rand.New(src)
    _ = r.Intn(0x1868f)
    _ = r.Intn(0x320)
    // RWX alloc
    kernel32 := syscall.MustLoadDLL("kernel32.dll")
    va := kernel32.MustFindProc("VirtualAlloc")
    addr, _, _ := va.Call(0, 0x10000, 0x3000, 0x40)
    // Copy decoded payload
    copy((*[0x10000]byte)(unsafe.Pointer(addr))[:], []byte{0x90, 0xC3})
    // Execute
    syscall.Syscall(addr, 0, 0, 0, 0)
}

Verification: Run strings repro.exe | grep -E 'math/rand|time\.Now|VirtualAlloc' — should hit all three. Compare section entropies to this sample's pefile.txt.

What you'll learn: How a trivial Go program produces a high-entropy .rdata and randomized main.* symbols when compiled with -trimpath, and why static string extraction fails against PRNG-driven C2.

Deployable Signatures

YARA Rule

rule LUMMASTEALER_Go1254_PRNG_RWX : infostealer {
    meta:
        description = "Lummastealer / ACR / OrderRe Go 1.25.4+ PE32 infostealer with PRNG C2 decoder and RWX alloc"
        author = "PacketPursuit"
        date = "2026-07-27"
        hash = "7b74bea75be45d0a798732cdb54674811c207a3b118960c2146e9e97fb35c94b"
        reference = "https://www.abuse.ch"
    strings:
        $go_build = "Go build ID:"
        $rand1 = "math/rand"
        $rand2 = "math_rand._Rand_.Float64"
        $rand3 = "math_rand._Rand_.Intn"
        $time_now = "time.Now"
        $time_unix = "time.Time.UnixNano"
        $va = "VirtualAlloc"
        $main_obf = /main\.[a-zA-Z]{12,22}/
        $blizzard = "blizzard-tecnica.com" ascii wide
        $r12 = "CN=R12"
    condition:
        uint16(0) == 0x5A4D and
        uint32(uint32(0x3C)+4) == 0x00004550 and
        ($go_build and $rand1 and $time_now and $va) and
        ($main_obf or $blizzard or $r12) and
        filesize > 1MB and filesize < 5MB
}

Behavioral Hunt Query (KQL / Microsoft Defender for Endpoint)

let go_build_ids = dynamic(["7b74bea75be45d0a798732cdb54674811c207a3b118960c2146e9e97fb35c94b"]);
DeviceEvents
| where SHA256 in (go_build_ids)
    or (InitiatingProcessFileName =~ "*.exe"
        and ActionType in ("CreateRemoteThread", "NtAllocateVirtualMemory")
        and InitiatingProcessCommandLine contains "Go build ID")
| summarize arg_max(Timestamp, *) by DeviceId, SHA256

IOC List

Type Value Note
SHA-256 7b74bea75be45d0a798732cdb54674811c207a3b118960c2146e9e97fb35c94b Sample
SHA-1 97635c3ec08bf49d3603de21890acc647fbf4f01 .text section
MD5 43e0ff6660304b032e088c47fc3ae0d4 .text section
ssdeep 49152:xE8UFgsoA86HD6dhe208dJtw6QA3bsGhKmH5:i7FgsoA86HD6d8VCwXAgGMmZ Full file
TLSH A8B56D11FCD794B6E0025732D8AB63FF6339AD064F335A97EA443E79B9362954C22309 Full file
Cert CN blizzard-tecnica.com Let's Encrypt R12
Cert issuer CN=R12 Let's Encrypt R12 intermediate
Cert validity 2026-04-27 to 2026-07-26 90-day DV
File size 2,378,376 bytes

Behavioral Fingerprint

This binary is a Go 1.25.4 PE32 GUI executable with a zeroed timestamp and high-entropy .rdata. On execution, it seeds a math/rand PRNG with hardcoded constants, performs float64 arithmetic transforms using embedded .rdata constants, and allocates an RWX memory region via VirtualAlloc. It copies a decoded payload into that region and dispatches execution through syscall.Syscall. No C2 URLs are present in the binary statically; endpoints are resolved at runtime via the PRNG transform. Network capability is via statically-linked net/http and crypto/tls. The binary is Authenticode-signed with a 90-day Let's Encrypt DV certificate for blizzard-tecnica.com.

Detection Signatures

Tool Result Notes
capa Failed — missing signatures directory. Please install capa signatures per https://github.com/mandiant/capa/blob/master/doc/installation.md ^[capa.txt]
floss Failed — command-line argument error (--no flag collision) ^[floss.txt]
yara PE_File_Generic only ^[yara.txt]
exiftool Win32 EXE, LinkerVersion 3.0, Subsystem Windows GUI ^[exiftool.json]

References

  • /intel/analyses/7b74bea75be45d0a798732cdb54674811c207a3b118960c2146e9e97fb35c94b.html (this page)
  • lummastealer — family entity page
  • golang-stealer-build-pattern — shared Go infostealer build artefacts
  • acrstealer — sibling cluster with identical toolchain
  • orderreshop — sibling cluster with identical toolchain
  • fused-string-api-decoding — runtime DLL+API string fusion technique
  • prng-seeded-c2-url-decoding — PRNG C2 decoding technique
  • OpenCTI artifact: b796d2a3-429a-46d3-bf14-d637a252de65

Provenance

  • file.txt — file v5.44
  • exiftool.json — ExifTool v12.76
  • pefile.txt — pefile (Python) + custom section-hash script
  • strings.txt — strings -n 6 (binutils)
  • rabin2-info.txt — radare2 v5.x
  • binwalk.txt — Binwalk v2.3.4
  • Decompilation — radare2 pdg (level 3 analysis, 2039 functions recovered)
  • Certificate extraction — Python pefile + OpenSSL pkcs7 / x509
  • Static-only; no CAPE detonation (no Windows guest available).