Case study — single-host file replacement
Every Executable
Was a Copy
A Visual Basic 6 binary spent six years quietly overwriting the tools on one Windows workstation — then replaced 92 of them in eleven seconds. This is the complete forensic record: what was found, how it worked, and what could not be proven.
.exe-wrapper suffix. The dominant build accounts for 45 of them; twenty builds appear exactly once.The night Node went silent
On 20 October 2026, a developer typed node --version and got nothing back. Not an error. Not a version string. Not a crash dialog. The process returned in silence, as though the command had never been issued. Node does not fail that way. It either prints a version or it fails loudly with a stack trace.
The runtime had not broken. The runtime was no longer Node. node.exe was a 1,576,960-byte Visual Basic 6 executable that had been present on the disk, in one form or another, for six years.
The first hours of the investigation produced a plausible but wrong theory. Two separate ripgrep binaries — one used by the editor, one by an agent tool — had also been replaced. Those editors execute ripgrep on every file search. Every time a search ran, the malicious payload files reappeared. That looked exactly like a watchdog: something was watching, detecting deletion, and restoring itself.
It was not. The re-drops were purely execution-driven. The malware re-created its persistence artefacts whenever any infected binary ran, and a hijacked rg.exe was running dozens of times an hour. The apparent self-healing was an artefact of the user's own workflow.
Assuming a watchdog would have sent the analysis looking for a scheduled task, service or Run key — none of which existed. The correct model was simpler and more unsettling: every infected executable was its own persistence mechanism. Restoring genuine ripgrep ended all re-drops across a 120-second idle window, which confirmed it.
The full-disk sweep that followed found the infection was not limited to the two files that broke. It had reached printer drivers, Adobe and CorelDRAW installations, WinRAR, installers, the Go toolchain, Node itself, and the node_modules trees of several development tools. Ninety-six replaced executables across more than sixty directories.
The sample was never executed on the production host during analysis. Everything below was derived from static parsing, filesystem metadata, controlled observation of the malware's own side effects, and a failed attempt at user-mode emulation.
What the binary is
The sample is a native-compiled Visual Basic 6 PE32 executable, unsigned, GUI subsystem, with no packer. Its identity is unusually stable: every build is exactly the same size, and the code and padding are byte-identical across all of them. Only the resource section changes.
| SHA-256 | f88fef68b5e33afc957e538413461d5ab0e2062dacdc01f7f30fbb49fbd046e1 |
|---|---|
| Size | 1,576,960 bytes — exactly 1540 KB, identical in every sample |
| Format | PE32, x86 (32-bit), GUI subsystem |
| Language | Visual Basic 6, native compilation |
| Packer | None — standard .text / .data / .rsrc |
| Signature | Unsigned. Get-AuthenticodeSignature returns NotSigned |
| PE timestamp | 2017-01-15 21:05:45 UTC — identical in every build, therefore templated or forged |
| Imports | MSVBVM60.DLL only |
| Entry point | 0x4013E0 |
| VB6 header | VB5! at file offset 0x16EF10 |
Build variation
At least 31 distinct builds were recovered from the 96 quarantined artefacts. They are not meaningfully different programs. A byte-level comparison of two builds found 2,860 differing bytes — 0.181 % of the file — all inside .rsrc. The icon, the version strings and the manifest vary; the engine does not.
The distribution is heavily skewed. One build accounts for nearly half the infection; the rest is a long tail, including twenty builds that appear exactly once. That pattern is consistent with a builder that regenerates cosmetic metadata each time it is run, rather than with 31 independent samples.
Version resource: the naming trap
The version resource is the most misleading part of the file. It looks authoritative, and every field in it is fabricated:
ProductName Orphancy
CompanyName Windows <- impersonation
FileDescription Geacata
FileVersion 1.09.0005
ProductVersion 1.09.0005
InternalName Braggat0
OriginalFilename Braggat0.exe
The embedded manifest names the application Riltroccha Ralocchia and requests level="asInvoker". That last detail matters: the binary does not ask for elevation. It runs with whatever privileges the user already has, and silently skips anything it cannot write to.
None of these strings identify a malware family. They are builder output — mutated dictionary words with numeric suffixes, in the same style as the procedure names found in the binary's string table. Orphancy, Geacata, Braggat0 and Riltroccha Ralocchia should be treated as artefacts of generation, not attribution (§13).
Anatomy of the file
The binary is mostly empty. Real executable code occupies roughly twelve kilobytes of a file that is one and a half megabytes long. The rest is a mix of repeating filler and synthetic padding engineered to look like data.
3.1 Imports: a standard VB6 surface
The only imported DLL is MSVBVM60.DLL; all Windows API access goes through the VB6 runtime thunk mechanism. The Declare table, which spans 0x171480–0x171D20, contains an ordinary set of VB6 control imports — not a malware-specific API surface:
| DLL | Functions |
|---|---|
| kernel32 | EnumDateFormatsExA, GetLastError, lstrcatA, SetLocalTime, WriteConsoleOutputAttribute, VirtualAllocEx |
| user32 | CopyRect, FreeDDelParam, ExcludeClipRect, GetForegroundWindow, SetParent, SetConsoleWindowInfo |
| gdi32 | GetViewportExtEx |
| winmm | auxGetDevCapsA, mixerMessage |
The odd-looking entries are runtime boilerplate, not intent. EnumDateFormatsExA belongs to the DateTimePicker control. auxGetDevCapsA and mixerMessage belong to multimedia controls. ExcludeClipRect and CopyRect belong to graphics controls. A VB6 executable carries these whether or not its author uses them.
Two imports deserve a second look, and got one. SetLocalTime is present, but the file-replacement behaviour observed on disk never required it; Windows updates modification time through ordinary write operations. VirtualAllocEx is present, but no injection target, remote process handle or shellcode region was found. Neither import is evidence of the capability it suggests on its own.
3.2 Strings: deliberately empty
There are no functional strings. No registry paths, no command lines, no URLs, no target filenames. Only 26 UTF-16 strings exist, and almost all of them come from the version resource. What remains is:
- DLL names, including
MSVBVM60.DLL; - a 169-entry dictionary wordlist with numeric suffixes —
Braggat0,Fishpot2,Saucerlike0,Gadiyo3; - randomised procedure names such as
Otosalpinx,Ethered,Hakashima; - three path fragments:
C:\,tempandTMP.
That is the entire readable vocabulary. String-based capability analysis is impossible on this file: whatever functional strings exist are encrypted or assembled at runtime. The wordlist is the builder's name generator, not a payload.
3.3 Size inflation: 1.5 MB of nearly nothing
The file is inflated to an exact, repeatable size. The layout tells the story:
- Headers0x0–0x10000.26%
- Entry thunk and data0x1000–0x18000.13%
- Repeating filler, entropy 1.500x1800–0x110004.03%
- Transition0x11000–0x1D0003.12%
- Synthetic padding, entropy ≈5.900x1D000–0x16900086.2%
- VB6 header, Declare table, code0x169000–0x1740002.86%
- Trailing slack0x174000–0x1810003.38%
71 24 71 25 … is byte-identical across every build and is the most reliable structural fingerprint in the file.| File range | Content | Character |
|---|---|---|
| 0x001000–0x001800 | PE entry thunk and initial data | Small, real |
| 0x001800–0x011000 | Repeating 71 24 71 25 … | Entropy 1.50 |
| 0x011000–0x1D000 | Transition | Mixed |
| 0x1D000–0x169000 | Synthetic padding, uniform per 64 KB block | Entropy ≈5.90, 38.1 % zeros |
| 0x16EF10 | VB5! header | Anchor for all validation |
| 0x171480–0x171D20 | ExternalTable / Declare records | Standard VB6 |
| 0x171000–0x174000 | The only region with real instruction density | ~12 KB of code |
The padding is not random. Its entropy sits at roughly 5.90 with 38.1 % zero bytes, and it is uniform per 64 KB block — a signature of generated data, not of compressed or encrypted content. Each 16 KB block contains 40–50 0xE8 and 0xC3 bytes, which is almost exactly the statistical expectation for random bytes (one in 256, or about 64 per 16 KB). Those bytes are not code, even though a byte-frequency heuristic would flag them as call and return instructions.
Because the padding is byte-identical across all 31 builds, it functions as a stable detection fingerprint even when every metadata string is randomised. The exact 1540 KB size is also consistent with a deliberate attempt to defeat size-based detection — an attempt that is defeated in turn by its own precision, because 1,576,960 is a suspiciously exact number.
3.4 No second stage
An exhaustive signature search came back negative on every count that matters for payload delivery:
| Signature class | Result |
|---|---|
| Embedded PE files | 1 — the file itself |
| ZIP / GZIP / CAB / RAR / 7z / OLE containers | None |
| AES S-box and inverse S-box | None |
| SHA-256 round constants | None |
| MD5 constants | None |
There is no bundled payload. The binary is the entire artefact. Whatever else it might do with a network connection, it does not carry a second stage inside itself, and there is no cryptographic material present that would decrypt one.
3.5 The unresolved layout anomaly
One finding resisted explanation and is recorded here as unresolved rather than smoothed over. The VB6 header pointer fields should point at valid structures. They point into zeroes:
+0x30 0x005701EC -> file 0x1701EC : 00 00 00 00 ... +0x4C 0x00570428 -> file 0x170428 : 00 00 00 00 ...
Linear disassembly at 0x170100 yields only add byte ptr [eax], al — the canonical disassembly of 00 00, not real code. This is not the layout of a conventional VB6 executable, and yet the binary executes and replicates successfully.
Three explanations are possible, and none was confirmed:
- the VB6 header offsets used during analysis are incorrect, and the real code lies somewhere else in the file;
- the binary reconstructs its code in memory at load time, so the on-disk layout is intentionally not executable as-is;
- it is a hollow template whose behaviour is delivered entirely through VB6 runtime mechanisms that static layout analysis does not expose.
This anomaly limits confidence in any claim about internal capability. Everything stated about behaviour in this report is derived from observed filesystem effects, not from reading the code. The distinction is maintained throughout.
How it worked
The mechanism is simple enough to state in one sentence: for every executable it can write to, it swaps itself in and hides the original under a v prefix. The sophistication is not in the algorithm. It is in the timestomping, the recursion, and the choice of targets.
4.1 The replacement loop
For each target executable:
- Rename
X.exetovX.exe. - Set
Hidden+SystemonvX.exe. - Write a copy of itself to
X.exe. - Skip any file it cannot write to, silently.
The behaviour is recursive: an entire directory tree is processed. During the 2026 wave, dozens of executables in a printer-driver suite were replaced in under ten seconds — impossible without a directory walk.
// behaviour reconstructed from filesystem effects; code was not recovered for each file in recursive_walk(start_path): if extension != ".exe": continue if file cannot be written: continue if file already contains this binary: continue // no re-stamp rename(file, "v" + file.name) set_attributes("v" + file.name, HIDDEN | SYSTEM) write_copy_of_self(file) // then, on every execution of any infected copy: ensure("%TEMP%\subfolder\filename.exe") write("%APPDATA%\...\Startup\filename.vbs") ensure("%APPDATA%\Paint.exe") write("%APPDATA%\...\Startup\Paint.lnk")
The self-check matters forensically. Files that already contain the binary are skipped, which is why the four Adobe files infected in 2020 were not re-stamped in 2026. Their modification times still point at the original event, preserving a clean chronological record that a naive re-infection would have destroyed.
4.2 Timestomping: the modification time gives it away
This is the single most useful forensic property discovered, and the one that makes detection possible without hashes.
The malware preserves the target's creation time and sets the modification time to the moment of infection. It is a partial timestomp: the creation timestamp is inherited because the file is renamed and rewritten in place rather than created fresh.
Verified against the HP printer suite, whose clean files retain their true extraction timestamps:
| File | Created | Modified | State |
|---|---|---|---|
| HpSetup.exe | 2026-04-11 12:25:17 | 2026-07-13 11:24:48 | Fake |
| wificonfig.exe | 2026-04-11 12:25:19 | 2026-07-13 11:24:55 | Fake |
| vHpSetup.exe | 2026-04-11 12:25:17 | 2026-04-11 12:25:17 | Genuine |
The gap is the signal. A file created in April and modified in July, sitting next to a hidden v-prefixed twin of a different size, is an infection pair regardless of what any hash database says. This holds even for builds that have never been seen before, which is precisely why it matters: hash matching would miss them.
Creation time belongs to the victim. Modification time belongs to the attacker. The distance between them is the infection window.
Forensic property, §4.2
4.3 Persistence: two artefacts, no services
Persistence is minimal and entirely user-level. Two files are written to the per-user Startup folder, and both are re-created whenever any infected binary executes:
| Artifact | Launches |
|---|---|
| %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\filename.vbs | %TEMP%\subfolder\filename.exe |
| %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\Paint.lnk | %APPDATA%\Paint.exe |
The script is four lines and relies on the shell's own execution API:
Set objShell = CreateObject("Shell.Application")
objShell.ShellExecute "C:\Users\<user>\AppData\Local\Temp\subfolder\filename.exe", "", "", "", 1
%APPDATA%\Paint.exe is a decoy name chosen to blend in. Real Windows Paint lives at %WINDIR%\System32\mspaint.exe and has nothing to do with this file.
4.4 No watchdog — proven by process correlation
No scheduled task, service, WMI subscription, Run key or driver was found, and repeated checks confirmed none existed. Re-infection correlated exactly with execution of a hijacked binary:
rg.exe PID 6160 started 12:31:12 -> filename.exe recreated 12:31:12
Paint.exe recreated 12:31:16
Restoring the genuine ripgrep ended all re-drops across a 120-second idle window. That is the strongest available negative evidence: when the last infected executable stopped running, so did the infection.
Two waves, six years apart
The full-disk scan covered 1,172,494 files on C:, pre-filtering on the exact size and then verifying every hit by parsing for the VB5! header. Two distinct modification-time clusters emerged, separated by more than six years.
Epoch 1 — 2020-01-23 22:48:01 UTC
Four files, all sharing the same modification second, all inside one cracked Adobe Premiere Pro CC 2018 repack:
C:\Adobe_Premiere_Pro_CC_2018_v12.1.2.69_x64_Multilingual\Set-up.exe ...\Crack\AMT Emulator 0.9.2\AMT Emulator 0.9.2_Getintopc.com.exe ...\Crack\Anticloud r4\Adobe.CC2018.Anticloud.r4.exe ...\Crack\Universal Patcher Patch 2.0\Universal Patcher 2.0.exe
The surrounding folder is instructive. Its clean contents retain their true archive dates from 2014–2018, so the 2020 timestamp is not an artefact of extraction: the four executables were modified on this host, years after the archive was unpacked.
Epoch 2 — 2026-07-13 11:24:48 UTC
The second wave is system-wide and fast: 92 files, beginning at 11:24:48 and reaching the last replacement by 11:24:59 — eleven seconds. It swept through user toolchains, editor extensions, package-manager caches and application installers:
C:\3uTools\siinst_1.6\siinst_1.6.exe C:\LJP1100_P1560_P1600_Full_Solution\ (24 executables, x32 and x64) C:\nvm4w\nodejs\node.exe C:\Users\<user>\.cache\opencode\bin\rg.exe C:\Users\<user>\.cache\kilo\bin\rg.exe C:\Users\<user>\.local\share\kilo\bin\rg.exe C:\Users\<user>\go\bin\gopls.exe C:\Users\<user>\.grok\bin\agent.exe C:\Users\<user>\.local\bin\python3.14.exe C:\Users\<user>\.bun\install\cache\...\codex-*.exe (3 files, 2 layouts) C:\Users\<user>\.vscode\extensions\... (kilo, copilot-chat, pylance, python-env-tools, debugpy) C:\Users\<user>\.trae\extensions\... (kilo, pylance, python-env-tools) C:\Users\<user>\openscreen\node_modules\... (18 files: electron, esbuild, 7za, biome, signtool, phantomjs, app-builder, ...) C:\Users\<user>\Documents\Corel\... (CorelDRAW 2021/2024/X7) C:\Users\<user>\Documents\...\WinRAR\Rar.exe C:\Users\<user>\Downloads\brave-browser-*.exe C:\Users\<user>\Desktop\HP_M1130_*.exe C:\Users\<user>\career-ops1\.opencode\node_modules\.bin\node-which.exe C:\Program Files\Common Files\Adobe\Adobe Desktop Common\HEX\Adobe CEF Helper.exe C:\Program Files\Image-Line\Downloader\Update\Updater.exe C:\Users\<user2>\... (WinRAR, Focusrite, 3uTools)
A clear targeting preference emerges: toolchain and updater binaries — rg, gopls, node, esbuild, electron, installers, updaters. These are files that execute frequently and are almost never inspected. Replacing a game executable would be noticed; replacing the search binary inside an editor's cache is not.
Targeting developer tools is a double-edged strategy. It maximises execution frequency, but it also breaks the tools the victim relies on. The infection was discovered precisely because node stopped printing a version — a failure the user could not ignore.
The host has only one volume, C:. An earlier open question about a possible D: sweep is therefore moot: there is no second volume to examine.
Collateral damage
Eight Adobe Photoshop CC 2017 support binaries were left broken, not by the malware directly but by an earlier cleanup that deleted the infected replacements while leaving the hidden originals orphaned. With no visible executable at the expected path, Photoshop could not start its auxiliary components:
| Component | Component |
|---|---|
| Droplet Template.exe | CEPHtmlEngine.exe |
| Adobe Spaces Helper.exe | Adobe QT32 Server.exe |
| dynamiclinkmanager.exe | amecommand.exe |
| dynamiclinkmediaserver.exe | ImporterREDServer.exe |
All eight were reconstructed by renaming the orphaned v* files back to their original names and clearing Hidden and System. The originals were intact; only their names had been taken.
This is a general hazard of file-replacement malware. A scanner that deletes the fake without restoring the displaced original converts a reversible infection into permanent application damage. On this host, several other applications were one deletion away from the same fate.
The probable vector
The 2020 epoch consists precisely of the files a user executes after downloading a repack: the installer and three crack utilities. One of them carries a warez distribution domain in its filename.
Assessment: consistent with a pirated Adobe Premiere Pro 2018 repack delivering the malware on or before 2020-01-23, with the sample lying dormant or sporadically active until the mass replication of 2026-07-13.
The dormancy is the interesting part. If the repack was executed once in 2020, four binaries were infected and then left alone for six years. Nothing in the binary enforces a delay; the likely explanation is simply that none of those four files was executed again until something triggered the 2026 wave. Which binary that was has not been established.
The folder could in principle have been restored from a backup that preserved both creation and modification times. The filesystem evidence favours on-host infection — the archive's other contents retain their original 2014–2018 dates while the four executables share a single 2020 second — but this cannot be proven from timestamps alone. The vector is assessed as probable, not established.
The practical lesson does not depend on resolving that caveat. Cracked software and repacks remain one of the most reliable delivery mechanisms for exactly this class of malware: unsigned, self-contained, no C2 required, and designed to survive on a machine for years without drawing attention.
Detection engineering
Detection for this sample has to survive three problems: 31 builds defeat hashing, the metadata strings are randomised per build, and the file size collides with at least one legitimate binary. The rules and triage procedure below were written against those constraints and validated on the host.
8.1 The size trap
Exactly one legitimate file on this host is also 1,576,960 bytes: C:\Program Files\WindowsApps\Microsoft.YourPhone_*\WinRT.Runtime.dll, an ordinary .NET assembly. Size is a useful pre-filter and nothing more. The VB5! marker is what makes a match real.
The same trap appeared in quarantine. Eighteen candidate copies were later confirmed as false positives and removed from the malware set: seventeen ByteFence quarantine containers and the WinRT.Runtime.dll collision. All eighteen were verified byte-identical to surviving originals, and every original remained intact.
8.2 YARA rules
Four rules were written, in decreasing order of confidence. Rules 1 and 2 are high confidence. Rule 3 hunts variants with randomised metadata. Rule 4 is a broad sweep for large file sets and expects manual triage.
import "hash" rule Orphancy_VB6_Replicator { meta: description = "VB6 self-replicating file replacer" family = "Orphancy / Geacata" sha256_1 = "f88fef68b5e33afc957e538413461d5ab0e2062dacdc01f7f30fbb49fbd046e1" strings: $vb5 = "VB5!" ascii $msvbvm = "MSVBVM60.DLL" ascii nocase $prod = "Orphancy" ascii $desc = "Geacata" ascii $internal = "Braggat0" ascii $manifest = "Riltroccha" ascii /* repeating padding that fills ~60 KB of .text */ $filler = { 71 24 71 24 71 25 71 25 71 24 71 25 71 25 71 25 } condition: uint16(0) == 0x5A4D and filesize < 4MB and $vb5 and $msvbvm and $filler and ( $prod or $desc or $internal or $manifest ) } rule Orphancy_Known_Sample { condition: uint16(0) == 0x5A4D and ( hash.sha256(0, filesize) == "f88fef68b5e33afc957e538413461d5ab0e2062dacdc01f7f30fbb49fbd046e1" or hash.sha256(0, filesize) == "21b66c732c5caa0b858899d3a5084143f2789e1bf639f49484f622fa38ed9a7a" ) }
rule Orphancy_SizeInflated_Filler { strings: $vb5 = "VB5!" ascii $filler = { 71 24 71 24 71 25 71 25 71 24 71 25 71 25 71 25 } condition: uint16(0) == 0x5A4D and filesize == 1576960 and $vb5 and #filler > 100 } rule Orphancy_Filler_Only { strings: $filler = { 71 24 71 24 71 25 71 25 71 24 71 25 71 25 71 25 } condition: uint16(0) == 0x5A4D and #filler > 500 }
8.3 Validation results
The clean test set was node.exe, rg.exe, notepad.exe, mspaint.exe and msvbvm60.dll. None matched. Three of the ten infected files matched the structural rules but not the exact-hash rule, which is the empirical proof that hash-only detection is insufficient here.
| Rule | Purpose | Confidence |
|---|---|---|
| Orphancy_Known_Sample | Exact hashes, zero false positives | High |
| Orphancy_VB6_Replicator | VB6 + MSVBVM60 + padding + metadata | High |
| Orphancy_SizeInflated_Filler | Exact size + padding; catches randomised metadata | Medium |
| Orphancy_Filler_Only | Broad hunt across large file sets | Low–medium |
8.4 Triage without YARA
Two PowerShell passes are enough to find both halves of every infection pair on an unmanaged host. The first finds the fakes; the second finds the displaced originals.
# fakes — size pre-filter, then confirm the VB5! marker Get-ChildItem C:\ -Recurse -Force -File -Include *.exe,*.dll -ErrorAction SilentlyContinue | Where-Object { $_.Length -eq 1576960 } | ForEach-Object { $b = [IO.File]::ReadAllBytes($_.FullName) if ([Text.Encoding]::ASCII.GetString($b).Contains('VB5!')) { $_ } } | Select-Object FullName, CreationTime, LastWriteTime # displaced originals — hidden + system v* files Get-ChildItem C:\ -Recurse -Force -File -Filter 'v*' -ErrorAction SilentlyContinue | Where-Object { ($_.Attributes -band 2) -and ($_.Attributes -band 4) -and $_.Extension -eq '.exe' } | Select-Object FullName, Length
The structural test — same directory, size-exact unsigned PE32 with VB5!, plus a hidden+system v-twin of different size, plus a large creation/modification gap — is more robust than any single indicator and does not depend on knowing the build.
Remediation
Containment was performed by move, not deletion. Every fake was preserved with its original path so the infection could be reconstructed and studied after the host was clean.
- Every fake was moved — not deleted — to
C:\Users\<user>\orphancy-report\evidence\quarantine-orphancy\restore\, preserving its original directory structure. The samples remain excluded from version control. - Each displaced original was renamed back to its true name and had
HiddenandSystemcleared. - The eight orphaned Photoshop support binaries were reconstructed from their hidden originals.
- Both Startup artefacts —
filename.vbsandPaint.lnk— were removed. - Runtimes were verified genuine by execution, not by size.
- An idle observation window confirmed no re-drops once the last hijacked
ripgrepwas replaced.
| Check | Result |
|---|---|
| Files re-scanned | 1,172,494 |
| Live malware instances outside quarantine | 0 |
| Quarantined artefacts | 96 (95 binaries + 1 .exe-wrapper) |
| Originals restored | All displaced v* files |
node --version | v22.22.1 |
rg --version | ripgrep 14.1.1 — all three copies |
gopls version | v0.21.1 |
| Startup folder | Only unrelated shortcuts remain |
Eight further files in the quarantine tree did not match the malware's size and were retained rather than silently discarded, so the quarantine remains an accurate record of what was removed.
Microsoft Defender's real-time protection and tamper protection were disabled on this host. The WinDefend service was stopped, with zero recorded detections. It never flagged any of the 96 artefacts. A clean Defender result on this machine means nothing. Re-enabling it requires an elevated shell that was not available during this analysis.
That last point deserves emphasis beyond this host. A dormant antivirus turns every "no threats found" message into a false negative. Before trusting a clean scan, confirm the scanner is actually running.
The emulation dead end
Static analysis can describe the file but not its full capability. The obvious next step was user-mode emulation. It failed in an instructive way.
Speakeasy 1.5.11 loaded the sample successfully — the PE parsed, the entry point was identified at 0x4013E0 — and then execution terminated immediately:
{
"ep_type": "module_entry",
"start_addr": "0x4013e0",
"error": {
"type": "unsupported_api",
"api_name": "MSVBVM60.ordinal_100",
"pc": "0xfeedf0d0"
},
"apis": []
}
MSVBVM60.ordinal_100 is ThunRTMain, the VB6 runtime entry point. Speakeasy does not implement it, so the emulator never reaches the sample's own code. No API trace, no file activity, no network activity: the run produced 267,998 characters of output that amount to a single unsupported-API error.
The attempt did confirm one thing. The stack showed the pushed argument 0x0056EF10, which is exactly the VB5! header at file offset 0x16EF10. The entry semantics are genuinely VB6, not a forged header pointing at unrelated code.
Getting that far required resolving a dependency chain — Speakeasy pulls in setuptools and unicorn — and the working combination was setuptools 70.0.0 with unicorn 1.0.2. That is recorded here because anyone repeating the attempt will hit the same wall.
User-mode emulation is not viable for this sample. Full behavioural analysis requires a real Windows VM with an installed VB6 runtime — and administrator rights to run it.
Conclusion, §10
Everything in this report about behaviour therefore comes from observed effects on the filesystem, not from watching the code execute. The distinction is the main limitation of the analysis and the main reason the capability question remains open.
Timeline
Reconstructed from filesystem timestamps, process correlation and the quarantine manifest. All times UTC.
- 2019-07-15 00:12Adobe Premiere repack extracted to
C:\ - 2020-01-23 22:48:01Four repack executables replaced — probable patient zero
- 2026-04-11 12:25HP printer driver suite installed, providing fresh targets
- 2026-07-13 11:24:48Mass recursive replication begins
- 2026-07-13 11:24:55Last epoch-2 replacement in the printer suite
- 2026-07-13 11:24:57
node.exereplaced - 2026-07-13 11:24:59
rg.exereplaced - 2026-07-13 12:09:31
filename.exedropped;filename.vbswritten - 2026-07-13 12:09:43
Paint.exedropped;Paint.lnkwritten - 2020 → 2026-10Latent; artefacts re-created on every execution of a hijacked binary
- 2026-10-20Discovered when
nodeproduced no output - 2026-10-20Contained: 96 fakes quarantined, all originals restored, 0 remaining
The eleven-second gap between the first and last replacement in the 2026 wave is the clearest evidence of a recursive directory walk. The earlier timestamps — node.exe two seconds after the printer suite, rg.exe two seconds after that — suggest a traversal that moved across the user profile in a single pass.
Open questions
Five questions could not be answered with the access available. They are listed with the specific evidence that would settle each one, so the report can be extended rather than rewritten.
- Capability and C2 — unknown. No plaintext network indicators and no decryptable strings exist in the file. Settling this requires executing the sample in a VM with the VB6 runtime and capturing network traffic.
- What triggered the 2026-07-13 wave. An infected binary in a parent directory was executed, but which one is not established. The candidates are the four 2020 Adobe files and any epoch-2 binary executed before 11:24:48.
- The static layout anomaly. The VB6 header pointers resolve into zeroed regions while the binary still executes (§3.5). Resolving it requires either a correct VB6 structure map or dynamic observation of the load process.
- Whether anything on
D:was affected. Moot: the host has only one volume,C:. - Whether the 2020 folder predates or postdates a system restore. Timestamps favour on-host infection but are not conclusive (§7).
None of these gaps affects the containment result. The host is clean; what remains unknown is what the sample could have done, not what it did on this machine.
What to call it
Orphancy, Geacata, Braggat0 and Riltroccha Ralocchia are builder-generated strings, not family names. They follow the mutated-word pattern typical of automated name randomisation, and every build shares them while differing only in .rsrc.
The engine is deterministic: same padding, same code, same exact size across all 31 builds. That is the signature of commodity builder output, not bespoke malware. Someone assembled a file-replacement routine with a name generator and a resource randomiser, then ran it.
There is insufficient evidence to attribute this to any known family. “Self-replicating VB6 binary of unknown lineage” is accurate. Naming it as a new family would not be — it would manufacture attribution out of strings that were generated precisely to prevent it.
For detection content, use the structural name Orphancy_VB6_Replicator and describe the behaviour. Do not propagate Orphancy as a family name in intelligence products; it is an artefact of the builder, and other samples from the same builder will carry different strings.
Appendix & method
A. Analysis environment
Host Windows 10 Home, x86-64, non-elevated user context
Tooling Python 3.13, pefile 2024.8.26, capstone,
yara-python, Speakeasy 1.5.11
Method static PE parsing, entropy mapping, section
disassembly, YARA validation, user-mode emulation
attempt, full-disk filesystem sweep
Constraint no administrator rights; no Java; no VM
Note no sample was executed on the production host
B. Full build list
All 31 SHA-256 values recovered from the quarantine, with sample counts. One further artefact was preserved with an .exe-wrapper suffix and is not listed here as a PE build.
| 45 | f88fef68b5e33afc957e538413461d5ab0e2062dacdc01f7f30fbb49fbd046e1 |
| 10 | 3d0c57ad1e7e81432b1baaadfeaf06905c942aed0ecfdb2f334ac75875a7d1fc |
| 3 | dd4be38a5c940d332746b0beaf347b331b1ecc38e1d5cab8cb781fd37635a2c6 |
| 3 | e50c7b68cffb8d2dad17ab59a08fc44f1e6f50b33f37a296271004bc5e39671b |
| 2 | d150a4c3e0dfe3cb7abab8a4a619d4604058b597685426a6667c16c442655723 |
| 2 | 3458cc2a3fbb1ec469b7ecbce9a333c41d2e4d5df77a0a2d6dbde98159a98cdd |
| 2 | da216f1f234e3bff0dea5baa64db0c781825d95bd89f31cb23e171190a812ceb |
| 2 | 8913f39d326fd8e4e9c609614c7e0c91cd7f898877647bcf206fedc4dd94dbf7 |
| 2 | fd6eb745288826bc55933367b9a5f112ed813b143c8f9fe79481fba484a17f6d |
| 2 | a2282e9e2ab2d0cae4b1f488a69e277a9757c3be9975c3aa47f68aa1d34385be |
| 2 | f76d61a208f78d663b18c1b7ebdff148334fd081a0cce685ff4014f8ea780d88 |
| 1 | 426aa907ee34980cdcb2cba153dde6bfcc71e5dbc715d63e224a6266d621f8d4 |
| 1 | 09584f3d4f052b6bb9b1251be492b2a5b85a5dce252198bdfc2cf514962d3a77 |
| 1 | 1fa1a5ba5c19584738ff06c5db40cd21705ee0e1dc754e72367f93f4d6eb433a |
| 1 | 4f14f7c1cf5dc80e44d9057d7c647e75d7997e028810969707b792fb4f7aff68 |
| 1 | 21b66c732c5caa0b858899d3a5084143f2789e1bf639f49484f622fa38ed9a7a |
| 1 | a8de09ba3e74fb777bc819b01c4ddf13bd71fe00e7723100033892be66360df8 |
| 1 | b62c0ca23ed68885f858fa2f53cd4faf7efee5863e4a2093035575ba6a7d3aa4 |
| 1 | 93d2ef6ae65155c202d763b73da8c01f496bd46f115924d06cd61809a696b058 |
| 1 | 1b5170dd94d3374d00c5956621bbe658fbd923a483ccf49a686c054673a12ac5 |
| 1 | 86a623a2b6cd4a19658c3a3b2b580613b55953a892329403ca0237bbc8b69ce7 |
| 1 | 8a0a6145c4bd41e50098bb84a43bbc257a2fe3815a3b5fa41b07984b58b78fe1 |
| 1 | 87cf4399001876281ba37f0913420e932a05c8bda4f99787a5504717aea53a7 |
| 1 | c3a645f95494c696240c3fbe0c0e01f384ee451e6bf4b1e558e4758cfff3c500 |
| 1 | b1438765292c24137d65c0e69ca47700673df503e415c203303fcfcea0e90169 |
| 1 | 618ec5abf12a6320fb5017be44939720780f58fc7c3e6823b52b5e74014b6f0c |
| 1 | 582c589896db6cd7659868b647c0ee53cea23cf903d5797a4854435c6ef08c48 |
| 1 | e5359391b053bcde7ea22e1d81e5e7c24c0ca39d5853a515c517a4d6ebe3901 |
| 1 | e29c38b3997796309878b369849f939136cdb82cf86954dbe40ec3057e30b99d |
| 1 | e8e9a5a8f1efda77e4a73bd556b8c5648f5d730683f5652dd8d0e42bff429496 |
| 1 | db42d828f7eada4b53fc3dee7c281b96f9996960afc5b3c395b09faaa607831c |
C. Indicator summary
%APPDATA%\Paint.exe %TEMP%\subfolder\filename.exe %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\filename.vbs %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\Paint.lnk pairing pattern X.exe 1,576,960 bytes, unsigned, VB5!, MSVBVM60.DLL only vX.exe Hidden + System, different (usually larger) size, original product name in version resource structural signature size exactly 1,576,960 filler 71 24 71 25 ... repeated, entropy 1.50 padding entropy ~5.90, 38.1% zeros, uniform per 64 KB anchor VB5! at 0x16EF10
D. Method notes and limitations
- Every detection was verified structurally, not by size alone. The one size collision on the host is documented (§8.1).
- Behaviour is reconstructed from filesystem effects and process correlation; the code was not decompiled.
- Dynamic analysis was attempted and failed at the VB6 runtime boundary (§10); no network or API behaviour is claimed.
- Timestamp evidence is strong but circumstantial, and is labelled as such wherever it supports the vector assessment.
- No git operations, repository modifications or credential handling were performed as part of the incident response.
1 The reference build is f88fef68…, present 45 times. 2 Quarantine totals include one artefact preserved with an .exe-wrapper suffix. 3 “Epoch” refers to a modification-time cluster, not a guaranteed execution event. 4 Defender detections were zero because the service was stopped, not because the files were clean.