Research/veneficus/the-host-decides
Series0-dayNo CVEHighPublic

Veneficus Mini · Phase 2

The host decides

Spicy malware. Full kill-chain implant: exploit, escalate, pivot, poison, C2. Scoring with actual weights, three-way persist, a retirement path that overwrites the image, and a concealment stack that starts in user mode.

Name
The host decides
Series
Veneficus Mini
Phase
2 · The host decides
Type
0-day analysis
CVE
n/a
CVE Risk
high
Disclosure Status
public
Vendor
n/a (private implant kit, source released)
Affected
Windows 10/11 x64 workstations
Published
04 Sept 2026
Updated
04 Sept 2026
Tags
0-day, malware, sandbox, persistence, amsi, etw, series

The first thing the agent does after reading its baked identity is ask the host a question that comes back as a number rather than a yes or no. assess_environment does not answer "am I in a VM." It returns a score clamped to 0-100, along with a list of reasons, and the orchestrator treats that number as a tax: over 80 means self_destruct::execute(true), over 60 means quiet mode, and anything else means full operation. That is the kind of model you write after a sandbox has burned you once and you have had time to think about which signals actually cost you the sample.

Scoring

The public body is utils.rs.

Signal Delta
Debugger in the process list +30
RAM under 2 GB +20
Fewer than two of Desktop / Documents / Downloads +20
CPU under 2, free C: under 40 GB, no recent user files, 1 ms sleep took over 500 ms, outbound HTTPS to a well-known site fails +15 each
Analysis tools, guest-tool registry, idle several minutes, CPUID hypervisor-present bit +10 each
Hypervisor guest-tool process +5
Many recently modified user files -20
All three user directories -15
A browser is running -10
Outbound HTTPS works -5

Decision: over 80, retire. Over 60, quiet. Else full.

Cheap sandboxes fail this table in the usual ways, and a well-provisioned lab does not: four cores, 8 GB, a browser, recent files, and the debugger not sitting in the process list under a textbook name while the sample runs. The outbound check is itself a detection, because an unexpected HTTPS connection from a process that is pretending to be a health-sync helper is exactly the kind of thing a SOC graphs. The CPUID hypervisor-present bit also fires on Hyper-V on real corporate endpoints and shoves those hosts toward quiet mode. Guest tools and analysis leftovers are the sandbox tells; Hyper-V is also how a lot of honest enterprise desktops are built.

Quiet mode is not a panic exit. The agent still beacons a host card and still polls the control plane; it just refuses to install, refuses to patch, refuses to load a driver, refuses to clip, and refuses to dump. That is the same branch you would want on a canary host, and it is also the branch this kit uses before a clipper starts rewriting pastes.

Concealment

Bring-up order in stealth/ is syscall path, then the stack-cover helper, then kernel hide (feature-gated), then the in-process patches.

Syscalls (syscalls.rs). A fallback chain of public techniques: Halo's Gate / Tartarus Gate-style SSN resolution, ntdll unhook, indirect syscall, then ordinary WinAPI. CallGhost is listed in Cargo.toml as the direct-syscall crate.

Stack-cover (lacuna.rs). Wrappers around lacuna-rs, meant to spoof return stacks on the sensitive calls.

Kernel hide (dkom.rs). Unlink from the process list, or lie to the chained enumerator (DKOM), after a kernel write primitive from the driver pool.

In-process patches (stomp.rs). The standard user-mode pair: the AMSI scan export made to return invalid-arg, and the ntdll ETW event-write export made to return zero. The page becomes writable via VirtualProtect, the bytes go in, and protection is restored. The effect is this process (and children that inherit the mapping); kernel telemetry is unaffected. If the script-scan library is not loaded yet, the handle is null and the patch no-ops.

The stager is supposed to quiet the script engine before the image starts, and that is the path that actually matters for stage 0. The PE then patches itself in case anything in-process still looks. I am not printing the bytes.

Persistence

Three-way OR in persistence.rs: first success wins, all three shell out, and all three are highly visible. They are also three different classes of hold, which is how you survive one of them getting burned.

Method Class Notes
WMI pulse Permanent event subscription in root\subscription Filter fires on a periodic system-uptime sample, about two minutes after boot. Action is the current image. T1546.003.
On-logon task schtasks, run as SYSTEM Needs admin. Masquerade name. T1053.005.
Machine Run key HKLM Run Needs admin. No HKCU fallback.

The names in the tree are in persistence.rs. Hunt the class, then confirm against the file, not a screenshot of this page.

Retirement

Forced wipe in self_destruct.rs uninstalls persistence, then three-pass overwrites the current image (0x00, 0xFF, CSPRNG), marks it for delete, unlinks it, and otherwise schedules delete on reboot (MoveFileEx DELAY_UNTIL_REBOOT). After that it runs wevtutil cl on System, Security, Application, and the PowerShell operational log.

Overwriting a running PE on Windows often fails sharing, so the reboot-pending path is the one that usually fires. Clearing Security needs admin and is itself a high-signal event (T1070.001, event 1102).

Replacement pulls a profile, requires an HTTPS URL, downloads to a throwaway temp name, spawns, and then wipes. There is no signature check and no hash pin, so whoever can write the profile chooses the next payload. That is a feature if you are the operator, and it is a supply chain if anyone else can write the profile.

Next: the teeth. Scoring is the polite part. The teeth are not.