bloodyAD Active Directory Attack Reference
bloodyAD is a Python tool that manipulates Active Directory objects over LDAP and LDAPS from a Linux attacker host. Where BloodHound only maps attack paths, bloodyAD executes them: reset passwords, write SPNs, add RBCD, grant DCSync, add shadow credentials, abuse dMSA accounts and more. It speaks LDAP directly, so there is no binary on disk and no AV surface, and it works with passwords, NT hashes, Kerberos tickets and certificates.
Install and Authentication
Install with pipx. bloodyAD authenticates with a cleartext password, an NT hash (pass-the-hash), a Kerberos ticket (pass-the-ticket) or a certificate (PKINIT / Schannel). --host takes a hostname or IP; for Kerberos use the FQDN and add --dc-ip when the name does not resolve.
Warning
Every write below is logged. Directory service changes raise events such as 4738 (user changed), 4724/4723 (password reset/change), 5136 (object attribute modified) and 4742 (computer changed). Reverse each change with its remove or set counterpart when the engagement allows.
Placeholders Used Here
Every example reuses the same variables. Where each one comes from:
$DOMAIN- domain FQDN, e.g.corp.local.$USER/$PASSWORD/$NTHASH- your current foothold account and its password or NT hash.$TARGET_IP- the DC.--hostaccepts this IP, or the DC FQDN (use the FQDN for Kerberos).$BASE_DN- the domain DN, e.g.DC=corp,DC=local. Each label in$DOMAINbecomes oneDC=component.$TARGET_USER/$TARGET_GROUP/$TARGET_COMPUTER- the sAMAccountName of the object you are abusing, taken straight fromget writableor a BloodHound edge. Computer and gMSA names end in$(e.g.WS01$).$TARGET_OBJECT/$GMSA- a sAMAccountName or a full DN. When two objects share a name, paste the fulldistinguishedNamefromget writableinstead.$LHOST- your attacker IP.Literal values like
fake/dc01(an SPN) orNewPass123!(a new password) are yours to pick; an SPN only has to be unique in the domain.
Anything not in that list - a tombstone DN containing \0ADEL:, an objectGUID, a SID - is never typed by hand. You read it from a get command first, then paste it into the write. Each section below shows which read produces the value the write needs.
Reading bloodyAD Output
A few flags change how results are printed and are worth knowing before you start:
--jsonprints machine-parseable JSON instead of the key/value layout.get objectandget searchemit binary attributes (objectSid, securityDescriptor, msDS-ManagedPassword) as base64; pass--resolve-sdto expand a security descriptor into readable owner and ACE entries.-v DEBUG(orTRACE) shows the raw LDAP exchange when a write is silently rejected.--attrlimits output to the attributes you care about instead of the full object.
Enumeration and Target Selection
get writable is the first command to run with any new credential. It lists exactly which objects and attributes the current identity can modify, which is the input to every attack on this page. Filter with --right WRITE to drop read-only noise.
distinguishedName: CN=svc_backup,CN=Users,DC=corp,DC=local permission: WRITE distinguishedName: CN=Helpdesk,OU=Groups,DC=corp,DC=local permission: WRITE distinguishedName: OU=Servers,DC=corp,DC=local permission: CREATE_CHILD
Read this top to bottom. WRITE on a user enables a password reset, a forged SPN or shadow credentials. WRITE on a group means add a member. WRITE on a computer means RBCD. CREATE_CHILD on an OU means you can create a computer, user or dMSA there. Each maps to a section below.
Reading objects and searching
get object dumps a single object; get search runs an arbitrary LDAP filter. Use them to confirm an attribute before and after a write, and to find roastable accounts and delegation without BloodHound.
Password Reset and Account Takeover
With ForceChangePassword, GenericWrite or GenericAll on a user you can set a new password without knowing the old one. Loud and disruptive: it locks out the real user and breaks service accounts mid-run. Prefer shadow credentials on anything that matters.
[+] Password changed successfully!
SMB 10.10.10.10 445 DC01 [+] corp.local\svc_backup:NewPass123!
Targeted Kerberoasting
GenericWrite or GenericAll on a user lets you write a fake SPN onto it, request a service ticket, and crack it offline. The account does not need to be a real service. Clear the SPN afterwards.
$krb5tgs$23$*svc_backup$CORP.LOCAL$fake/dc01*$a1b2...<snip>
AS-REP Roasting via UAC
GenericWrite lets you flip the DONT_REQ_PREAUTH bit on a user so it becomes AS-REP roastable, then request the encrypted blob without authenticating as them. Remove the flag when done.
[email protected]:9f8e...<snip>
Shadow Credentials (native)
With GenericWrite or GenericAll on a user or computer, bloodyAD writes a key credential to msDS-KeyCredentialLink, authenticates with PKINIT, and returns the account's TGT and NT hash in one command. No password change, fully reversible: the preferred takeover when stealth matters. Requires a DC at Server 2016+ with PKINIT (an enterprise CA present).
[+] KeyCredential generated with sha256 of RSA key: 9c1f... [+] Saved PFX (#PKCS12) certificate & key at /tmp/svc_backup.pfx [+] Authenticating as 'svc_backup' via PKINIT [+] Saved credential cache to svc_backup.ccache [+] NT hash for 'svc_backup': aad3b435b51404eeaad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889
Use --stealth to skip persisting the certificate to disk, and remove shadowCredentials $TARGET_USER to clear the key credential when you no longer need it.
Resource-Based Constrained Delegation (RBCD)
GenericWrite, GenericAll or WriteAccountRestrictions on a computer lets you write its msDS-AllowedToActOnBehalfOfOtherIdentity so an account you control can impersonate any user to that machine. You need a controlled computer account; create one with add computer if the machine account quota allows (default 10 per user).
[+] ATTACKER$ created [+] $TARGET_COMPUTER$ msDS-AllowedToActOnBehalfOfOtherIdentity updated
Granting DCSync Rights
If you own the domain object or have WriteDacl on it (often reached through the WriteOwner chain below), grant your own account the two replication rights, then pull hashes with secretsdump. This is the standard path to the krbtgt hash and a Golden Ticket.
corp.local\krbtgt:502:aad3b435b51404eeaad3b435b51404ee:d3c02561bba6ee4ad6cfd024ec8fda5d:::
The krbtgt hash enables a Golden Ticket. See DCSync and Domain Takeover.
WriteOwner to Full Control
WriteOwner does not grant access by itself. Take ownership, give yourself GenericAll, then run the matching abuse: password reset on a user, AddMember on a group, or add dcsync on the domain object.
Group Membership
GenericWrite or AddMember on a group lets you add any principal. Adding yourself to a high-value group (Domain Admins, DnsAdmins, Backup Operators, Remote Management Users) is a direct escalation. New membership only takes effect in a fresh Kerberos ticket: re-authenticate after adding.
distinguishedName: CN=Domain Admins,CN=Users,DC=corp,DC=local objectSid: S-1-5-21-...-512
gMSA Password Read
If you can write a gMSA's msDS-GroupMSAMembership (or already control a principal that is allowed to retrieve the password), add yourself, then read the managed password. bloodyAD decodes the blob into the NT hash and AES keys for pass-the-hash.
msDS-ManagedPassword.NTLM: aad3b435b51404eeaad3b435b51404ee:8d2c9f0b1a7e6d5c4b3a2f1e0d9c8b7a msDS-ManagedPassword.AES256: 1b2c...<snip>
BadSuccessor (dMSA abuse)
On Windows Server 2025 domains, CreateChild on an OU lets you create a delegated Managed Service Account (dMSA) and link it to any victim via msDS-ManagedAccountPrecededByLink. The DC then issues the dMSA a TGT whose PAC carries the victim's keys, and bloodyAD prints the victim's NT hash as the dMSA's "previous keys". On patched DCs you also need WRITE on the victim, so use --prepatch only against unpatched DCs.
[+] Creating DMSA evilDMSA$ in OU=Staging,DC=corp,DC=local [+] Impersonating: CN=Administrator,CN=Users,DC=corp,DC=local [+] dMSA TGT stored in ccache file evilDMSA_xy.ccache dMSA previous keys found in TGS (including keys of preceding managed accounts): RC4: 7a3b1c9d2e4f5a6b8c0d1e2f3a4b5c6d
The RC4 entry under "previous keys" is the victim's NT hash. Use it for pass-the-hash, or use the saved ccache directly with -k --use-kcache.
Recycle Bin Restore
With WRITE on a tombstone in the Deleted Objects container, you can restore the object. A restored user often reuses its old password, giving instant access.
You do not build the restore target by hand. When an object is deleted, AD rewrites its RDN to CN=<oldname>\0ADEL:<objectGUID>, where the GUID is that object's own objectGUID. So list the Deleted Objects container first and copy the exact distinguishedName from the output. The two -c controls (show deleted, show recycled) are what make tombstones visible to the search; without them the container looks empty. The same DN also appears in get writable when you hold WRITE on the tombstone.
distinguishedName: CN=svc_legacy\0ADEL:1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d,CN=Deleted Objects,DC=corp,DC=local sAMAccountName: svc_legacy lastKnownParent: CN=Users,DC=corp,DC=local
[+] Restored to CN=svc_legacy,CN=Users,DC=corp,DC=local
Creating Users and Computers
CREATE_CHILD on a container or OU lets you add new principals. A new computer account is the building block for RBCD and shadow-credential chains; a new user is useful for persistence under your own control.
ADIDNS Records
Authenticated users can usually create DNS records in the AD-integrated zone. Add a record pointing at your host to enable WPAD or name-spoofing primitives that feed NTLM relay. Keep this in mind as a chaining step rather than a standalone win.
Pair this with relaying. See NTLM Relay.
UAC Flag Reference
add uac sets and remove uac clears userAccountControl flags. The flags worth abusing:
DONT_REQ_PREAUTH(0x400000) - makes the account AS-REP roastable.TRUSTED_FOR_DELEGATION(0x80000) - marks a computer for unconstrained delegation so it caches TGTs of anyone who authenticates to it.TRUSTED_TO_AUTH_FOR_DELEGATION(0x1000000) - enables protocol transition for a constrained-delegation S4U2Self abuse.DONT_EXPIRE_PASSWORD(0x10000) - keeps a backdoor account's password from expiring.ACCOUNTDISABLE(0x2) - disable or, when removed, re-enable an account.
References
-
bloodyAD (GitHub)github.com/CravateRouge/bloodyAD (opens in new tab)
AD privilege escalation toolkit over LDAP: get/set/add/remove modules used throughout this page.
-
bloodyAD Wikigithub.com/CravateRouge/bloodyAD/wiki (opens in new tab)
Full command reference and connection options.
-
Impacket (GitHub)github.com/fortra/impacket (opens in new tab)
GetUserSPNs, GetNPUsers, getST, secretsdump, psexec used to follow up bloodyAD writes.
-
NetExec (nxc) (GitHub)github.com/Pennyw0rth/NetExec (opens in new tab)
Verify recovered credentials and pass-the-hash.
-
BadSuccessor (Akamai)www.akamai.com/blog/security-research/abusing-dmsa-for-privilege-escalation (opens in new tab)
Original research on dMSA-based privilege escalation on Server 2025.
Was this helpful?
Your feedback helps improve this page.