typeanalysisfamilygoloaderconfidencehigh
SHA-256: 4e286cd901813a5f80411e417fb5defe25ff9af00706e68509392f6e75cc3908

goloader: 4e286cd9 — Go 1.25.1 reflective loader with AES-CBC encrypted payload in PE resource

Executive Summary

PE64 AMD64 binary compiled with Go 1.25.1. It is a reflective-code loader built on the legitimate github.com/pkujhd/goloader package, embedding an AES-CBC + zlib-encrypted second-stage Go object in a 912-byte PE resource. Eighteen randomized main.* function names suggest the payload, once decrypted, is dynamically linked in-memory via goloader rather than statically compiled into the outer binary. No network IOCs, registry persistence, or Authenticode signature were recovered in static strings.

What It Is

Trait Value
SHA-256 4e286cd901813a5f80411e417fb5defe25ff9af00706e68509392f6e75cc3908
File type PE32+ executable (GUI) x86-64, 9 sections^[file.txt]
Compiler Go 1.25.1^[strings.txt:9]
Build mode CGO_ENABLED=0, static, -trimpath implied (no absolute paths)^[strings.txt:5300-5305]
Module path goloader^[strings.txt:2045]
Build ID TJ7FBPtJdZI82i_gZMjm/59TZNuuZCej16dO3e6xW/qX4SXpL3Z1nCXw7RdXvx/ekT21_npJso311hGWjXF^[strings.txt:9]
Authenticode Unsigned^[pefile.txt]
Manifest vertexexpertleaf.service_decoder, version 4.0.32.156, description Discount Persona Guide^[pefile.txt:459-488]
Embedded payload PE resource id 16, RVA 0x1FB0A0, size 0x390 (912 bytes)^[pefile.txt:454-455]
Capa Failed — default signature path missing^[capa.txt]
YARA Generic PE hit only^[yara.txt]
Dynamic Skipped — no CAPE Windows guest available^[dynamic-analysis.md]

Family ascription: The triage pipeline tagged this as legionloader. No in-corpus precedent exists for legionloader, and no external intelligence was available to verify the link. The binary’s build artifacts point squarely to the open-source pkujhd/goloader project, a Go runtime linker that loads .o / .a object files into a running process. The family page is therefore created under goloader, not legionloader, with a note that the attribution remains contested.

How It Works

  1. Outer loader — A standard Go Windows AMD64 binary compiled with CGO_ENABLED=0 and likely -trimpath=true. On entry, the Go runtime initializes normally.

  2. Payload retrieval — At runtime the binary reads PE resource id 16 (offset 0x1FB0A0, 912 bytes)^[pefile.txt:454-455]. The xxd dump shows the resource body is high-entropy alphanumeric ASCII, consistent with an encrypted or encoded payload^[terminal:xxd resource 16].

  3. Decryption — The binary imports crypto/internal/fips140/aes including NewCBCDecrypter^[strings.txt:4864-4878], crypto/internal/fips140/sha256^[strings.txt:4924-4934], crypto/internal/fips140/sha3 (SHAKE, Digest)^[strings.txt:4942-4962], and crypto/internal/fips140/hmac (HMAC-SHA2-256)^[strings.txt:4975-4984]. AES-CBC is the most probable decryption layer. The AES S-Box and inverse S-Box are present in .rdata^[binwalk.txt:8-9].

  4. Decompression — After decryption, the payload is decompressed via compress/zlib and hash/adler32^[strings.txt:4841-4848, 4830-4840].

  5. Runtime linking — The decompressed bytes are passed to goloader.Read (from the pkujhd/goloader package) which parses a Go object / archive file. goloader.Load then resolves the object’s symbols against the host process’s symbol table and links them. The randomized main.* symbols — main.htg21w9j, main.j7kxray, main.h1eq17, main.ekfgcw, main.fsdya3m0r, main.rwnd58880, main.jtksgnv9cp, main.nwntqwqupe, main.iyz67zv, main.e4i55n1, main.cwnjg3, main.j16287wq, main.w8axetvs1y, main.o0oazr, main.adjfhfxv, main.l6pwhoovki^[strings.txt:4879-4900] — are the second-stage functions that goloader makes callable.

  6. Execution — The loader ultimately calls the linked module’s main.main entry (or equivalent) from main.main^[strings.txt:4880], effectively transferring control to the second stage without ever writing it to disk.

Decompiled Behavior

Static-only analysis; no Ghidra decompilation was attempted because the malicious logic lives inside an encrypted object file rather than the main .text section. The outer binary’s entry point is standard Go AMD64 PE startup: CPUID checks, GS segment register reads, and runtime.main dispatch. The noteworthy control flow is:

  • main.main calls into main.htg21w9j or similar randomized wrappers.
  • Those wrappers call goloader/loader.go routines (object parsing) and goloader/vm.go routines (virtual-machine-style symbol relocation).
  • The IAT is thin: kernel32.dll only — VirtualAlloc, CreateThread, SuspendThread, ResumeThread, SetThreadContext, GetThreadContext, WriteFile, LoadLibraryW, GetProcAddress^[strings.txt:5431-5463] and syscall.loadlibrary / syscall.getprocaddress^[strings.txt:3988-3989]. These APIs are available to the second stage for process injection if desired.

C2 Infrastructure

No hardcoded C2 URLs, IPs, DGA domains, or mutex names were found in 5,507 lines of static strings^[strings.txt]. net/http and net package references appear only as part of the Go runtime’s syscall table, not as non-runtime package imports. The actual C2 logic is presumed to exist inside the encrypted second-stage payload and is therefore invisible without dynamic execution or successful payload decryption.

Interesting Tidbits

  • Manifest masqueradevertexexpertleaf.service_decoder with description Discount Persona Guide is a random-word construction, not a known legitimate product^[pefile.txt].
  • No Authenticode — Unlike the ACR/Lumma/OrderRe Go infostealer cluster, this sample carries no signature, only a fabricated manifest^[pefile.txt].
  • No icon resource — The PE has no .rsrc icon section (only RT_MANIFEST and the encrypted blob in resource id 16)^[pefile.txt], another divergence from the typical Go-stealer build pattern documented at golang-stealer-build-pattern.
  • Go 1.25.1 — This is a bleeding-edge Go release (current stable at time of writing). Using the latest compiler may be an anti-sandbox measure (sandboxes running older Go runtime detection heuristics) or simply the builder’s default.
  • Binwalk false positive — Binwalk flagged mcrypt 2.2 encrypted data, algorithm: blowfish-448 at offset 0x18E6E3^[binwalk.txt:5]. This is almost certainly an entropy artifact; the actual crypto is AES-CBC as evidenced by the AES S-Box imports and NewCBCDecrypter string.
  • *main.z56lo5j — One type symbol survives obfuscation with a recognizable pattern. It may be an internal struct used by the loader or the second stage.

How To Mess With It (Homelab Replication)

Toolchain:

GOVERSION=go1.25.1
GOOS=windows
GOARCH=amd64
CGO_ENABLED=0

Build recipe:

  1. Create a Go module that imports github.com/pkujhd/goloader.
  2. Write your payload as a normal Go package with an exported func Main().
  3. Build the payload as an object file: go build -buildmode=c-archive or produce .o via go tool compile.
  4. Encrypt the object with AES-CBC (key derived via SHA-256 or HMAC), then zlib-compress it.
  5. Embed the blob as a PE resource using github.com/akavel/rsrc or windres.
  6. Write the loader: read resource → decrypt → decompress → goloader.Read(objBytes)goloader.Load(symPtr, module).
  7. Compile the loader: go build -trimpath -ldflags="-s -w".

Verification: Detonate in a sandbox. The outer binary should allocate a private memory region (VirtualAlloc), create no child processes, and make no network connections — all behavior is contained inside the in-memory linked module.

What you’ll learn: How to build a position-independent Go reflective loader that evades static string extraction of the payload.

Deployable Signatures

YARA Rule

rule GoLoader_Reflective_AES_Resource
{
    meta:
        description = "Go reflective loader using pkujhd/goloader with AES-CBC encrypted PE resource payload"
        author = "pp-hermes"
        date = "2026-06-08"
        family = "goloader"
        sha256 = "4e286cd901813a5f80411e417fb5defe25ff9af00706e68509392f6e75cc3908"

    strings:
        $goloader_loader = "goloader/loader.go"
        $goloader_vm = "goloader/vm.go"
        $goloader_exe = "goloader/exe_main.go"
        $aes_cbc = "crypto/cipher.NewCBCDecrypter"
        $aes_fips = "crypto/internal/fips140/aes"
        $zlib = "compress/zlib.NewReader"
        $adler = "hash/adler32"

        // AES S-Box presence in .rdata
        $aes_sbox_lo = { 63 7C 77 7B F2 6B 6F C5 30 01 67 2B FE D7 AB 76 }

        // Go build ID prefix
        $go_build = "Go build ID:"

    condition:
        uint16(0) == 0x5A4D and
        filesize < 5MB and
        3 of ($goloader_*) and
        $aes_cbc and
        $zlib and
        $go_build and
        // High-entropy PE resource (id 16 or 24) — heuristic
        // Prefer behavioural sigma for this
        1 of ($aes_sbox_lo)
}

Behavioral Hunt Query (Sigma)

title: Go Reflective Loader with VirtualAlloc + CreateThread Sequence
logsource:
    category: process_creation
    product: windows
detection:
    selection:
        CommandLine|contains:
            - '.exe'
        # Memory-allocation followed by thread creation in same process, short window
    condition: selection
    # EDR/Sysmon telemetry required: sequence of VirtualAlloc(RWX) then CreateThread in same PID within 5s
    # Sigma does not express sequences natively; deploy as EQL or Splunk SPL:
    # index=sysmon EventCode=7 OR EventCode=8 | stats dc(EventCode) as events by Image, ProcessId _time span=5s | where events>=2

IOC List

Type Value Note
SHA-256 4e286cd9... Outer loader
SHA-256 unknown Second-stage payload (encrypted, static-only)
Resource PE resource id 16, RVA 0x1FB0A0, size 0x390 Encrypted payload container
Manifest vertexexpertleaf.service_decoder Masquerade identity
C2 none observed Expected inside encrypted payload

Behavioral Fingerprint

A Go static PE64 compiled with CGO_ENABLED=0 and -trimpath=true that imports github.com/pkujhd/goloader package routines. The binary contains an AES S-Box in .rdata, imports crypto/cipher.NewCBCDecrypter, compress/zlib.NewReader, and hash/adler32. It has a high-entropy PE resource (id 16) and no .rsrc icon. On execution it calls VirtualAlloc followed by CreateThread with no preceding child-process creation or network connection — all malicious behavior is staged inside the in-memory linked module.

Detection Signatures

ATT&CK ID Technique Evidence
T1203 Execution goloader loads and executes Go code at runtime^[strings.txt:5301-5305]
T1620 Reflective Code Loading Encrypted payload decrypted in-memory, linked without file write^[pefile.txt:454-455]
T1027 Obfuscated Files or Information AES-CBC + zlib compression of embedded payload^[strings.txt:4864-4878,4841-4848]
T1083 File and Directory Discovery GetTempPathW, FindFirstFileW imports^[strings.txt:1971-1990]

References

  • pkujhd/goloader: https://github.com/pkujhd/goloader
  • Go 1.25.1 release notes: https://go.dev/doc/go1.25
  • Analysis report: /intel/analyses/4e286cd901813a5f80411e417fb5defe25ff9af00706e68509392f6e75cc3908.html

Provenance

Static analysis performed on 2026-06-08 using file, strings, rabin2, xxd, and manual review of pefile.txt, binwalk.txt, strings.txt, floss.txt, capa.txt, and yara.txt. CAPE detonation was not available (no Windows guest). No Ghidra decompilation was attempted because the malicious payload is encrypted and linked at runtime. All claims cite the specific artifact file and line number.