typeanalysisfamilyphorpiexconfidencehighcreated2026-06-10updated2026-06-10malware-familyloaderpemsvcc2defense-evasion
SHA-256: 2ffc320332ff30d80c0a1da728aa59db136438338409cf01a96b71e488d21b67

phorpiex: 2ffc3203 — Thin MSVC9 downloader sibling, peinf→xmr→xmrget chain, May 2026

Executive Summary

10 KB MSVC9 PE32 downloader dropped by the Phorpiex crimeware campaign. No packing, no obfuscation, honest main() entry. Downloads three payloads sequentially from a hardcoded Russian IP via dual WinInet/URLMon fetch paths, gates execution with an OS build-number check and a filesystem marker file, spawns each dropped payload via ShellExecuteW, and scrubs the Zone.Identifier ADS. Static-only analysis — no CAPE Windows guest available.

What It Is

Field Value
SHA-256 2ffc320332ff30d80c0a1da728aa59db136438338409cf01a96b71e488d21b67
Size 10 240 bytes ^[file.txt]
Type PE32 executable (GUI) Intel 80386, 5 sections ^[file.txt]
Linker MSVC 9.0 (Visual C++ 2008) ^[pefile.txt:45] ^[rabin2-info.txt:17]
Compiled 2026-05-22 07:45:34 UTC ^[pefile.txt:34] ^[exiftool.json:15]
Signed No ^[rabin2-info.txt:27]
Packed No ^[rabin2-info.txt:23] ^[binwalk.txt]
Family phorpiex (OpenCTI label dropped-by-phorpiex) ^[metadata.json:7]

Confirmed campaign sibling to 6b8527a7 and 025f5798 — same C2 IP (178.16.54.109), same build day, same dual-download logic, same marker filename (d3333333333333333333.txt), same toolchain fingerprint. Delta: payload name rotation (peinf.exe instead of 15.exe observed in 6b8527a7).

How It Works

  1. Entry delaymain sleeps 2000 ms, then calls the downloader worker fcn.004010a8 with the first URL ^[r2:main].
  2. Payload chain — Three hardcoded URLs fetched in order:
    • http://178.16.54.109/peinf.exe ^[strings.txt:83]
    • http://178.16.54.109/xmr.exe ^[strings.txt:82]
    • http://178.16.54.109/xmrget.exe ^[strings.txt:81]
  3. Dual-path download — Primary path uses WinInet (InternetOpenW with Chrome/128 UA → InternetOpenUrlWInternetReadFileWriteFileCreateFileW). If that fails, sleeps a random interval, regenerates a random filename, and retries via URLDownloadToFileW ^[r2:fcn.004010a8] ^[pefile.txt:278-302]. See wininet-urlmon-dual-download.
  4. Marker-file gatingfcn.004012fb expands %appdata% and checks/creates d3333333333333333333.txt as a filesystem mutex ^[strings.txt:91] ^[r2:fcn.004012fb]. Prevents redundant payload execution on subsequent runs. See marker-file-mutex-gating.
  5. OS build gatingfcn.004013c0 loads ntdll.dll, resolves RtlGetVersion, and returns true only if dwBuildNumber >= 0x55f0 (22000 = Windows 11 / Server 2022). If the check fails, the payload branch is skipped ^[r2:fcn.004013c0] ^[strings.txt:89]. See rtlgversion-build-gating.
  6. Execution — Each successfully downloaded file is spawned via ShellExecuteW with verb open ^[r2:fcn.004010a8] ^[strings.txt:90].
  7. ADS scrub — After writing the file, the dropper constructs %s:Zone.Identifier and deletes it via DeleteFileW to remove the "Downloaded from Internet" mark ^[r2:fcn.004010a8] ^[strings.txt:85]. See zone-identifier-deletion.
  8. Random filename%TEMP%\<rand><rand>.exe, seeded with GetTickCount and formatted via _snwprintf ^[r2:fcn.004010a8] ^[strings.txt:86].

Decompiled Behavior

main (0x0040143e) — honest C runtime entry. Sleeps, then orchestrates three sequential download attempts. Each attempt: push URL → call fcn.004010a8 → if success, call fcn.004012fb (marker gate). If marker gate returns true, proceeds to next URL; otherwise exits.

fcn.004010a8 (Downloader/Executor) — the workhorse. Allocates a 0x1630-byte stack buffer via fcn.004014b0 (stack probe). Expands %TEMP%, seeds srand(GetTickCount()), generates %s\%d%d.exe, opens a WinInet session with the hardcoded Chrome/128 User-Agent string, fetches the URL in 4096-byte chunks (InternetReadFile), writes to disk (CreateFileW / WriteFile), closes handles, scrubs Zone.Identifier, then spawns the file via ShellExecuteW("open", ...). If any step fails, falls back to URLDownloadToFileW after sleeping rand() % 0xea60 milliseconds and regenerating the filename.

fcn.004012fb (Marker Gate) — expands %appdata%, then calls CreateFileW on the path. If the call returns INVALID_HANDLE_VALUE (file already exists), returns 1 (gate open / skip). If the call succeeds (file created), returns 0 (proceed). This is the inverse of a typical mutex check but serves the same purpose: preventing redundant execution.

fcn.004013c0 (OS Build Check) — loads ntdll.dll via GetModuleHandleW, resolves RtlGetVersion via GetProcAddress, fills an OSVERSIONINFOEXW struct (size 0x114), and validates dwBuildNumber >= 0x55f0 (22000). Returns boolean.

C2 Infrastructure

  • IP: 178.16.54.109 — Russia, Saint Petersburg, ASN 48666 (Beget LLC)
  • Paths: /peinf.exe, /xmr.exe, /xmrget.exe
  • Protocol: HTTP (cleartext), no TLS, no DNS resolution
  • User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 ^[strings.txt:84]
  • No domains, no cookies, no POST body.

Interesting Tidbits

  • IsDebuggerPresent is imported ^[pefile.txt:331] but not invoked in any decompiled function; likely residual from the MSVCRT startup or an unused anti-debug template.
  • QueryPerformanceCounter imported but unused in the main flow ^[pefile.txt:312]; possible anti-emulation residue from a shared builder stub.
  • Chrome/128 UA is current as of May 2026, suggesting the builder is actively maintained.
  • Payload name peinf.exe is a rotation from 15.exe observed in sibling 6b8527a7; C2 infrastructure is otherwise identical.
  • No persistence mechanism in the outer binary — the downloader relies on dropped payloads to install their own persistence.
  • The binary has a standard VS_VERSIONINFO manifest requesting asInvoker execution level ^[pefile.txt:356-394].

How To Mess With It (Homelab Replication)

Toolchain: Visual C++ 2008 (MSVC 9.0), x86, static link to MSVCR90 (/MT).

Working snippet (WinInet primary + URLMon fallback): Compile with cl.exe /O2 /MT downloader.cpp using the VC9 toolset. Use InternetOpenW, InternetOpenUrlW, InternetReadFile, WriteFile, ShellExecuteW, and URLDownloadToFileW. Expand %TEMP% via ExpandEnvironmentStringsW, seed srand(GetTickCount()), generate %TEMP%\%d%d.exe, and spawn with verb open. Add a DeleteFileW call on %s:Zone.Identifier to replicate the full fingerprint.

Verification: If capa signatures were installed, the reproducer should hit download file and execute shell command.

Deployable Signatures

YARA Rule

rule Phorpiex_ThinMSVC9_Downloader_May2026 {
    meta:
        description = "Phorpiex thin MSVC9 downloader — peinf/xmr/xmrget variant"
        author = "PacketPursuit"
        date = "2026-06-10"
        hash = "2ffc320332ff30d80c0a1da728aa59db136438338409cf01a96b71e488d21b67"
    strings:
        $ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36" ascii wide
        $url1 = "http://178.16.54.109/peinf.exe" ascii wide
        $url2 = "http://178.16.54.109/xmr.exe" ascii wide
        $url3 = "http://178.16.54.109/xmrget.exe" ascii wide
        $zone = "%s:Zone.Identifier" ascii wide
        $temp_pattern = "%s\\%d%d.exe" ascii wide
        $marker = "d3333333333333333333.txt" ascii wide
        $rtl = "RtlGetVersion" ascii wide
    condition:
        uint16(0) == 0x5A4D and
        uint32(uint32(0x3C)) == 0x4550 and
        pe.linker_version.major == 9 and
        pe.imports("WININET.dll", "InternetOpenW") and
        pe.imports("urlmon.dll", "URLDownloadToFileW") and
        pe.imports("SHELL32.dll", "ShellExecuteW") and
        3 of ($url*) and $zone and $marker
}

Behavioral Hunt Query (KQL/Splunk pseudo-code)

(process_name="*.exe" AND parent_process_name="*.exe")
| where command_line LIKE "%TEMP%\%.exe"
| where dns_query="178.16.54.109" OR url LIKE "http://178.16.54.109/%"
| stats count by host, user, process_name, command_line

IOC List

Type Value Note
SHA-256 2ffc320332ff30d80c0a1da728aa59db136438338409cf01a96b71e488d21b67 Outer dropper
IP 178.16.54.109 C2 / payload host
URL http://178.16.54.109/peinf.exe First-stage payload
URL http://178.16.54.109/xmr.exe Second-stage payload
URL http://178.16.54.109/xmrget.exe Third-stage payload
User-Agent Mozilla/5.0 ... Chrome/128.0.0.0 Safari/537.36 Hardcoded WinInet UA
Marker file %appdata%\d3333333333333333333.txt Filesystem mutex
Temp file %TEMP%\<rand><rand>.exe Staged payload name

Detection Signatures (ATT&CK Mapping)

Tactic Technique Evidence
Initial Access T1204.002 — User Execution: Malicious File Spam-distributed PE ^[metadata.json]
Execution T1059.003 — Windows Command Shell ShellExecuteW with verb open ^[r2:fcn.004010a8]
Defense Evasion T1070.004 — File Deletion DeleteFileW on Zone.Identifier ^[r2:fcn.004010a8]
Defense Evasion T1497.001 — Virtualization/Sandbox Evasion: System Checks RtlGetVersion build-number gating ^[r2:fcn.004013c0]
Defense Evasion T1055 — Process Injection (inferred) InterlockedExchange / InterlockedCompareExchange imported ^[pefile.txt:313] — not observed in decompiled path; may be used by dropped payload
Command and Control T1105 — Ingress Tool Transfer WinInet + URLMon dual download ^[r2:fcn.004010a8]
Discovery T1082 — System Information Discovery RtlGetVersion OS build check ^[r2:fcn.004013c0]

References

Provenance

Analysis derived from:

  • file.txt — file(1) output
  • pefile.txt — pefile/PE-bear header dump
  • exiftool.json — ExifTool PE metadata
  • rabin2-info.txt — radare2 binary summary
  • strings.txt — raw ASCII/Unicode strings
  • binwalk.txt — embedded artifact scan
  • r2 decompile — radare2 pseudo-C (pdg) of main, fcn.004010a8, fcn.004012fb, fcn.004013c0
  • metadata.json — OpenCTI artifact labels

Report generated 2026-06-10. Static-only; no CAPE detonation available.