typeanalysisfamilymaskgramstealerconfidencemediumcreated2026-06-01updated2026-06-01peinfostealerclippermingwc2persistencediscoverycollection
SHA-256: abeaa63ba93f483c272b28670da91c5eee81aa8153b338da39e88504e6064e86

maskgramstealer: abeaa63b — MinGW-w64 PE64 infostealer with runtime API resolution and wallet-seed regex

Executive Summary

A 174 KB PE32+ x64 console binary, sibling to unclassified-pe64-clipper (af6e1f46). Same build fingerprint (MinGW-w64 binutils 2.41), same hardcoded Telegram user ID user_1779580244692, identical wallet regex, and shared screenshot-capture logic. Adds a custom in-memory PE parser + export-hash API resolver (FUN_004069c0) that dynamically resolves FindFirstFileW, FindNextFileW, and other imports at runtime via encoded name tables. Static-only — no CAPE detonation available.

What It Is

  • Format: PE32+ x86-64, 7 sections, stripped, unsigned, no exports ^[file.txt] ^[pefile.txt:32-39] ^[rabin2-info.txt]
  • Toolchain: MinGW-w64 GCC (LinkerVersion 2.41 / binutils 2.41) ^[pefile.txt:44-46] ^[rabin2-info.txt:17-18]
  • Timestamp: 2026-05-25 15:57:41 UTC — fabricated, ingested 2026-06-01 ^[pefile.txt:34]
  • IAT (static): KERNEL32.dll + USER32.dll only. Key file-system and injection APIs are runtime-resolved. ^[pefile.txt:228-309]
  • Notable static imports: CreateRemoteThread, WriteProcessMemory, VirtualAllocEx, VirtualProtectEx, OpenProcess ^[strings.txt:268-274]
  • Signing: None ^[rabin2-info.txt]

How It Works

Runtime API Resolution

  • FUN_004069c0 — custom in-memory PE export hash resolver. Parses the on-disk image of a target DLL, hashes export names with a FNV-1a-like routine (hash = hash * 0x21 + byte), then compares against a hardcoded 32-bit seed to locate the desired export RVA. Re-implements GetProcAddress without string equality. ^[ghidra:FUN_004069c0] ^[r2:fcn.004069c0]
  • FUN_00421b00 — string decryptor. XOR-like substitution using a 16-byte rolling key (0xd9 multiplier + 0x39 addend) followed by a second LUT pass. Decodes DLL and API names that are later fed to LoadLibraryA / GetProcAddress. ^[ghidra:FUN_00421b00]
  • FUN_004113f0 — initialization routine that loads KERNEL32.dll, decodes ~20 API names via FUN_00421b00, resolves them with GetProcAddress, and stores pointers in .bss globals (DAT_00426900, DAT_004267d0, etc.). Confirmed resolved APIs include FindFirstFileW, FindNextFileW, MultiByteToWideChar, WideCharToMultiByte, GetFileAttributesExW. ^[ghidra:FUN_004113f0]

Collection

  • Scans %DESKTOP%|%DOWNLOADS%|%DOCUMENTS% (and %MUSIC%|%VIDEOS%|%PICTURES%) for files matching *wallet*|*seed*|*mnemonic*|*phrase*|*backup*|*recovery*|*12words*|*24words*. ^[strings.txt:280] ^[strings.txt:285-291]
  • FUN_00411c30 splits the wallet regex on | and tests each pattern against discovered filenames. ^[ghidra:FUN_00411c30]
  • Captures screenshot as screenshot.jpg. ^[strings.txt:275]
  • Harvests MachineGuid from SOFTWARE\Microsoft\Cryptography. ^[strings.txt:105-109]
  • GetUserNameW for username enumeration. ^[strings.txt:293]
  • browser: and [BLOB: fragments suggest clipboard/browser data capture. ^[strings.txt:276-277]

Process Injection / Evasion

  • Static imports include the full cross-process injection toolkit: CreateRemoteThread, WriteProcessMemory, VirtualAllocEx, VirtualProtectEx, OpenProcess. ^[strings.txt:268-274]
  • No anti-VM, anti-debug, or packed sections. .text entropy normal for uncompressed native code. ^[pefile.txt:91-92,131-132]

C2 / Exfiltration

  • Hardcoded Telegram-style identifier user_1779580244692 referenced twice in .rdata. ^[strings.txt:278,294]
  • Chrome User-Agent string with runtime version substitution (%d.0.0.0). ^[strings.txt:283]
  • No static URL, IP, or bot token visible. C2 protocol inferred as Telegram Bot API or HTTPS POST.

Deploy / TTPs

Technique ID Evidence
Data from Local System T1005 %DESKTOP%, %DOWNLOADS%, %DOCUMENTS% enumeration ^[strings.txt:285-291]
Screen Capture T1113 screenshot.jpg ^[strings.txt:275]
Steal Crypto Wallet T1649 Wallet seed-phrase regex ^[strings.txt:280]
Process Injection T1055 CreateRemoteThread, WriteProcessMemory, VirtualAllocEx ^[strings.txt:268-274]
Application Layer Protocol: Web Protocols T1071.001 Chrome UA; Telegram user ID ^[strings.txt:283,278]
System Information Discovery T1082 MachineGuid, GetUserNameW, GetComputerNameA ^[strings.txt:105-109], ^[strings.txt:293]
Credentials in Files T1552.001 Wallet regex file scanning ^[strings.txt:280]
Clipboard Data T1115 browser: , [BLOB: fragments ^[strings.txt:276-277]

C2 Infrastructure

Indicator Value Notes
Telegram user ID user_1779580244692 Hardcoded ^[strings.txt:278,294]
User-Agent Mozilla/5.0 ... Chrome/%d.0.0.0 Safari/537.36 Hardcoded ^[strings.txt:283]
C2 URL/IP Not present Runtime-decoded or absent
Bot token Not present Not in static artifacts

Siblings & Attribution

  • unclassified-pe64-clipper (af6e1f4644b2e1e2a9c269d3acbd2faa1ea3facb9b68829c6f6a93a34ddb9c44) — direct sibling: same toolchain, same user_1779580244692, same wallet regex, same directory targets, same Chrome UA, same screenshot.jpg. Differences: abeaa63b adds the in-memory PE export-hash resolver (FUN_004069c0), whereas af6e1f46 resolves API names with plain GetProcAddress after XOR-loop decoding.
  • Family attribution: maskgramstealer (per triage pipeline label). Confidence medium — no open-source reporting on this name yet; cluster is defined by static overlap.

Interesting Tidbits

  • The export-hash resolver (FUN_004069c0) walks the PE Export Directory manually, hashes every name starting with Zw (kernel-mode prefix) to build a sorted lookup table, then binary-searches for the target hash. This is an unusual hybrid: it prepares a table of Zw* exports but the actual search is generic — any hash can be queried. Likely borrowed from a loader or shellcode template. ^[ghidra:FUN_004069c0]
  • entry0 starts by zeroing the .bss section with a rep-stosb-like loop (emulated via while zero-write) before calling CRT initializers. Clean startup, no obfuscation in the entry path itself. ^[r2:entry0]
  • No CAPE detonation. Windows guest unavailable at time of ingestion. All behavior inferred from static and decompilation.

How To Mess With It (Homelab Replication)

Toolchain: MinGW-w64 GCC 13.x, binutils 2.41, target x86_64-w64-mingw32, subsystem CONSOLE.

x86_64-w64-mingw32-gcc -O2 -s -static-libgcc -static-libstdc++ \
  stealer.c -o reproducer.exe -lws2_32

Verification: file reproducer.exePE32+ executable (console) x86-64 (stripped to external PDB).

Deployable Signatures

YARA — MaskgramStealer

rule MaskgramStealer : infostealer {
    meta:
        description = "Detects maskgramstealer family by wallet regex and Telegram user ID"
        author = "PacketPursuit"
        date = "2026-06-01"
        sha256 = "abeaa63ba93f483c272b28670da91c5eee81aa8153b338da39e88504e6064e86"
    strings:
        $wallet_regex = "*wallet*|*seed*|*mnemonic*|*phrase*|*backup*|*recovery*|*12words*|*24words*" ascii wide
        $telegram_id = "user_1779580244692" ascii wide
        $ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" ascii wide
        $screenshot = "screenshot.jpg" ascii wide
        $machineguid = "MachineGuid" ascii wide
    condition:
        uint16(0) == 0x5A4D and
        $wallet_regex and
        3 of them
}