typeanalysisfamilysilverfoxconfidencehighmalware-familyloaderratdefense-evasionc2obfuscationevasionpecode-injection
SHA-256: 139329dc9992e132f9c8d887ad685660161cefcfb0a18867d616a7d217a0605e

silverfox: 139329dc9 — MSVC C++ RC4 loader with zero section names and PEB-walking API resolution

Third observed SilverFox variant: MSVC C++ x64 (not Rust), 173 KB PE32+ with a standard RC4 (not custom stream cipher) encrypted .text payload, zero-named section anti-signature, and PEB-walking runtime API resolution. Chinese-language social engineering lure masquerades as an internal staff disciplinary list.

Executive Summary

A PE32+ Windows dropper compiled with MSVC 14.16 (Visual Studio 2017/2019) on 2025-12-05. The binary carries nine sections, all with empty names, zero static imports at load time, and a deliberately corrupt PE header (zeroed checksum, fabricated LOAD_CONFIG and debug directories). A small position-independent stub in the last executable section decrypts the main .text, .data, and import table at runtime via standard RC4, then walks the PEB to resolve LoadLibraryA, GetProcAddress, and VirtualProtect, and performs its own import table fix-up before transferring control to the decrypted payload. Co-tagged valleyrat by OpenCTI; CAPE detonation was unavailable (no Windows guest).

What It Is

Attribute Detail Evidence
SHA-256 139329dc9992... triage.json
File name Chinese-language internal staff disciplinary list lure triage.json
Type PE32+ executable (GUI) x86-64, 9 sections file.txt
Size 173 056 bytes triage.json
Compiler MSVC 14.16 (C/C++) exiftool.json:18, pefile.txt:49
Timestamp 2025-12-05 06:31:50 UTC rabin2-info.txt:11, pefile.txt:38
Stripped Partial (symbol info absent but sections intact) pefile.txt:39
Signed Unsigned rabin2-info.txt:27
Family silverfox / valleyrat / trojan/silverfox.bg[qtsc] metadata.json:8
CAPE Skipped — no Windows guest available dynamic-analysis.md

How It Works

Anti-analysis / build layer

The PE header is deliberately corrupted to frustrate automated parsers: checksum is zeroed, the LOAD_CONFIG directory holds impossible future timestamps (2056), and the debug directory is filled with garbage.^[pefile.txt:68,424,480] All nine section headers carry empty Name fields — a cheap anti-signature trick that breaks many tools that expect .text or .rsrc.^[pefile.txt:80] IMAGE_FILE_RELOCS_STRIPPED is set despite DllCharacteristics declaring HIGH_ENTROPY_VA.^[pefile.txt:43]

Encryption layer

The entry-point stub lives in the final executable section (sect_5, VA 0x2b000, EP at VA 0x2ca40).^[pefile.txt:54, rabin2-info.txt:9] It performs a three-stage decryption:

  1. Decrypt API strings — Five short strings (e.g. LoadLibraryA, GetProcAddress, VirtualProtect) are XOR-decrypted in-place using a length-dependent key (key_byte = (index % 0x6d) + 0x92) and a flag-bit gate that triggers the decryption only if the low bit of the last byte is set.^[ghidra:FUN_14002bd60, ghidra:FUN_14002bda0, ghidra:FUN_14002bde0, ghidra:FUN_14002be20]
  2. RC4 decrypt sections — The main code section (sect_0, 0x22C00 bytes, entropy 7.998), the key data section (sect_1), and the import/data section (sect_2) are decrypted with a 28-byte RC4 key derived from the decrypted API strings.^[ghidra:FUN_14002b360, ghidra:FUN_14002b470, r2:fcn.14002b8a0, r2:fcn.14002ba20, r2:fcn.14002bb10]
  3. Import fix-up — After decryption, fcn.14002bc00 walks the now-readable import directory, resolves every DLL/API pair via the previously resolved GetProcAddress / LoadLibraryA, and patches the IAT thunk entries in-memory.^[r2:fcn.14002bc00]

API resolution

The stub does not rely on the static IAT for its own operation. It resolves needed APIs at runtime via classic PEB-walking:

  • GS:[0x60]PEB.LdrInMemoryOrderModuleList → enumerate entries until ntdll.dll is found by string comparison.^[r2:fcn.14002b5c0, ghidra:FUN_14002b5c0]
  • Export table traversal on the matched module base (BaseDllName check at offset +0x58 into the LDR_DATA_TABLE_ENTRY).^[r2:fcn.14002b6c0]

Payload

Two strings in the final section suggest the decrypted payload is a DLL loaded from memory rather than a second-stage PE file on disk: Stub.dll at VA 0x14002c4b8 and g_stcParam at VA 0x14002c4cb.^[strings.txt:439-440, ghidra:search] The entry point of the decrypted main section is at offset 0x215B4 relative to the image base.^[pefile.txt:54]

Decompiled Behavior

Entry point (EP at VA 0x14002ca40) — Resides in the last executable section (sect_5, 0x1C00 bytes raw). The decompilation is heavy with opaque predicates and control-flow flattening, but the overall flow is reconstructible:

  1. FUN_14002b5c0 — PEB-walk to recover ntdll.dll base address.^[ghidra:FUN_14002b5c0]
  2. FUN_14002b6c0 — Walk ntdll exports to find GetProcAddress by comparing name hashes.^[r2:fcn.14002b6c0]
  3. FUN_14002bd60 / FUN_14002bda0 / FUN_14002bde0 — Populate encrypted string buffers for LoadLibraryA, VirtualProtect, and GetProcAddress.^[ghidra:FUN_14002bd60, ghidra:FUN_14002bda0, ghidra:FUN_14002bde0]
  4. FUN_14002be20 / FUN_14002bf40 — XOR-decrypt the above strings in-place using the loop byte = cipher[i] ^ ((i % 0x6d) + 0x92).^[ghidra:FUN_14002be20, ghidra:FUN_14002bf40]
  5. FUN_14002b8a0 — Allocate stack buffer, perform RC4 KSA (FUN_14002b360) on the 28-byte key, then RC4 PRGA (FUN_14002b470) to decrypt the 0x22C00-byte .text section in-place.^[r2:fcn.14002b8a0, r2:fcn.14002b360, r2:fcn.14002b470]
  6. FUN_14002ba20 — Same RC4 routine to decrypt sect_2 (data).^[r2:fcn.14002ba20]
  7. FUN_14002bb10 — Same RC4 routine to decrypt sect_1 (key / import strings).^[r2:fcn.14002bb10]
  8. FUN_14002bc00 — Walk decrypted import directory and fix-up every thunk.^[r2:fcn.14002bc00]
  9. Transfercall qword [image_base + 0x215B4] into the decrypted payload. Observed via xrefs in the flow.^[pefile.txt:54]

C2 Infrastructure

Static-only (no CAPE detonation). No hardcoded IPs, domains, URLs, mutexes, or named pipes were recovered from the plaintext strings or decrypted sections. The C2 mechanism lives inside the encrypted payload, which was not extracted in this analysis. The RC4 key is runtime-dependent on the decrypted API strings, making static payload extraction non-trivial without full emulation.

Interesting Tidbits

  • Empty section names: All nine IMAGE_SECTION_HEADER.Name fields are null-filled. This breaks many PE parsers and signature tools that expect .text, .rdata, etc.^[pefile.txt:80]
  • Corrupt metadata: LOAD_CONFIG and debug directories both contain impossible future dates and random data. The security directory is empty.^[pefile.txt:276,424,480]
  • Checksum zero: Despite being an executable on a modern Windows subsystem, the optional header checksum is explicitly zeroed.^[pefile.txt:68]
  • Social engineering in Chinese: The lure document title claims to be an internal list of "disciplinary violations of internal staff in Q3 2025", a classic pressure tactic in Chinese-language phishing.^[triage.json:5]
  • No standard capa output: Capa failed to run (missing signatures directory).^[capa.txt] Floss also failed due to incorrect invocation flags.^[floss.txt]
  • Stub.dll + g_stcParam: The presence of these two strings strongly implies the decrypted payload is expected to be a DLL with a global parameter structure — a pattern seen in other reflective DLL loaders.^[strings.txt:439-440]

How To Mess With It (Homelab Replication)

Goal: Produce a minimal PE with encrypted .text and PEB-walking API resolution.

  1. Toolchain: MSVC 2019 (v142) or MSVC 2022 (v143), x64 Release, /O2 /GS-.
  2. PE layout: Create a PE with two extra sections after .text: .key (holds the RC4 key) and .stub (holds the decryptor stub). Mark .text as MEM_READ | MEM_WRITE so the stub can decrypt it in-place.
  3. RC4 key: Pick any 28-byte string; store it in .key. In the stub, copy the key to the stack and run standard KSA + PRGA over .text.
  4. PEB walking: Use gs:[0x60] chain as documented at peb-walking-api-resolution. Resolve GetProcAddress and LoadLibraryA by walking ntdll exports.
  5. Import fix-up: After decryption, parse the now-readable import directory and patch thunks with resolved addresses.
  6. Anti-signature: Zero all section names in the header and corrupt the checksum / debug timestamps.
  7. Verification: Run your reproducer in a debugger, set a breakpoint on the decrypted payload entry-point, and confirm execution flows into decrypted code.

Deployable Signatures

YARA

rule silverfox_rc4_loader_2025 {
    meta:
        family = "silverfox"
        description = "SilverFox RC4 loader: MSVC C++ x64, zero section names, encrypted sections, Stub.dll reference"
        author = "PacketPursuit SOC"
        date = "2026-06-04"
        sha256 = "139329dc9992e132f9c8d887ad685660161cefcfb0a18867d616a7d217a0605e"
    strings:
        $stub_dll = "Stub.dll" ascii wide nocase
        $g_stcParam = "g_stcParam" ascii wide nocase
        $pcnypr = "pcnypr" ascii wide nocase
    condition:
        uint16(0) == 0x5a4d and
        pe.number_of_sections == 9 and
        for all i in (0 .. pe.number_of_sections - 1) : (
            pe.sections[i].name == ""
        ) and
        (
            $stub_dll or
            $g_stcParam or
            $pcnypr
        ) and
        pe.sections[0].entropy > 7.8 and
        filesize < 500KB
}

Behavioral fingerprint

This binary has zero section names, zero checksum, and impossible future LOAD_CONFIG/debug timestamps. On execution, a small stub in the final section walks the PEB (gs:[0x60] → Ldr → InMemoryOrderModuleList) to resolve GetProcAddress, then decrypts three RC4-encrypted sections (the first with entropy >7.9) using a 28-byte key derived from XOR-obfuscated API strings. It then walks its own import directory, resolving every API via the dynamically obtained resolver, and finally jumps to offset 0x215B4 of the decrypted .text. No disk write of the payload is expected; the DLL is prepared purely in memory.

IOC list

Indicator Type Value Static evidence
SHA-256 Hash 139329dc9992e132f9c8d887ad685660161cefcfb0a18867d616a7d217a0605e metadata.json
Filename String 2025年第三季度内职人员违纪名单信息,请大家使用内网电脑查看(文件切勿外发).exe triage.json
Compilation Timestamp 2025-12-05 06:31:50 UTC pefile.txt:38
Decrypted API key pattern Behavior XOR loop: plain[i] = cipher[i] ^ ((i % 0x6d) + 0x92) ghidra:FUN_14002bf40
RC4 key length Config 28 bytes r2:fcn.14002b8a0 (r8d=0x1c)
Payload entry offset Config 0x215B4 relative to image base pefile.txt:54
Stub DLL marker String Stub.dll strings.txt:439
Global param marker String g_stcParam strings.txt:440

Detection Signatures / ATT&CK Mapping

ID Technique Implementation Evidence
T1027.002 Obfuscated Files / Information RC4-encrypted .text with entropy 7.998 pefile.txt:95
T1055 Process Injection Reflective DLL preparation in decrypted .text; imports resolved dynamically Static inference from stub strings and flow
T1106 Native API Direct Nt* API strings (NtClose, NtDuplicateObject, NtQueryInformationProcess, NtRemoveProcessDebug) in decrypted imports strings.txt:282-286
T1497.001 Sandbox Evasion NtRemoveProcessDebug, DbgUiSetThreadDebugObject, IsDebuggerPresent strings present strings.txt:286-288, strings.txt:366
T1027 Obfuscated Files or Information Zero section names, fabricated PE directories, zeroed checksum pefile.txt:43,68,80

References

  • entities/silverfox — Family page with Rust and C variant fingerprints
  • techniques/peb-walking-api-resolution — Detailed PEB-walking implementation
  • OpenCTI artifact — Source label silverfox/valleyrat

Provenance

  • File identification: file v5.45 — ^[file.txt]
  • PE structural analysis: pefile (Python) — ^[pefile.txt]
  • Header summary: radare2 rabin2^[rabin2-info.txt]
  • Metadata: exiftool v12.76 — ^[exiftool.json]
  • String extraction: strings^[strings.txt]
  • Decompilation: Ghidra (pyghidra MCP) + radare2 pdc — provenance markers in-line
  • Static provenance: all claims trace to raw/analyses/139329dc9992e132f9c8d887ad685660161cefcfb0a18867d616a7d217a0605e/*