typeanalysisfamily9d2ca3confidencemediumpecompilersigningevasiongolangobfuscationc2infostealerdefense-evasion
SHA-256: 2914975816372d0dc79b777915f66955d312213ea036b84ff16ad5ab0bcfdd66

9d2ca3: 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

  1. PRNG seedingmain.main calls time.Now, extracts nanoseconds, and seeds math/rand. ^[r2:sym.main.main]
  2. Staged dispatchmain.main invokes 33 randomized main.* functions in strict sequence. Each performs opaque slice operations, XOR loops, or mutex-protected buffer growth before returning. ^[strings.txt:main.*]
  3. Junk delaymain.Wqmcsrfqooezj allocates a 100-element int slice, fills each with math_rand.Intn(1000), sums them, then loops 0x1000 iterations doing no-op increments. Standard sandbox timing evasion. ^[r2:sym.main.Wqmcsrfqooezj]
  4. Fused-string API decodingmain.Xjnzvbmn constructs a buffer from a fused .rdata blob (observed string fragment: VirtualAllocrandautoseedsweepWaiters...), slices it via main.uciyqnpcdd / main.qhybyctuf, and passes the result to main.sncqxmmgztyymei which resolves the API through syscall._LazyProc_.Call. ^[r2:sym.main.Xjnzvbmn] ^[r2:sym.main.sncqxmmgztyymei]
  5. Custom PE parser / export walkermain.fvcmychoeu contains loops over PE export tables: LoadLibraryGetProcAddress via index arithmetic and bounds-checked slice access. This is the same pattern documented for fused-string-api-decoding. ^[r2:sym.main.fvcmychoeu]
  6. System fingerprinting — After all staged calls, main.main collects os.hostname, GetCurrentProcessId, Getpagesize, accumulates a timing counter, and formats the result through a mangled fmt.Sprintf string. Consistent with host-fingerprinting / heartbeat construction. ^[r2:sym.main.main]
  7. 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 CDDA1164C88E40890E189788E7C9F32B matches sibling cc4aa789, confirming the same signer/pipeline. ^[pefile.txt:252-253]
  • .rdata is not a valid embedded PE — Despite 170 MZ hits and one PE\0\0 signature inside .rdata, none form a contiguous valid DOS+NT header set. The PE\0\0 at .rdata+0xa7f500 has invalid COFF fields (Machine 0xffff). .rdata is standard Go type metadata + string tables. ^[pefile.txt:98-115]
  • More randomized names than prior sibling — 39 unique main.* symbols vs 28 in a7b9f3dd, indicating builder drift or a larger code base. ^[strings.txt]
  • No .rsrc icon — Distinguishes this from ACR Stealer siblings that embed 256×256 PNG icons. ^[pefile.txt:246-247]
  • .symtab retained — The builder did not strip symbols (-s -w absent). This makes function names fully recoverable despite the randomization. ^[pefile.txt:218-235]
  • CAPE / capa / floss unavailablecapa signatures missing; floss command-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.exelang: go, subsys: Windows GUI
  • strings repro.exe | grep 'go1.25' → should hit
  • Real randomization of function names requires an obfuscator such as garble or 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 (module uyiUNvZdvAGQnhv)
  • /intel/analyses/cc4aa789cf0c80b32004b90be6be0ad80944ad85730c6095cc3ca29469059503.html — Third sibling sharing certificate serial (misattributed as 54e64e)
  • 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 .rdata and 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]