HelloNet Exposed: Reversing the APT That Weaponized ViPNet’s Trusted Update Channel

Somewhere in a Russian energy provider’s network in May 2026, a file named wtsapi32.dll was sitting in a folder it had no business being in. Not System32. Not SysWOW64. It was parked inside C:\Program Files (x86)\InfoTeCS\VIPNet Update System\, right next to the executable that InfoTeCS itself ships to keep the country’s certified VPN software patched. Every reboot, the trusted updater loaded it. Every reboot, an implant chain woke up inside svchost.exe. The whole thing rode in on the one process nobody audits: the security tool that is supposed to be protecting you.

That is the story Kaspersky’s Securelist team told in mid-July 2026, and it is worth dissecting layer by layer, because the tradecraft here is clean, modular, and deliberately quiet. This is a full teardown of HelloNet: the sideloaded loader (HelloInjector), the in-memory proxy and module host (HelloProxy), the reconnaissance and log-scrubbing tools (HelloExecutor and HelloCleaner), the Rust C2 (HelloBackdoor), and the renamed PuTTY tunnel that stitched it all back to a single command server.


Why ViPNet’s Update Channel Is the Ideal Delivery Mechanism

Start with the target, because it explains everything downstream. ViPNet is a family of Russian information-security products from InfoTeCS: VPN, endpoint protection, firewall, certificate management, centralized administration, secure messaging, and file transfer. In Russia it is not optional decoration. It carries government certification for regulated and state environments, which means you find it deployed across the exact sectors HelloNet went after: government, energy, transport, logistics, education, and heavy industry.

The strategic insight the operators exploited is uncomfortable but obvious once you see it. A software update pipeline is a trust relationship encoded in software. The updater runs at boot, runs with privilege, reaches out to the network by design, and is explicitly exempted from most “why is this process doing that?” alerting. Analysts learn to ignore it. EDR tuning learns to ignore it. If you can get code executing inside that pipeline, you inherit all of that trust for free.

The key process is itcsrvup64.exe (or itcsrvup.exe on 32-bit), the ViPNet Update System service that launches at OS startup. It is signed by InfoTeCS. It is expected to phone home. And, as the campaign proved, it loads at least one dependency by name rather than by full path, which is the crack the whole operation was driven through.

This is also not InfoTeCS’s first rodeo with this actor. Back in April 2025 a separate campaign shipped a backdoor inside LZH archives that mimicked ViPNet’s update structure, bundling action.inf, a legitimate lumpdiag.exe, a malicious msinfo32.exe loader, and an encrypted payload. Different mechanics, same obsession: ViPNet as the wedge. Treat the two as distinct campaigns, but read them together as a pattern. Someone keeps coming back to this vendor.


DLL Sideloading Fundamentals and the wtsapi32.dll Choice

DLL sideloading works because of how Windows resolves an unqualified module name. When a process calls LoadLibrary("wtsapi32.dll") or has the import listed in its PE import table without a full path, the loader walks the DLL search order. With Safe DLL Search Mode enabled (the default), the order roughly is:

  1. The directory the application was loaded from
  2. The system directory (System32)
  3. The 16-bit system directory
  4. The Windows directory
  5. The current directory
  6. Directories on %PATH%

Notice step one. The application’s own directory beats System32. wtsapi32.dll is the Windows Terminal Services API library, and it legitimately lives in C:\Windows\System32\. But if itcsrvup64.exe imports it by name, a same-named DLL sitting in the ViPNet install folder is loaded first. The legitimate one in System32 never gets a look.

wtsapi32.dll is a well-worn sideloading target. HijackLibs tracks it against roughly 18 vulnerable applications, which tells you this is a known, reusable primitive rather than a one-off discovery. The operators did not need a novel bug. They needed a signed process with a lazy import and a writable install directory, and ViPNet handed them both.

One classification note worth getting right: this is T1574.002 (DLL Side-Loading), not T1574.001 (DLL Search-Order Hijacking). The distinction is that side-loading rides a legitimately installed, signed application by planting a malicious DLL next to it, which is precisely what happened here. The victim binary is real and signed; only the dependency is poisoned.


HelloInjector: From Boot to svchost.exe

The malicious wtsapi32.dll is HelloInjector, and its only job is to get real code out of the ViPNet updater’s process and into a stealthier host. To keep itcsrvup64.exe from crashing on load, a competent sideload DLL forwards the real exports, either through a .def file or linker pragmas, so every legitimate call still resolves against the genuine System32 library.

The injection logic follows a clean target-selection routine. If HelloInjector finds it is not already running inside svchost.exe, it enumerates every process, hunting for one whose name contains svchost and whose command line contains netsvcs. The netsvcs service host group is a good hiding spot: it is always present, it hosts a crowd of legitimate services, and outbound connections from it draw little suspicion.

Once it finds that target, it injects using direct NT-layer calls rather than the classic Win32 wrappers:

// Illustrative HelloInjector target-select + inject pattern (x64)
// Direct NT calls chosen to sidestep IAT/inline hooks on CreateRemoteThread

HANDLE hProc = NULL;
CLIENT_ID cid = { (HANDLE)targetPid, NULL };
OBJECT_ATTRIBUTES oa = { sizeof(oa) };

NtOpenProcess(&hProc, PROCESS_ALL_ACCESS, &oa, &cid);

PVOID remote = NULL;
SIZE_T size = payloadLen;
NtAllocateVirtualMemory(hProc, &remote, 0, &size,
                        MEM_COMMIT | MEM_RESERVE,
                        PAGE_EXECUTE_READWRITE);

NtWriteVirtualMemory(hProc, remote, payload, payloadLen, NULL);

HANDLE hThread = NULL;
NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL, hProc,
                 (PVOID)remote, NULL, FALSE, 0, 0, 0, NULL);

The choice of NtWriteVirtualMemory and NtCreateThreadEx is deliberate. Plenty of user-mode EDR hooks sit on WriteProcessMemory and CreateRemoteThread in kernel32/kernelbase. Dropping down to the ntdll syscall stubs (or issuing direct syscalls) skips those hooks entirely. After the remote thread fires, HelloInjector re-checks that it is now living inside a process whose name contains svchost, and once confirmed it loads and runs the next-stage payload. That payload is stored in the DLL body in plain text, which is sloppy from an anti-analysis standpoint but great for detection engineers.

The persistence story is elegant: no run key, no scheduled task, no new service. The updater already runs at boot, so the sideload inherits its lifecycle. That maps to T1543.003 by virtue of hijacking the update service, but there is no additional autorun artifact to find, which is exactly why you have to detect the load itself.


Flowchart showing HelloNet injection chain from OS boot through itcsrvup64.exe loading the malicious wtsapi32.dll, [DLL search order](https://genxcyber.com/pe-file-format-deep-dive/) hijack, NT-layer process injection into svchost.exe, and HelloProxy executing in memory
HelloInjector rides the ViPNet updater’s boot lifecycle and uses direct NT syscalls to land HelloProxy inside a trusted svchost.exe process group.

HelloProxy: An In-Memory Relay and a Module Host

The payload that lands in svchost.exe is HelloProxy, and it plays two roles at once: hidden proxy and module loader.

Its stealth comes from function interception via the Microsoft Detours library. HelloProxy hooks three functions:

Hooked functionPurpose of the hook
closesocketPrevent premature closure of C2 sockets
shutdownSame, block teardown of the C2 channel
NtDeviceIoControlFileCarries the main malicious logic

The closesocket and shutdown hooks are a survival mechanism. svchost.exe is a busy process with legitimate socket churn, and normal service code closing sockets could tear down the covert channel by accident. By intercepting those calls, HelloProxy keeps its own C2 sockets alive while letting everything else pass. NtDeviceIoControlFile is where the actual command dispatch and proxy control live, because on Windows a huge amount of socket I/O ultimately funnels through device IOCTLs on \Device\Afd, the Ancillary Function Driver behind Winsock.

A Detours install for this pattern looks like the following:

// HelloProxy-style hook install with Microsoft Detours
static int (WSAAPI *Real_closesocket)(SOCKET) = closesocket;

int WSAAPI Hook_closesocket(SOCKET s) {
    if (IsC2Socket(s)) return 0;         // pretend success, keep it open
    return Real_closesocket(s);
}

DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)Real_closesocket, Hook_closesocket);
// ... DetourAttach for shutdown and NtDeviceIoControlFile ...
DetourTransactionCommit();

In proxy mode HelloProxy takes address:port strings from the C2, opens fresh sockets, and relays traffic between them, turning the compromised host into a pivot inside the victim network on ports 5003 and 5060. As a loader it does something worse for defenders: it receives an executable from the command server, maps it into its own process memory, and runs it on a separate thread. No file touches disk. Every follow-on module runs reflectively inside a signed, trusted svchost.exe. That is T1055, T1090 (proxy), and T1105 (ingress tool transfer) all riding one process.


Illustration of HelloProxy as a ghost inside a trusted process, relaying network traffic and injecting malicious modules invisibly
HelloProxy hides inside svchost.exe, hooking socket calls to keep C2 channels alive while reflectively loading follow-on modules entirely in memory.

HelloExecutor and HelloCleaner: Recon and Cover Tracks

Two of the modules HelloProxy pulled down handle the unglamorous middle of the operation.

HelloExecutor is a command-execution backdoor used for reconnaissance across the infected organizations. It runs shell commands and probes the network. In the observed activity it repeatedly touched C:\Users\Public\Music, an inspired staging choice. Public is world-writable, it is not a temp folder that triggers reflex alerting, and a “Music” subfolder looks like noise. The recon here lights up a cluster of discovery techniques: T1082 (system info), T1016 (network config), T1049 (network connections), T1057 (process discovery), T1018 (remote system discovery), and T1083 (file and directory discovery).

HelloCleaner is the janitor. It removes ViPNet log data specifically to erase the traces of the sideload and injection chain. That surgical targeting matters. It is not wiping the whole Security event log and screaming “someone tampered here”; it is scrubbing the ViPNet-specific logs that would otherwise expose the abuse of the update component. That is T1070.004 (file deletion) executed with knowledge of exactly which artifacts implicate the intrusion, which is a strong signal of an operator who understood the target software intimately.


HelloBackdoor: Reversing the Rust C2

The crown jewel is HelloBackdoor, written in Rust. It supports file upload, file download, and command execution (arbitrary commands via cmd.exe), plus self-termination. Nothing exotic in the feature set. The interesting part is the activation handshake and the reversing headache Rust introduces.

HelloBackdoor listens on TCP port 443 and stays completely silent until a client sends a specific token. Kaspersky reports the expected string as 47c6235b4d2611184, described as part of the MD5 hash of the string hello\n. Worth being precise here, because I want you to be able to reproduce and verify this: the full MD5 of hello\n is 47c6235b4d26111834aeda84b7b8024b. The first 16 hex characters are 47c6235b4d261118 and the second half is 34aeda84b7b8024b. The token Kaspersky publishes, 47c6235b4d2611184, is 17 characters and lines up with the first half plus one extra nibble, which looks like a truncation or off-by-one artifact in reporting rather than a clean “second half.” If you are building detection off this, key on the substring exactly as published and note the ambiguity. Do not silently “correct” it to the mathematically tidy half, because the wire behavior is what the malware actually checks.

The handshake is a cheap but effective anti-analysis and anti-scan control. A researcher pointing a scanner at port 443 gets nothing. Only a client that presents the exact token gets a command loop. Anything else is dropped silently. A minimal lab reconstruction (offset to port 4433 so you never touch anything real) makes the logic clear:

use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

// Lab-only reconstruction of the HelloBackdoor gate logic.
const TOKEN: &[u8] = b"47c6235b4d2611184"; // as published by Securelist

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:4433").await?;
    loop {
        let (mut sock, _) = listener.accept().await?;
        let mut buf = [0u8; 32];
        let n = sock.read(&mut buf).await?;
        if &buf[..n.min(TOKEN.len())] != TOKEN {
            // wrong handshake: drop quietly, defeat naive scanners
            continue;
        }
        sock.write_all(b"ready\n").await?;
        // command loop: exec / upload / download / self-terminate
    }
}

Rust is a genuine problem for signature writers, and that is likely part of why they chose it. A cargo build --release binary is statically linked, strips most symbols, and drops you into LLVM-generated code with heavy monomorphization and inlining. There is no tidy import table to anchor on, no C runtime landmarks you are used to. What you do get are Rust’s own panic infrastructure strings. In Ghidra or IDA you will find panicked at, thread 'main' panicked, and source-path fragments baked into the panic metadata. Those become your least-bad YARA anchors, alongside the handshake token itself, which is the only truly high-confidence string in the sample.


The SSH Tunnel Layer: Renamed PuTTY

For interactive access and pivoting back out, the operators leaned on a renamed legitimate tool rather than custom code. From C:\Users\Public\Music they launched a PuTTY/Plink binary renamed to frontpage.exe, opening an SSH tunnel from the victim infrastructure to their command server at 5.39.253[.]206. That is T1572 (protocol tunneling) plus T1036 (masquerading), and it is a smart operational choice: SSH traffic is mundane, PuTTY is a signed, trusted, whitelisted-everywhere utility, and renaming it defeats naive allowlists keyed on filename while keeping a valid Authenticode signature.

The rename is also the tell. Legitimate PuTTY does not ship as frontpage.exe, and the PE version metadata betrays the swap:

# Filename says frontpage; the PE metadata does not lie
$info = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("C:\Users\Public\Music\frontpage.exe")
$info.OriginalFilename   # expect PUTTY.EXE / plink, not frontpage.exe
Get-AuthenticodeSignature "C:\Users\Public\Music\frontpage.exe" | Select Status, SignerCertificate

Any process launched out of C:\Users\Public\* with -R, -L, or -ssh on its command line should be treated as hostile until proven otherwise.


Initial Access: Abusing ViPNet’s MFTP Update Trust

The sideload explains persistence, but how did the first malicious file land inside a certified VPN product’s directory? InfoTeCS’s own advisory points at the MFTP transport protocol in ViPNet Client 4. The attack scenario surfaced on ViPNet networks at nodes running ViPNet Administrator. The prerequisite is significant: the attacker must already have compromised a ViPNet Administrator controlling node inside a trusted network. From that captured node, they could send a specially crafted envelope that imitates a legitimate software update, propagating the payload to client nodes over the trusted channel.

That is the crux of the whole campaign as a supply-chain lesson. The update trust is transitive. Compromise one Administrator node and every client that trusts it becomes reachable through a mechanism that looks, to every downstream defense, exactly like a normal patch.

The affected versions from the InfoTeCS advisory:

ProductVulnerable up to
ViPNet Client 44.5.3 build 65211
ViPNet Client 44.5.5 build 24733
ViPNet Administrator4.6.11.5113

Patch to at or above those builds, and treat every ViPNet Administrator node as a Tier-0 asset, because it functionally is one.


Hierarchy diagram showing how a compromised ViPNet Administrator node forges an MFTP update envelope that propagates the malicious wtsapi32.dll to multiple downstream ViPNet Client nodes via the trusted update channel
Compromising a single ViPNet Administrator node makes the MFTP update trust transitive – every downstream client becomes a delivery target indistinguishable from a legitimate patch.

IOC Compendium and YARA

Pull the concrete indicators into one place.

TypeIndicator
C2 IP (SSH tunnel)5.39.253[.]206
Ports443 (HelloBackdoor), 5003 and 5060 (HelloProxy)
Handshake token47c6235b4d2611184 (as published; substring of MD5 of hello\n)
Malicious DLLwtsapi32.dll in C:\Program Files (x86)\InfoTeCS\VIPNet Update System\
Renamed PuTTYfrontpage.exe in C:\Users\Public\Music\
Staging dirC:\Users\Public\Music\
Host processitcsrvup64.exe / itcsrvup.exe
Injection targetsvchost.exe -k netsvcs

For the Rust backdoor, anchor on the handshake token first and treat everything else as supporting context:

rule HelloBackdoor_Rust_C2 {
    meta:
        description = "HelloNet HelloBackdoor Rust C2 implant"
        author      = "GenXCyber"
        reference   = "securelist.com/tr/hellonet-vipnet/120700/"
        status      = "experimental"
    strings:
        $md5_token = "47c6235b4d2611184" ascii    // high-confidence anchor
        $rust_p1   = "panicked at" ascii
        $rust_p2   = "thread 'main' panicked" ascii
        $cmd       = "cmd.exe" ascii wide
    condition:
        uint16(0) == 0x5A4D and filesize < 15MB and
        ($md5_token or ($rust_p1 and $rust_p2 and $cmd))
}
rule HelloInjector_wtsapi32_Sideload {
    meta:
        description = "HelloNet HelloInjector wtsapi32.dll loader"
        author      = "GenXCyber"
        status      = "experimental"
    strings:
        $path  = "InfoTeCS\\VIPNet Update System" wide ascii nocase
        $nt1   = "NtWriteVirtualMemory" ascii
        $nt2   = "NtCreateThreadEx" ascii
        $tgt   = "netsvcs" ascii wide
    condition:
        uint16(0) == 0x5A4D and $path and ($nt1 or $nt2) and $tgt
}

These are hunting rules, not production-hardened signatures. Validate against real samples from a TI feed before you deploy, and keep the experimental status until you have.


Detection, Hunting, and Hardening

The good news is that a quiet chain leaves loud primitives if you are watching the right telemetry. The single highest-value signal is a signed process loading an unsigned or wrongly-signed DLL from a non-standard path.

Sysmon coverage that matters here:

Event IDWhat to catch
7 (Image Loaded)itcsrvup64.exe loading wtsapi32.dll from the ViPNet dir; check Signed/SignatureStatus
11 (File Create)wtsapi32.dll written into C:\Program Files (x86)\InfoTeCS\
8 / 10 (CreateRemoteThread / ProcessAccess)itcsrvup64.exe touching svchost.exe with VM_WRITE + CREATE_THREAD rights
1 (Process Create)frontpage.exe launched from Public\Music; svchost spawning odd children
3 (Network Connection)outbound 443/5003/5060 from svchost.exe

A Sigma skeleton for the sideload catches the whole thing at load time:

title: HelloNet HelloInjector - wtsapi32.dll Sideload in ViPNet Directory
status: experimental
logsource:
  product: windows
  category: image_load
detection:
  selection:
    Image|endswith: '\itcsrvup64.exe'
    ImageLoaded|contains: 'VIPNet Update System\wtsapi32.dll'
  filter_legit:
    ImageLoaded|startswith:
      - 'C:\Windows\System32\'
      - 'C:\Windows\SysWOW64\'
  condition: selection and not filter_legit
level: critical
tags: [attack.persistence, attack.defense_evasion, attack.t1574.002]

On the network side, the handshake is a gift because it is a fixed content string on a fixed port:

alert tcp any any -> $HOME_NET 443 (
  msg:"HelloNet HelloBackdoor activation handshake";
  content:"47c6235b4d2611184"; depth:20;
  flow:to_server,established;
  classtype:trojan-activity; sid:9000001; rev:1;
)

For the injection itself, the gold standard is the Microsoft-Windows-Threat-Intelligence ETW provider (ETWTI), which surfaces KERNEL_THREATINT_TASK_ALLOCVM_REMOTE and KERNEL_THREATINT_TASK_WRITEVM_REMOTE events that catch remote NtAllocateVirtualMemory and NtWriteVirtualMemory even when the actor uses direct syscalls to dodge user-mode hooks. The catch: ETWTI is only consumable by a PPL or kernel-level agent (Defender, CrowdStrike Falcon, and similar). If your EDR does not have it, lean on Kernel-Process events and Sysmon 8/10 instead. Kaspersky’s own coverage names the KEDR Expert rule vipnet_load_library_code_injection and MDR detections built on monitoring wtsapi32.dll creation in the ViPNet directory plus unusual, unsigned children of itcsrvup64.exe.

Hardening that actually breaks this chain:

  1. Enforce a WDAC or AppLocker policy requiring every DLL loaded by itcsrvup64.exe to carry a valid InfoTeCS Authenticode signature. An unsigned wtsapi32.dll from the app directory gets blocked outright, and the entire chain dies at stage zero.
  2. Confirm Safe DLL Search Mode is on (HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeDLLSearchMode).
  3. Enable Process Creation auditing with command lines (auditpol /set /subcategory:"Process Creation" /success:enable) and File System object-access auditing on C:\Program Files (x86)\InfoTeCS\.
  4. Patch ViPNet Client 4 to at least 4.5.3 build 65211 or 4.5.5 build 24733, and ViPNet Administrator to at least 4.6.11.5113.
  5. Alert on any outbound 5003/5060 and on any process out of C:\Users\Public\* carrying SSH flags.
  6. Treat ViPNet Administrator nodes as Tier-0. Their compromise is the whole initial-access story.

Attribution: A Low-Confidence Chinese-Speaking Cluster

Here is where I want you to be disciplined. Kaspersky put this at low confidence, and the honest read is that the evidence is thin and partly consistent with a false flag. Two artifacts get cited: an unused sina.com string inside the tooling, and a download mirror associated with USTC (the University of Science and Technology of China). Both nudge toward a Chinese-speaking cluster.

Now weigh what those actually prove. An unused string is dead code or a leftover, easy to plant and easy to fake precisely because it costs the operator nothing and reads as a “smoking gun” to a rushed analyst. A download mirror is infrastructure, and infrastructure is rented, borrowed, and reused across unrelated actors constantly. Neither one is a behavioral fingerprint. Compare that to what genuinely tends to survive false-flag scrutiny: consistent operational tempo across timezones, code reuse tied to a known toolkit, victimology that fits a known collector’s requirements, and compilation or build artifacts that are hard to spoof. HelloNet’s public evidence is mostly the flimsy category.

The practical doctrine: false flags are cheap on exactly these signals, so treat unused strings and shared mirrors as leads, never conclusions. For a defender, attribution should change almost nothing about your response. You patch the versions, you enforce the WDAC policy, you hunt the load event and the handshake, regardless of whose flag is flying. Save the geopolitics for the threat-intel report and act on the technical facts, which are unambiguous.


The Bigger Lesson: Update Pipelines Are Attack Surface

The reason to care about HelloNet beyond one country’s infrastructure is that it is a clean template. Find a signed, boot-launched, network-facing security or VPN agent. Find a lazy import or a writable install directory. Ride the trust that everyone extends to that agent by default. Use the vendor’s own update transport for lateral movement so your propagation looks like patching. Scrub the vendor-specific logs on the way out. Nothing here is ViPNet-specific in principle; it is a recipe that works against any product whose update channel is trusted more than it is verified.

The architectural takeaway for defenders is that “it is our security software” has to stop being a reason to lower scrutiny and start being a reason to raise it. Security agents run privileged, run early, and talk to the network. That is the exact profile an attacker wants to inherit. Signature-enforce their DLL loads, treat their management nodes as Tier-0, and instrument their processes at least as heavily as you would instrument a browser or an Office suite. The update pipeline you trust the most is the one an APT wants the most.


Illustration of a trusted update pipeline being covertly compromised through an unsupervised access point, representing how security software update channels become attack surfaces
The update pipeline you trust the most is the one an APT wants most – security software’s privileged, boot-time, network-facing profile is exactly the attack surface Hello Net was designed to inherit.

Key Takeaways

  • HelloNet’s entire foothold rests on one sloppy primitive: itcsrvup64.exe loading wtsapi32.dll by name, letting a planted DLL in the ViPNet directory win the search order. WDAC signature enforcement on that load kills the whole chain.
  • The implant chain is modular and mostly fileless after stage zero: HelloInjector into svchost.exe -k netsvcs via direct NtWriteVirtualMemory/NtCreateThreadEx, then HelloProxy hosting HelloExecutor, HelloCleaner, and the Rust HelloBackdoor in memory.
  • The port-443 handshake token 47c6235b4d2611184 is your single strongest detection anchor. Match it exactly as published; do not “correct” the truncation ambiguity, because the malware checks the wire bytes, not the tidy math.
  • Renamed PuTTY (frontpage.exe) tunneling to 5.39.253[.]206 shows how signed LOLBins beat filename allowlists. Check OriginalFilename in PE metadata, not the file name.
  • Attribution is low confidence and consistent with a false flag. The sina.com string and USTC mirror are leads, not proof. Respond to the technical facts, not the flag.
  • The real lesson is structural: trusted update pipelines are a first-class attack surface. The security tool you audit the least is the one an APT will want the most.

Related Tutorials

References