452e085f42d6054435f95d363588f3d516f1a52d28b033f33a91e843ac4d720esilverfox: 452e085f — MSVC C++ x64 process hollowing injector with LZSS decompressor and privilege escalation
Executive Summary
A stripped MSVC C++ x64 PE (228 KB) that decompresses an LZSS-compressed payload, escalates privileges to SeDebugPrivilege, enumerates running processes, and injects the decompressed second stage into a suspended child via VirtualAllocEx/NtWriteVirtualMemory/CreateProcessW. Also embeds an alternate ShellExecuteExW execution path and imports MoveFileExW for delayed self-erasure. No hardcoded C2 found statically; payload delivery and C2 are assumed to live inside the decompressed stage. This is the sixth confirmed SilverFox sibling, linking the 50K C stub (82d4255) and the RC4 loader (139329dc9) into a single toolchain evolution.
What It Is
| Attribute | Detail |
|---|---|
| SHA-256 | 452e085f42d6054435f95d363588f3d516f1a52d28b033f33a91e843ac4d720e^[triage.json] |
| File type | PE32+ executable (GUI) x86-64, stripped, for MS Windows^[file.txt] |
| Size | 233 880 bytes (228.4 KB)^[triage.json] |
| Compiler | MSVC 14.0 (C/C++)^[pefile.txt:45]^,^[exiftool.json:18] |
| Linker | MSVC 2015/2017 (14.0)^[pefile.txt:45] |
| Sections | .text, .rdata, .data, .pdata, .rsrc, .reloc (6 sections, standard names)^[pefile.txt:75] |
| Stripped | Yes (external PDB only)^[pefile.txt:39] |
| Exports | 0^[pefile.txt:268] |
| Signature | Unsigned (no Authenticode)^[rabin2-info.txt:27] |
| Imports | KERNEL32.dll, ntdll.dll, ADVAPI32.dll, SHELL32.dll, PSAPI.DLL (5 DLLs, 46 total imports)^[pefile.txt:268] |
| Labels | silverfox, trojan/silverfox.bg[qtsc]^[triage.json] |
How It Works
Entry chain is a simple two-call thunk: entry @ 140001000 → FUN_140001020 → FUN_140001030.^[ghidra:entry:140001000] The main payload routine (FUN_140001030) orchestrates the full infection lifecycle.
Decompression. FUN_140001030 calls FUN_140004000 twice (at 140001723 and 140001c5b) to perform in-memory LZSS sliding-window decompression.^[ghidra:FUN_140001030] The decompressor recovers a 12-bit length/offset encoding with SSE-accelerated back-reference copies via FUN_1400065c0.^[ghidra:FUN_140004000] This is the same algorithm used by siblings ed1a0047 and beb3a9d9.^lzss-payload-decompression
Privilege escalation. Two internal helpers (FUN_1400038c0 and FUN_140003f80) both execute the same token-manipulation sequence: OpenProcessToken → LookupPrivilegeValueW → AdjustTokenPrivileges to enable SeDebugPrivilege.^[ghidra:FUN_1400038c0, ghidra:FUN_140003f80] This prepares the injector for cross-session process access.
Process enumeration. CreateToolhelp32Snapshot / Process32FirstW / Process32NextW walk the process list; lstrcmpiW matches a target name decrypted at runtime.^[ghidra:FUN_140001030:process-enumeration] The target name is not recoverable statically.
Injection. VirtualAllocEx + NtWriteVirtualMemory (or WriteProcessMemory) write the decompressed payload into the remote process, followed by CreateProcessW with a custom STARTUPINFO built by FUN_140005190.^[ghidra:FUN_140001030:injection-chain] This is classic process-hollowing without CreateRemoteThread — the child is created suspended and its memory is overwritten before resumption.
Alternate execution path. If injection fails or the target process is unavailable, FUN_1400044b0 launches a new process via ShellExecuteExW.^[ghidra:FUN_1400044b0] This provides a reliability fallback.
Self-erasure. The binary imports MoveFileExW (no MOVEFILE_DELAY_UNTIL_REBOOT constant is visible statically, but the import profile and sibling behavior make the intent clear).^[pefile.txt:302]
Anti-analysis / sandbox evasion. No debugger checks or VM artifacts were found in the decompiled stub. The binary does enumerate loaded modules via NtQuerySystemInformation (SystemModuleInformation) in FUN_140005c00, looking for a specific decrypted module name; if absent, the execution path may diverge.^[ghidra:FUN_140005c00]
Decompiled Behavior
Entry point (entry @ 140001000)
// entry
FUN_140001020();
RtlExitUserProcess(0);
FUN_140001020 is a trivial wrapper that calls FUN_140001030 and returns.^[ghidra:FUN_140001020] All behavior lives in the single large routine at 140001030.
Main orchestrator (FUN_140001030)
This ~2700-byte function (Ghidra) contains 30+ API calls and 15 internal helper calls. Key order of operations inferred from callee list:
GetModuleFileNameW— retrieves own path (self-reference check)CreateToolhelp32Snapshot+Process32FirstW/Process32NextW— enumerate processeslstrcmpiW— compare process names against a runtime-decoded targetDuplicateHandle+GetProcessId— obtain a usable handle to the targetOpenProcessToken→LookupPrivilegeValueW→AdjustTokenPrivileges— escalate toSeDebugPrivilegeGetSystemDirectoryW/GetTempPathW/GetTempFileNameW— prepare staging pathsCreateFileW/CreateFileMappingW/MapViewOfFile— map payload bufferFUN_140004000— LZSS decompress embedded payload into mapped viewVirtualProtect— adjust page permissions on decompressed bufferVirtualAllocEx+NtWriteVirtualMemory— write payload into remote processCreateProcessW(viaFUN_140005190) — create suspended child or reinjectShellExecuteExW(viaFUN_1400044b0) — fallback launchMoveFileExW/DeleteFileW— cleanup / self-deleteSleep— pacing / sandbox evasion
LZSS decompressor (FUN_140004000)
Called from two call sites inside FUN_140001030. Signature: void FUN_140004000(longlong out_buf, ulonglong out_len, longlong in_buf, uint in_len, undefined4 *bytes_written). Uses 12-bit length/offset tokens, 4-byte SSE copy loops (FUN_1400065c0), and back-reference sliding-window reconstruction. Identical structural fingerprint to sibling beb3a9d9.^[ghidra:FUN_140004000]
Token escalators (FUN_1400038c0 and FUN_140003f80)
Both functions implement the same OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ...) → LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", ...) → AdjustTokenPrivileges(..., SE_PRIVILEGE_ENABLED, ...) sequence.^[ghidra:FUN_1400038c0, ghidra:FUN_140003f80] Two copies suggest either linker artifact or deliberate redundancy.
Module enumerator (FUN_140005c00)
Resolves NtQuerySystemInformation dynamically (string-decoded), queries SystemModuleInformation (class 5), walks the module list looking for a decrypted target name, and extracts a DWORD from offset 0x2c of the matching entry.^[ghidra:FUN_140005c00] Used for environment gating or EDR targeting.
C2 Infrastructure
No hardcoded C2 strings recovered statically. All network-relevant indicators (domains, IPs, URLs, mutexes, named pipes) are absent from the string table. The binary is a pure loader/stager; C2 logic is expected to reside in the LZSS-decompressed second stage.
Interesting Tidbits
- Embedded filename:
UaaxkcZq.exeappears twice at14001d914and14001d98c, likely a randomized per-build dropper name.^[strings.txt] - Version string:
9.8.4400.291at14001d8d8and14001da00— probably a masquerade version or internal build tag.^[strings.txt] - No anti-debug: Unlike the RC4 loader sibling (
139329dc9), this sample has noIsDebuggerPresent, noCheckRemoteDebuggerPresent, and no TLS callback anti-analysis.^[capa.txt] - Standard IAT: Unlike the PEB-walking variants (
139329dc9,82d4255), this binary uses a normal import table (46 imports across 5 DLLs), making static analysis straightforward once symbols are resolved.^[pefile.txt:268] - Size class: 228 KB sits neatly between the 50K C stub (
82d4255) and the 173K RC4 loader (139329dc9), suggesting a progression: lean stub → medium IAT injector → heavy RC4 loader.
How To Mess With It (Homelab Replication)
Toolchain: MSVC 2017/2019 (v14.x), x64 Release.
Key ingredients:
- LZSS compressor (12-bit window) — any standard implementation works; pack a second-stage DLL/EXE into
.rdata VirtualAllocEx+NtWriteVirtualMemory+CreateProcessW(CREATE_SUSPENDED)for hollowingOpenProcessToken→LookupPrivilegeValueW(L"SeDebugPrivilege")→AdjustTokenPrivilegesMoveFileExW(MOVEFILE_DELAY_UNTIL_REBOOT)for self-cleanupShellExecuteExWfallback launcher
Verification step: Build a test injector that writes "HELLO" into a suspended notepad.exe. Run capa on the resulting binary — it should hit inject code into process, allocate or change RWX memory, and manipulate process privileges.
Deployable Signatures
YARA
rule silverfox_x64_injector_452e085f {
meta:
description = "SilverFox x64 process hollowing injector with LZSS decompressor"
author = "PacketPursuit SOC"
date = "2026-06-04"
sha256 = "452e085f42d6054435f95d363588f3d516f1a52d28b033f33a91e843ac4d720e"
family = "silverfox"
strings:
$uaax = "UaaxkcZq.exe" wide ascii
$ver = "9.8.4400.291" wide ascii
$lzss_loop = { 48 8B 44 24 ?? 48 8B 4C 24 ?? E8 ?? ?? ?? ?? } // FUN_1400065c0 copy helper
$ntq = "NtQuerySystemInformation" wide ascii
$se_debug = "SeDebugPrivilege" wide ascii
$movf = "MoveFileExW" wide ascii
condition:
uint16(0) == 0x5a4d
and pe.is_64()
and ($uaax or $ver)
and (pe.imports("KERNEL32.dll", "CreateProcessW")
and pe.imports("KERNEL32.dll", "VirtualAllocEx")
and pe.imports("ntdll.dll", "NtWriteVirtualMemory")
and pe.imports("ADVAPI32.dll", "AdjustTokenPrivileges")
and pe.imports("SHELL32.dll", "ShellExecuteExW"))
and filesize < 300KB
}
Sigma
title: SilverFox Process Hollowing Injector
description: Detects SilverFox-like process injection chain with privilege escalation and LZSS decompressor
logsource:
product: windows
category: process_creation
detection:
selection:
CommandLine|contains:
- "UaaxkcZq.exe"
- "9.8.4400.291"
selection_parent:
ParentImage|endswith:
- "UaaxkcZq.exe"
selection_api:
- ImageLoaded|endswith:
- "\\psapi.dll"
- CommandLine|contains:
- "CreateProcessW"
- "VirtualAllocEx"
- "NtWriteVirtualMemory"
- "AdjustTokenPrivileges"
condition: selection or selection_parent or selection_api
falsepositives:
- Unknown
level: high
IOCs
| IOC | Value | Type |
|---|---|---|
| SHA-256 | 452e085f42d6054435f95d363588f3d516f1a52d28b033f33a91e843ac4d720e |
Hash |
| Embedded filename | UaaxkcZq.exe |
String |
| Version string | 9.8.4400.291 |
String |
| LZSS decompressor | FUN_140004000 @ 0x140004000 |
Function |
| Token escalator 1 | FUN_1400038c0 @ 0x1400038c0 |
Function |
| Token escalator 2 | FUN_140003f80 @ 0x140003f80 |
Function |
| Main orchestrator | FUN_140001030 @ 0x140001030 |
Function |
| Entry point | 0x140001000 |
Address |
Behavioral Fingerprint
This binary decompresses an LZSS payload at runtime, escalates privileges to SeDebugPrivilege, enumerates running processes via CreateToolhelp32Snapshot, and writes the decompressed payload into a suspended child process using VirtualAllocEx and NtWriteVirtualMemory. It also contains a fallback ShellExecuteExW execution path and imports MoveFileExW for delayed self-deletion. No hardcoded C2 is present; all network logic is assumed to live in the decompressed stage.
Detection Signatures
| capa rule | ATT&CK ID | Evidence |
|---|---|---|
| inject code into process | T1055 | VirtualAllocEx, NtWriteVirtualMemory, CreateProcessW^[ghidra:FUN_140001030] |
| allocate or change RWX memory | T1055 | VirtualAllocEx / VirtualProtect^[ghidra:FUN_140001030] |
| manipulate process privileges | T1055.012 | OpenProcessToken, AdjustTokenPrivileges^[ghidra:FUN_1400038c0] |
| load library | T1070.004 | LoadLibraryW, GetProcAddress^[ghidra:FUN_140001030] |
| reference anti-VM strings | T1497.001 | None found static |
| encrypt data using RC4 | T1027 | None found (LZSS, not RC4) |
Note: capa reports reference anti-VM strings but no specific strings were recovered in static analysis; this may be a false positive or the strings are runtime-decoded inside the LZSS payload.^[capa.txt]
References
- silverfox — family entity page
- lzss-payload-decompression — LZSS decompression technique
- process-hollowing — process hollowing technique
- SeDebugPrivilege-escalation — privilege escalation technique
- NtQuerySystemInformation-module-enumeration — module enumeration technique
- MoveFileExW-delayed-self-deletion — self-deletion procedure
- ed1a00479fe2ea2555882c67719abc86e98b512f122aea79adacf37355cab996 — Rust sibling
- 82d425516199d497c3a25edc4c3ad05c14469f697230f3ad17fe03ce73cd0216 — C stub sibling
- 139329dc9992e132f9c8d887ad685660161cefcfb0a18867d616a7d217a0605e — RC4 loader sibling
- beb3a9d9fa738ac7ebac7dc8f5357c9a6673cfae1bc50fd73497d350afd5ed1c — Authenticode sibling
- e772de930167a24868814510021d73d8c061b4d7af0946ac302e53bb1c9cba56 — Sangfor masquerade sibling
Provenance
- Static analysis performed with Ghidra (MCP) and radare2 (MCP) on pp-hermes LXC.
- Imported binary:
<sample 452e085f42d6.bin> - Ghidra project name:
452e085f42d6054435f95d363588f3d516f1a52d28b033f33a91e843ac4d720e.bin-1cdd44 - radare2 analysis level: 2 (271 functions)
- CAPE detonation: skipped — no Windows guest available. Dynamic behavior inferred from static decompilation.