Field Notes / Malware
Incident report 20 Oct 2026 TLP:CLEAR

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.

Fig. 1 — The specimen wall. One tile per quarantined artefact, shaded by build frequency. Ninety-five binaries across 31 distinct builds, plus one artefact preserved with an .exe-wrapper suffix. The dominant build accounts for 45 of them; twenty builds appear exactly once.
96
artefacts quarantined
31
distinct builds
1,172,494
files scanned
2
infection epochs
11s
for the 2026 wave
0
live instances left
01

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.

The trap that shaped the investigation

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.

02

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.

Sample identity — reference build
SHA-256f88fef68b5e33afc957e538413461d5ab0e2062dacdc01f7f30fbb49fbd046e1
Size1,576,960 bytes — exactly 1540 KB, identical in every sample
FormatPE32, x86 (32-bit), GUI subsystem
LanguageVisual Basic 6, native compilation
PackerNone — standard .text / .data / .rsrc
SignatureUnsigned. Get-AuthenticodeSignature returns NotSigned
PE timestamp2017-01-15 21:05:45 UTC — identical in every build, therefore templated or forged
ImportsMSVBVM60.DLL only
Entry point0x4013E0
VB6 headerVB5! 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.

Fig. 2 — Build frequency. Twenty of the 31 builds appear exactly once. The full hash list is in the appendix. Build frequency is not an indicator of capability: all builds share identical code.

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:

version resource
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).

03

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:

Declared imports by library
DLLFunctions
kernel32EnumDateFormatsExA, GetLastError, lstrcatA, SetLocalTime, WriteConsoleOutputAttribute, VirtualAllocEx
user32CopyRect, FreeDDelParam, ExcludeClipRect, GetForegroundWindow, SetParent, SetConsoleWindowInfo
gdi32GetViewportExtEx
winmmauxGetDevCapsA, 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:\, temp and TMP.

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%
Fig. 3 — Byte map, whole file. A 1,576,960-byte executable in which 86 % is padding and 2.9 % is the only region with real instruction density. The filler pattern 71 24 71 25 … is byte-identical across every build and is the most reliable structural fingerprint in the file.
Region-by-region breakdown
File rangeContentCharacter
0x001000–0x001800PE entry thunk and initial dataSmall, real
0x001800–0x011000Repeating 71 24 71 25 …Entropy 1.50
0x011000–0x1D000TransitionMixed
0x1D000–0x169000Synthetic padding, uniform per 64 KB blockEntropy ≈5.90, 38.1 % zeros
0x16EF10VB5! headerAnchor for all validation
0x171480–0x171D20ExternalTable / Declare recordsStandard VB6
0x171000–0x174000The 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:

Embedded-payload search
Signature classResult
Embedded PE files1 — the file itself
ZIP / GZIP / CAB / RAR / 7z / OLE containersNone
AES S-box and inverse S-boxNone
SHA-256 round constantsNone
MD5 constantsNone

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:

header pointers → file offsets
+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:

  1. the VB6 header offsets used during analysis are incorrect, and the real code lies somewhere else in the file;
  2. the binary reconstructs its code in memory at load time, so the on-disk layout is intentionally not executable as-is;
  3. it is a hollow template whose behaviour is delivered entirely through VB6 runtime mechanisms that static layout analysis does not expose.
Effect on confidence

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.

04

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:

  1. Rename X.exe to vX.exe.
  2. Set Hidden + System on vX.exe.
  3. Write a copy of itself to X.exe.
  4. 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.

reconstructed behaviour — not decompiled source
// 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:

Timestamp evidence — HP printer suite
FileCreatedModifiedState
HpSetup.exe2026-04-11 12:25:172026-07-13 11:24:48Fake
wificonfig.exe2026-04-11 12:25:192026-07-13 11:24:55Fake
vHpSetup.exe2026-04-11 12:25:172026-04-11 12:25:17Genuine

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:

Startup artefacts
ArtifactLaunches
%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:

Startup\filename.vbs
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:

process correlation, 2026-07-13
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.

05

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.

4
files · 2020 epoch
92
files · 2026 epoch
60+
directories
11 s
2026 wave duration

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:

epoch 1
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:

epoch 2 — representative paths
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.

Why the 2026 wave was so visible

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.

06

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:

Orphaned Photoshop components
ComponentComponent
Droplet Template.exeCEPHtmlEngine.exe
Adobe Spaces Helper.exeAdobe QT32 Server.exe
dynamiclinkmanager.exeamecommand.exe
dynamiclinkmediaserver.exeImporterREDServer.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.

07

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.

Caveat — timestamp evidence is circumstantial

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.

08

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

Do not match on size alone

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.

orphancy.yar — rules 1 and 2
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" )
}
orphancy.yar — rules 3 and 4 (hunt)
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

10 / 10
infected files detected
0 / 5
false positives
3
builds caught by structure, missed by hash

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 purposes
RulePurposeConfidence
Orphancy_Known_SampleExact hashes, zero false positivesHigh
Orphancy_VB6_ReplicatorVB6 + MSVBVM60 + padding + metadataHigh
Orphancy_SizeInflated_FillerExact size + padding; catches randomised metadataMedium
Orphancy_Filler_OnlyBroad hunt across large file setsLow–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.

triage.ps1
# 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.

09

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.

  1. 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.
  2. Each displaced original was renamed back to its true name and had Hidden and System cleared.
  3. The eight orphaned Photoshop support binaries were reconstructed from their hidden originals.
  4. Both Startup artefacts — filename.vbs and Paint.lnk — were removed.
  5. Runtimes were verified genuine by execution, not by size.
  6. An idle observation window confirmed no re-drops once the last hijacked ripgrep was replaced.
Post-remediation verification
CheckResult
Files re-scanned1,172,494
Live malware instances outside quarantine0
Quarantined artefacts96 (95 binaries + 1 .exe-wrapper)
Originals restoredAll displaced v* files
node --versionv22.22.1
rg --versionripgrep 14.1.1 — all three copies
gopls versionv0.21.1
Startup folderOnly 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.

What could not be fixed without administrator rights

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.

10

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:

speakeasy output (excerpt)
{
  "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.

11

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:57node.exe replaced
  • 2026-07-13 11:24:59rg.exe replaced
  • 2026-07-13 12:09:31filename.exe dropped; filename.vbs written
  • 2026-07-13 12:09:43Paint.exe dropped; Paint.lnk written
  • 2020 → 2026-10Latent; artefacts re-created on every execution of a hijacked binary
  • 2026-10-20Discovered when node produced 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.

12

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.

  1. 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.
  2. 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.
  3. 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.
  4. Whether anything on D: was affected. Moot: the host has only one volume, C:.
  5. 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.

13

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.

Naming guidance

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.

14

Appendix & method

A. Analysis environment

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.

31 builds · 95 binary samples
45f88fef68b5e33afc957e538413461d5ab0e2062dacdc01f7f30fbb49fbd046e1
103d0c57ad1e7e81432b1baaadfeaf06905c942aed0ecfdb2f334ac75875a7d1fc
3dd4be38a5c940d332746b0beaf347b331b1ecc38e1d5cab8cb781fd37635a2c6
3e50c7b68cffb8d2dad17ab59a08fc44f1e6f50b33f37a296271004bc5e39671b
2d150a4c3e0dfe3cb7abab8a4a619d4604058b597685426a6667c16c442655723
23458cc2a3fbb1ec469b7ecbce9a333c41d2e4d5df77a0a2d6dbde98159a98cdd
2da216f1f234e3bff0dea5baa64db0c781825d95bd89f31cb23e171190a812ceb
28913f39d326fd8e4e9c609614c7e0c91cd7f898877647bcf206fedc4dd94dbf7
2fd6eb745288826bc55933367b9a5f112ed813b143c8f9fe79481fba484a17f6d
2a2282e9e2ab2d0cae4b1f488a69e277a9757c3be9975c3aa47f68aa1d34385be
2f76d61a208f78d663b18c1b7ebdff148334fd081a0cce685ff4014f8ea780d88
1426aa907ee34980cdcb2cba153dde6bfcc71e5dbc715d63e224a6266d621f8d4
109584f3d4f052b6bb9b1251be492b2a5b85a5dce252198bdfc2cf514962d3a77
11fa1a5ba5c19584738ff06c5db40cd21705ee0e1dc754e72367f93f4d6eb433a
14f14f7c1cf5dc80e44d9057d7c647e75d7997e028810969707b792fb4f7aff68
121b66c732c5caa0b858899d3a5084143f2789e1bf639f49484f622fa38ed9a7a
1a8de09ba3e74fb777bc819b01c4ddf13bd71fe00e7723100033892be66360df8
1b62c0ca23ed68885f858fa2f53cd4faf7efee5863e4a2093035575ba6a7d3aa4
193d2ef6ae65155c202d763b73da8c01f496bd46f115924d06cd61809a696b058
11b5170dd94d3374d00c5956621bbe658fbd923a483ccf49a686c054673a12ac5
186a623a2b6cd4a19658c3a3b2b580613b55953a892329403ca0237bbc8b69ce7
18a0a6145c4bd41e50098bb84a43bbc257a2fe3815a3b5fa41b07984b58b78fe1
187cf4399001876281ba37f0913420e932a05c8bda4f99787a5504717aea53a7
1c3a645f95494c696240c3fbe0c0e01f384ee451e6bf4b1e558e4758cfff3c500
1b1438765292c24137d65c0e69ca47700673df503e415c203303fcfcea0e90169
1618ec5abf12a6320fb5017be44939720780f58fc7c3e6823b52b5e74014b6f0c
1582c589896db6cd7659868b647c0ee53cea23cf903d5797a4854435c6ef08c48
1e5359391b053bcde7ea22e1d81e5e7c24c0ca39d5853a515c517a4d6ebe3901
1e29c38b3997796309878b369849f939136cdb82cf86954dbe40ec3057e30b99d
1e8e9a5a8f1efda77e4a73bd556b8c5648f5d730683f5652dd8d0e42bff429496
1db42d828f7eada4b53fc3dee7c281b96f9996960afc5b3c395b09faaa607831c

C. Indicator summary

file-system indicators
%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.