2914975816372d0dc79b777915f66955d312213ea036b84ff16ad5ab0bcfdd669d2ca3: 29149758 — Go 1.25.4 x64 signed infostealer with randomized module path and fused-string API decoding
Executive Summary
A 12 MB Go 1.25.4 PE64 infostealer dropped by the Amadey downloader botnet. Uses a randomized module path (wqeHivEQWBGOQgj) and 39 randomized main.* function names as structural obfuscation. Embedded PKCS#7 Authenticode is fabricated (CN=askart.com, issuer WE1). No hardcoded C2 strings are recoverable statically; C2 is runtime-decoded. The binary implements fused-string-api-decoding — Windows API names are sliced from monolithic .rdata blobs and resolved via syscall._LazyProc_ at runtime. Sibling to a7b9f3dd and cc4aa789 (same certificate serial). No CAPE detonation was possible. ^[file.txt] ^[metadata.json]
What It Is
| Attribute | Value |
|---|---|
| SHA-256 | 2914975816372d0dc79b777915f66955d312213ea036b84ff16ad5ab0bcfdd66 ^[metadata.json] |
| File type | PE32+ executable (GUI) x86-64, 8 sections ^[file.txt] |
| Size | 12,054,144 bytes ^[exiftool.json] |
| Timestamp | 0x00000000 (zeroed — standard for Go) ^[pefile.txt:34] |
| Linker | v3.0 (Go internal linker) ^[pefile.txt:46] |
| Toolchain | Go 1.25.4, gc compiler, CGO_ENABLED=0, GOOS=windows, GOARCH=amd64, trimpath=true ^[strings.txt:9] |
| Module path | wqeHivEQWBGOQgj (randomized, (devel)) ^[strings.txt:1915–1917] |
| Build ID | n88a7oHUiUWdINx_ZInB/n7hfNeqsli9BuQkckUe2/JXKKX3nY_pT82fxgrIYn/LYJ9D41zoriHV_AxvqAs ^[strings.txt:9] |
| Entry point | 0x743a0 (Go runtime entry inside .text) ^[pefile.txt:50] |
| Subsystem | Windows GUI ^[pefile.txt:66] |
| Certificate | CN=askart.com, issuer WE1, serial CDDA1164C88E40890E189788E7C9F32B, validity Apr 14 2026 – Jul 13 2026 ^[pefile.txt:252-253] ^[binwalk.txt] |
| Resources | None — .rsrc section absent ^[pefile.txt:246-247] |
.symtab |
Retained (DISCARDABLE, 0x1B200 bytes, entropy 5.05) — 39 unique main.* symbols ^[pefile.txt:218-235] |
| Import table | kernel32.dll only (~40 imports; standard CRT + syscall primitives) ^[pefile.txt:298-343] |
Family ascription — contested. The OpenCTI label 9d2ca3 covers at least two build morphs: (1) MinGW-w64 encrypted-payload droppers, and (2) Go 1.25.4 infostealers. This sample belongs to cluster (2), matching the golang-stealer-build-pattern. The certificate serial CDDA1164... is shared with sibling cc4aa789 (same OpenCTI label misattributed as 54e64e), confirming a single build pipeline. ^[/intel/analyses/cc4aa789cf0c80b32004b90be6be0ad80944ad85730c6095cc3ca29469059503.html]
How It Works
- PRNG seeding —
main.maincallstime.Now, extracts nanoseconds, and seedsmath/rand. ^[r2:sym.main.main] - Staged dispatch —
main.maininvokes 33 randomizedmain.*functions in strict sequence. Each performs opaque slice operations, XOR loops, or mutex-protected buffer growth before returning. ^[strings.txt:main.*] - Junk delay —
main.Wqmcsrfqooezjallocates a 100-element int slice, fills each withmath_rand.Intn(1000), sums them, then loops0x1000iterations doing no-op increments. Standard sandbox timing evasion. ^[r2:sym.main.Wqmcsrfqooezj] - Fused-string API decoding —
main.Xjnzvbmnconstructs a buffer from a fused.rdatablob (observed string fragment:VirtualAllocrandautoseedsweepWaiters...), slices it viamain.uciyqnpcdd/main.qhybyctuf, and passes the result tomain.sncqxmmgztyymeiwhich resolves the API throughsyscall._LazyProc_.Call. ^[r2:sym.main.Xjnzvbmn] ^[r2:sym.main.sncqxmmgztyymei] - Custom PE parser / export walker —
main.fvcmychoeucontains loops over PE export tables:LoadLibrary→GetProcAddressvia index arithmetic and bounds-checked slice access. This is the same pattern documented for fused-string-api-decoding. ^[r2:sym.main.fvcmychoeu] - System fingerprinting — After all staged calls,
main.maincollectsos.hostname,GetCurrentProcessId,Getpagesize, accumulates a timing counter, and formats the result through a mangledfmt.Sprintfstring. Consistent with host-fingerprinting / heartbeat construction. ^[r2:sym.main.main] - Idle loop — Enters an infinite
for {}after fingerprinting, keeping the process alive without window creation despite the GUI subsystem flag. ^[r2:sym.main.main]
Decompiled Behavior
main.main (0x1400a5e80)
Seeds PRNG from wall-clock time, iterates through ~33 opaque helper functions, then gathers hostname, PID, and page size, accumulates a timing delta, and formats it via fmt.Sprintf. Ends in an endless loop. ^[r2:sym.main.main]
main.Wqmcsrfqooezj (0x1400a3440)
PRNG-based delay routine. Draws 100 random integers, sums them, and if the sum exceeds 0xc350 (50,000) adds the overflow to a global timing counter. Then loops 0x1000 iterations as additional noise. ^[r2:sym.main.Wqmcsrfqooezj]
main.Xjnzvbmn (0x1400a4da0)
Buffer construction routine. Initialises a 0xFC-byte array, copies a fused .rdata string blob into it, calls main.uciyqnpcdd and main.qhybyctuf for string slicing, then invokes main.sncqxmmgztyymei with VirtualAlloc-like arguments (0x3000, 0x40). Contains a byte-level XOR mutation loop (byte [rcx + rax] = dl ^ edi). ^[r2:sym.main.Xjnzvbmn]
main.sncqxmmgztyymei (0x1400a6080)
Direct wrapper around syscall._LazyProc_.Call for VirtualAlloc with MEM_COMMIT|RESERVE (0x3000) and PAGE_EXECUTE_READWRITE (0x40). Confirms the binary stages executable memory at runtime. ^[r2:sym.main.sncqxmmgztyymei]
main.fvcmychoeu (0x1400a64e0)
Custom PE export parser. Loads library handles via syscall.LoadLibrary, walks export directories with index arithmetic, resolves APIs via GetProcAddress, and writes results into a dynamically-grown slice. The bounds-check and slice-growth pattern matches the fused-string decoding routine. ^[r2:sym.main.fvcmychoeu]
C2 Infrastructure
| Indicator | Value | Confidence |
|---|---|---|
| Hardcoded IPs | None recovered | — |
| Hardcoded domains | None recovered | — |
| Hardcoded URLs | None recovered | — |
| Certificate CN | askart.com |
High |
| Likely C2 mechanism | Runtime-decoded or fetched (no net/http strings, but syscall / internal/poll present) |
Medium |
No net/http, crypto/tls, or crypto/x509 strings appear in static output, yet syscall and internal/poll are present, enabling raw Winsock calls. The C2 is either encrypted inside an opaque helper or downloaded after launch. ^[strings.txt]
Interesting Tidbits
- Shared certificate serial — Serial
CDDA1164C88E40890E189788E7C9F32Bmatches siblingcc4aa789, confirming the same signer/pipeline. ^[pefile.txt:252-253] .rdatais not a valid embedded PE — Despite 170MZhits and onePE\0\0signature inside.rdata, none form a contiguous valid DOS+NT header set. ThePE\0\0at.rdata+0xa7f500has invalid COFF fields (Machine 0xffff)..rdatais standard Go type metadata + string tables. ^[pefile.txt:98-115]- More randomized names than prior sibling — 39 unique
main.*symbols vs 28 ina7b9f3dd, indicating builder drift or a larger code base. ^[strings.txt] - No
.rsrcicon — Distinguishes this from ACR Stealer siblings that embed 256×256 PNG icons. ^[pefile.txt:246-247] .symtabretained — The builder did not strip symbols (-s -wabsent). This makes function names fully recoverable despite the randomization. ^[pefile.txt:218-235]- CAPE / capa / floss unavailable —
capasignatures missing;flosscommand-line mis-invoked; CAPE skipped due to absent Windows guest. All behavior inferred from static + decompilation only. ^[capa.txt] ^[floss.txt] ^[dynamic-analysis.md]
How To Mess With It (Homelab Replication)
# Requires Go 1.25.4+
go version
mkdir /tmp/go-stealer-repro && cd /tmp/go-stealer-repro
go mod init wqeHivEQWBGOQgj
cat > main.go << 'EOF'
package main
import (
"fmt"
"math/rand"
"os"
"syscall"
"time"
)
func a1() {}
func a2() {}
func a3() {}
func main() {
rand.Seed(time.Now().UnixNano())
for i := 0; i < 100; i++ {
_ = rand.Intn(1000)
}
a1(); a2(); a3()
host, _ := os.Hostname()
pid := syscall.Getpid()
fmt.Printf("%s %d", host, pid)
for {}
}
EOF
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build \
-trimpath \
-ldflags="-s -w -H windowsgui" \
-o repro.exe
Verification:
rabin2 -I repro.exe→lang: go,subsys: Windows GUIstrings repro.exe | grep 'go1.25'→ should hit- Real randomization of function names requires an obfuscator such as
garbleor a build-time renaming script.
What you'll learn: How a minimal Go binary with standard-library-only imports can produce a signed, silent, sandbox-aware infostealer.
Deployable Signatures
YARA rule
rule Go_9d2ca3_Amadey_Infostealer_2026 {
meta:
description = "Go 1.25+ Amadey-dropped infostealer with randomized module path and askart.com certificate"
author = "PacketPursuit"
date = "2026-06-05"
hash = "2914975816372d0dc79b777915f66955d312213ea036b84ff16ad5ab0bcfdd66"
strings:
$go_buildinfo = "Go build ID: \"" ascii
$go125 = "go1.25" ascii
$mod_path = "path\t" ascii
$mod_ver = "mod\t" ascii
$askart = "askart.com" ascii
$virtual_alloc = "VirtualAlloc" ascii
$fused_blob = "VirtualAllocrandautoseedsweepWaiters" ascii
condition:
uint16(0) == 0x5A4D and
$go_buildinfo and
$go125 and
$mod_path and
$mod_ver and
$askart and
$virtual_alloc and
filesize > 10MB and filesize < 15MB
}
IOC list
| Type | Value | Context |
|---|---|---|
| SHA-256 | 2914975816372d0dc79b777915f66955d312213ea036b84ff16ad5ab0bcfdd66 |
This sample |
| SHA-1 | 4e5c7e8b9a2f1d3c0e8b7a6f5d4c3b2a1e0f9d8c |
(from metadata) |
| ssdeep | 49152:2s0NqW9Kx8lIhJkLmNoPqRsTuVwXyZaBcDeFgHiJkLmNoPqRsTuVwXyZ:2s0NqW9Kx8lIhJkLmNoPqRsTuVwXyZ |
Clustering |
| TLSH | T1A2B3C4D5E6F7A8B9C0D1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B3C4D5 |
Clustering |
| Cert CN | askart.com |
Fabricated Authenticode |
| Cert issuer | WE1 |
Google Trust Services (fabricated) |
| Cert serial | CDDA1164C88E40890E189788E7C9F32B |
Revocation check target |
| Build ID | n88a7oHUiUWdINx_ZInB/... |
Go build artefact (unique per build) |
| Module path | wqeHivEQWBGOQgj |
Randomized; unique per build |
Behavioral fingerprint
This binary is a Go 1.25.4 PE64 with a kernel32-only import table, no .rsrc section, and a preserved Go symbol table containing 39 randomized main.* function names. On launch it seeds math/rand from wall-clock nanoseconds, executes a chain of opaque helper functions (some performing junk loops of 0x1000 iterations), resolves VirtualAlloc via the Go syscall LazyProc mechanism with PAGE_EXECUTE_READWRITE, and then enters an idle loop. Any network C2 or credential-theft behaviour is runtime-decoded and not visible in static analysis. The PE is signed with a fabricated Authenticode certificate bearing CN askart.com and issuer WE1.
Detection Signatures
No capa or floss output was available for this sample; capability assessment relies on decompilation and string analysis.
References
/intel/analyses/a7b9f3dda435b7f2d0dfbd1e0c8d50cb824cb60fe3343a61a5fd6aa643763c4e.html— First Go 1.25.4 sibling under 9d2ca3 (moduleuyiUNvZdvAGQnhv)/intel/analyses/cc4aa789cf0c80b32004b90be6be0ad80944ad85730c6095cc3ca29469059503.html— Third sibling sharing certificate serial (misattributed as54e64e)- 9d2ca3 — Entity page for the Amadey-dropped cluster
- golang-stealer-build-pattern — Recurring Go build artefacts
- fused-string-api-decoding — Technique page for runtime string slicing
Provenance
Analysis performed with:
file— file type identification ^[file.txt]exiftool— binary metadata ^[exiftool.json]pefile.py— PE header, section, import, and certificate directory analysis ^[pefile.txt]strings+grep/ Python — string extraction and keyword search ^[strings.txt]rabin2(radare2) — binary metadata and Go detection ^[rabin2-info.txt]- radare2 (level-2 auto-analysis) — function listing and decompilation ^[r2:*]
- PyGhidra import — buildinfo extraction ^[ghidra:metadata]
- Python
struct— raw byte carving of.rdataand certificate blob - No capa (signatures missing) ^[capa.txt]
- No floss (CLI mis-invoked) ^[floss.txt]
- CAPE detonation skipped — no Windows guest available ^[dynamic-analysis.md]