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.