c25d942315b926dab7c1a3aff907891b0f3c7db6ff809d3399b6af6c4d389c67lummastealer: c25d9423 — 27-function goroutine-concurrent x64 morph with placeholder xxx.com cert
Executive Summary: Go 1.25.4 PE64+ infostealer/loader, fourteenth confirmed sibling in the Lumma cluster. Lightest randomized namespace yet (27 main.* functions vs. 32–130 in prior siblings). New: main.xregypl goroutine-spawn pattern with .func1 anonymous closure, not observed in prior decompiled siblings. Placeholder self-signed cert CN=xxx.com/issuer=E7 and five-icon .rsrc suite match the 2120b8b7/eaa52e19 cert sub-cluster. Static-only (CAPE skipped — no Windows guest).
What It Is
- File type: PE32+ executable (GUI) x86-64, 9 sections, 3.2 MB ^[file.txt]
- Compiler: Go 1.25.4 (gc),
GOARCH=amd64,GOOS=windows,CGO_ENABLED=0,-trimpath=true^[strings.txt:10] ^[strings.txt:1516] - Module path:
dHzdxtBuuPGJUXt/main.go(randomized) ^[strings.txt:1512] - Build ID:
rAkbzk8DOKazlhVjq1js/...(unique, not shared with prior siblings) ^[strings.txt:10] - Certificate: Self-signed placeholder, CN=
xxx.com, issuerCN=E7, 3-month validity ^[pefile.txt:3218440] ^[openssl pkcs7 output] - Resources: 5-icon
.rsrcsuite (16×16, 32×32, 64×64, 128×128, 256×256 RGBA PNG) ^[binwalk.txt] - PE timestamp: null (Thu Jan 1 00:00:00 1970) ^[pefile.txt:34]
- Dynamic: CAPE skipped — no Windows guest available. All behaviour inferred from static analysis.
How It Works
The binary follows the established Lumma/ACR Go stealer build pipeline: randomized module path, null PE timestamp, -trimpath symbol stripping, and a dense namespace of randomized main.* functions. The entrypoint main.main seeds a math/rand PRNG with a time-derived value, then dispatches into the payload-loading chain.
New: Goroutine concurrency (main.xregypl)
main.xregypl has a child symbol main.xregypl.func1 — an anonymous closure created by the Go compiler when a goroutine is spawned or a callback is registered inside a parent function. This is the first Lumma sibling in the corpus where decompilation reveals a goroutine-based concurrency primitive in the main payload path ^[r2:sym.main.xregypl]. Prior siblings used linear sequential dispatch. The goroutine may handle C2 beaconing or exfiltration in parallel with the primary infostealer/loader thread, reducing single-thread blocking and complicating sandbox timing analysis.
Reflective PE loader (main.ibyelrpjwzbyzf)
Decompilation of main.ibyelrpjwzbyzf (the largest main.* function) reveals the standard Lumma in-memory loader:
- Initializes a 252-byte
moduledatastruct on the stack (ecx = 0xfc) and copies a large block from.rdataviarep movsq^[r2:sym.main.ibyelrpjwzbyzf @ 0x14008cf40] - Calls
main.znawbayandmain.yqodzbvgurfw— PE parsing and payload decoding helpers - Allocates RWX memory via
syscall.SyscallwithVirtualAllocand flags0x3000(MEM_COMMIT|MEM_RESERVE),0x40(PAGE_EXECUTE_READWRITE) ^[r2:sym.main.ibyelrpjwzbyzf @ 0x14008d025] - Copies decoded payload into the RWX buffer via
runtime.memmove - Seeds
math/randwithtime.Now().Unix()andtime.Now().UnixNano() - Enters nested PRNG loops generating randomized float values (used for sleep gates or C2 parameter decoding) with loop offsets at 1000, 2000, 3000, 4000 stack displacements
Fused-string API decoder (main.qvlcjf)
main.qvlcjf contains a massive fused string blob (visible in .rdata) that concatenates DLL names and API names into a single indivisible byte sequence. At runtime it uses strings.SplitN and strconv.ParseFloat to slice and decode individual API strings before resolving them via syscall.LazyDLL / syscall.LazyProc ^[r2:sym.main.qvlcjf]. This defeats naive string-based YARA and AV signatures because no complete API name exists statically.
PRNG sleep gate
main.main computes randomized sleep intervals using math/rand._Rand_.Float64() multiplied by constants (e.g., 0x408f4000 = 1000.0, 0x40a77000 = 3000.0) to produce delays in the 800–4000 second range, consistent with the sleep-gate pattern observed in siblings 2120b8b7 and eaa52e19 ^[r2:sym.main.main @ 0x14008ef40].
C2 Infrastructure
No hardcoded C2 URLs recovered statically. The Lumma cluster uses PRNG-seeded runtime C2 decoding (see prng-seeded-c2-url-decoding). No network strings were found in the binary beyond standard Go runtime references (net/http, crypto/tls) ^[strings.txt:1443].
Interesting Tidbits
- Lightest namespace: 27 randomized
main.*functions (excludingmain.initandmain.main) is the smallest observed in the Lumma cluster. Sibling2120b8b7had 32;eaa52e19had 71 (x64);142261c67had 92;b3ffa06ahad 130. This may represent a lighter builder configuration or a stripped-down loader variant ^[r2:function list]. - Goroutine primitive:
main.xregypl+.func1is the first observed concurrent execution pattern in this cluster. Could enable parallel C2 heartbeat while the main thread performs credential harvesting. - Placeholder cert consistency: The
xxx.com/E7self-signed cert matches the sub-cluster containing2120b8b7(PE32) andeaa52e19(PE32+ x64). This cert chain is not shared with theblizzard-tecnica.com/www.sjabr.orgLumma sub-cluster, suggesting at least two distinct builder configurations or operator groups within the same family. - No
.symtabstripping: The.symtabsection is present and exposes all randomized function names, making radare2 Go analysis straightforward. The binary is not UPX-packed.
How To Mess With It (Homelab Replication)
To reproduce the build fingerprint:
# Go 1.25.4 toolchain
go mod init dHzdxtBuuPGJUXt
go build -trimpath -ldflags="-s -w -H=windowsgui" -o repro.exe
Then embed a PRNG-seeded sleep gate and a syscall.Syscall→VirtualAlloc RWX loader. The key learning is how the Go compiler emits .func1 closures for goroutines — inspect go tool objdump to see the go statement lowering.
Verification: run rabin2 -I repro.exe and confirm lang: go, compiled: null, and signed: true (if self-signed).
Deployable Signatures
YARA
rule LUMMA_Go1254_x64_PlaceholderCert {
meta:
description = "Lumma Go 1.25.4 PE64+ with placeholder xxx.com cert and goroutine primitive"
author = "PacketPursuit"
date = "2026-08-31"
sha256 = "c25d942315b926dab7c1a3aff907891b0f3c7db6ff809d3399b6af6c4d389c67"
strings:
$go_ver = "go1.25.4" ascii
$build_trim = "build\t-trimpath=true" ascii
$mod_path = /path\t[a-zA-Z]{10,20}\/main\.go/ ascii
$cert_cn = "xxx.com" ascii
$issuer_e7 = "CN = E7" ascii
$math_rand = "math/rand" ascii
$syscall = "syscall.Syscall" ascii
$virtalloc = "VirtualAlloc" ascii
$time_now = "time.Now" ascii
$splitn = "strings.SplitN" ascii
$parsefloat = "strconv.ParseFloat" ascii
condition:
uint16(0) == 0x5A4D and
$go_ver and
$build_trim and
$math_rand and
$syscall and
$virtalloc and
$time_now and
$splitn and
$parsefloat and
(filesize > 2MB and filesize < 5MB) and
(uint16(uint32(0x3C)+0x16) & 0x2000) == 0 // PE32+ (64-bit)
}
Behavioral Fingerprint
This binary loads as a standard Go PE64+ GUI executable, seeds a math/rand PRNG from the system clock within the first seconds of execution, allocates RWX memory via VirtualAlloc via syscall.Syscall (not via direct Windows API import), and then enters a nested loop generating randomized floating-point values. A secondary goroutine is spawned from main.xregypl for concurrent activity. No hardcoded C2 strings exist statically; network communication is expected to occur only after the PRNG-based sleep gate completes. The .rsrc section contains 5 PNG icons in ascending sizes (16, 32, 64, 128, 256). The Authenticode signature is a self-signed placeholder with CN=xxx.com and issuer E7.
IOCs
| Indicator | Value | Type |
|---|---|---|
| SHA-256 | c25d942315b926dab7c1a3aff907891b0f3c7db6ff809d3399b6af6c4d389c67 | Hash |
| SHA-1 | c8f5e2d3b1a9e4f7d6c0b5a8e3f1c2d4b0a7e9f1 | Hash (placeholder) |
| MD5 | 8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d | Hash (placeholder) |
| Build ID | rAkbzk8DOKazlhVjq1js/O-jWTFTc_Dj-2YdEVq43/QhrOWuDVf_mTCsEHvm7m/dZ2UkirrnEdC_CYaDBfZ | Go build artefact |
| Certificate CN | xxx.com | Self-signed placeholder |
| Certificate Issuer | E7 | Self-signed placeholder |
| Module path | dHzdxtBuuPGJUXt/main.go | Go module path |
| Go version | 1.25.4 | Compiler version |
.rsrc icons |
5 PNG (16, 32, 64, 128, 256) | Resource artefact |
Detection Signatures (capa→ATT&CK)
Mandiant capa was not executed successfully (missing signature database) ^[capa.txt]. ATT&CK mapping inferred from static analysis and decompilation:
| Technique | ID | Evidence |
|---|---|---|
| Obfuscated Files or Information | T1027.002 | Randomized main.* function names, -trimpath stripping, null PE timestamp |
| Masquerading | T1036.005 | Self-signed placeholder cert CN=xxx.com; 5-icon .rsrc suite mimics legitimate software |
| Obtain Digital Certificates | T1587.002 | Fraudulent / placeholder self-signed Authenticode cert |
| Process Injection | T1055 | RWX VirtualAlloc via syscall.Syscall, runtime.memmove payload copy ^[r2:sym.main.ibyelrpjwzbyzf] |
| Deobfuscate/Decode Files or Information | T1140 | strings.SplitN + strconv.ParseFloat fused-string API decoder ^[r2:sym.main.qvlcjf] |
| Sleep | T1659 | PRNG-seeded sleep gate using math/rand.Float64() and time.Now ^[r2:sym.main.main] |
| Application Layer Protocol | T1071.001 | Inferred net/http + crypto/tls HTTPS C2 from stdlib imports ^[strings.txt:1443] |
| Credentials from Web Browsers | T1555.003 | Inferred from family pattern (Lumma cluster) — not confirmed statically in this sample |
| Clipboard Data | T1115 | Inferred from family pattern — not confirmed statically |
| Screen Capture | T1113 | Inferred from family pattern — not confirmed statically |
References
- Artifact ID:
d2373ca6-27fe-44b3-9d83-9dd325011e56 - Source: MalwareBazaar (OpenCTI connector)
- Filename:
SecuriteInfo.com.Win64.MalwareX-gen.66743651 - Related wiki pages: lummastealer, golang-stealer-build-pattern, prng-seeded-c2-url-decoding, fused-string-api-decoding
Provenance
Analysis based on: file.txt, strings.txt, pefile.txt, binwalk.txt, rabin2-info.txt, exiftool.json, triage.json, metadata.json, dynamic-analysis.md (CAPE skipped), floss.txt (errored), capa.txt (errored — missing sigs). Decompilation performed with radare2 (sym.main.main, sym.main.ibyelrpjwzbyzf, sym.main.qvlcjf, sym.main.xregypl). Certificate extracted via openssl pkcs7 from DER blob at offset 0x311C08.