# The OSCP Toolkit: bloodyAD

*Executing the Active Directory attack paths BloodHound finds, from a Linux box.*

## What bloodyAD is, and where it fits

BloodHound is very good at one thing: showing you what's possible in an Active Directory environment. It collects the directory, maps the relationships between users, groups, and computers, and highlights the paths that lead from a low-privileged account to Domain Admin. What it doesn't do is act on any of it. BloodHound shows you the path but doesn't walk it, and walking it is the job bloodyAD is built for.

bloodyAD is an Active Directory privilege-escalation framework. In practical terms, it's a single Linux tool that logs into a domain controller over LDAP and lets you read, add, and modify objects in the directory: users, groups, computers, permissions, and delegation settings.

It authenticates with whatever credential you have. A cleartext password, an NT hash, a Kerberos ticket, or a certificate all work, which matters on a real engagement where you rarely start with a clean password.

### Why bloodyAD instead of the alternatives

Plenty of tools abuse Active Directory permissions, so it's fair to ask why this one is worth learning. The short version is that it consolidates a lot of separate capabilities into one consistent interface, and it runs from Linux.

Impacket covers much of the same ground, but it's a collection of individual scripts: one to edit a DACL, another to change an owner, another for RBCD, another to add a computer. Each has its own syntax. bloodyAD puts most of those operations behind one tool with one grammar.

PowerView, Rubeus, and Whisker are Windows-native tools and are commonly used from a Windows foothold. bloodyAD lets you perform many of the same directory-side operations directly from your Linux attack box.

Certipy is the better tool for AD CS exploitation, and bloodyAD doesn't try to replace it. bloodyAD can enumerate certificate templates, but for actually exploiting an ESC path you'll still want Certipy. The two are complementary.

So the case for bloodyAD isn't that it beats everything else. It's that when you have a credential, a Linux box, and a permission to abuse, it's the most direct way to act on that permission without touching a Windows host. It also works transparently through a SOCKS proxy, so the same commands run unchanged when you're pivoting through a compromised host, which is where the Ligolo-ng chapter connects.

### What it can actually do

bloodyAD is often introduced as a password-reset tool, which undersells it. The CLI is organized under a few verbs:

*   **get** reads from the directory: object attributes, domain trusts, and `get writable`, which lists what your current account can modify.
    
*   **add** creates or grants: a group member, a computer, a GenericAll ACE, DCSync rights, shadow credentials, RBCD, or a UAC flag.
    
*   **set** changes an existing value: an object's owner, a password, a service principal name for a targeted Kerberoast, or a restore on a deleted object.
    
*   **remove** reverses any of the above, which matters because cleaning up after yourself is part of the work.
    

Together, that covers a large part of the Active Directory attack surface in a single binary.

### How it works alongside BloodHound

This is worth spelling out, because it's the reason bloodyAD earns a place next to BloodHound rather than competing with it.

The two tools handle different halves of the same job. BloodHound is analysis; bloodyAD is execution.

BloodHound collects the directory, builds the graph, and shows you the edges: ForceChangePassword, GenericWrite, GenericAll, WriteOwner, AddMember, AddKeyCredentialLink, AllowedToAct. It tells you what you can do and where each option leads, and it does all of this read-only. It never modifies the domain.

bloodyAD is what turns one of those edges into an actual change. Most of the mapping is straightforward once you've seen it. Just to name a few:

*   ForceChangePassword corresponds to `set password`.
    
*   GenericAll over a user lets you run `set password` or `add shadowCredentials` directly.
    
*   WriteOwner is a two-step: `set owner` to take the object, then `add genericAll` to grant yourself control, and then you abuse it.
    
*   GenericWrite lets you write an SPN and Kerberoast, or add shadow credentials.
    
*   AllowedToAct corresponds to `add rbcd`.
    
*   GenericAll over a group corresponds to `add groupMember`.
    

Most edges BloodHound draws have a corresponding bloodyAD command.

The relationship runs in both directions, too. BloodHound's graph is a snapshot taken at collection time, while bloodyAD's `get writable` shows you what your identity can modify right now. That's how you catch access that changed since your last collection, or that your collector missed.

In practice the workflow is a loop: collect with your BloodHound collector, analyze the graph, execute the useful edge with bloodyAD, then re-collect to see what your new access opened up, and repeat as needed.

It's also worth knowing that bloodyAD's author ships two companion scripts, pathgen and autobloody, which read BloodHound's Neo4j database directly, work out a privilege-escalation path, and execute it through bloodyAD automatically. This post covers the primitives one at a time so you understand each one, rather than handing the whole path to automation, but the fact that the integration exists at all says something about how closely the two tools are meant to work together.

## Installing bloodyAD on Kali

There are two reasonable ways to install bloodyAD on Kali, and it's worth understanding the trade-off rather than copying one command blindly.

Kali packages bloodyAD, so the simplest option is:

```plaintext
sudo apt install bloodyad
```

This pulls the dependencies for you and updates alongside the rest of your system, and it's the approach Kali recommends whenever a tool is already packaged.

The reason I usually install it with pipx instead is that bloodyAD is developed actively, and new features and fixes reach the upstream project before they make it into the Kali package. If you want the current version, pipx gives you the upstream build in an isolated environment without modifying Kali's system Python.

It's also worth being clear on why the answer isn't plain pip. Since Kali 2024.4, system-wide pip installs are blocked, and running `sudo pip install` returns an `externally-managed-environment` error. pipx is the supported way to install standalone Python tools around that restriction.

### Installing with pipx

First, check whether pipx is already present:

```plaintext
pipx --version
```

If it isn't, install it from APT:

```plaintext
sudo apt update && sudo apt install pipx -y
```

Then make sure pipx's binary directory is on your PATH:

```plaintext
pipx ensurepath
```

If that command makes a change, open a new terminal before continuing so the shell picks it up.

Now install bloodyAD:

```plaintext
pipx install bloodyAD
```

Confirm it installed and check where it lives:

```plaintext
bloodyAD --help
which bloodyAD
```

On a standard Kali setup, the executable will be in your user's local bin directory:

```plaintext
/home/kali/.local/bin/bloodyAD
```

### Keeping it updated

If you install with pipx, one thing to remember is that pipx-managed tools sit outside APT. Running `sudo apt update && sudo apt upgrade` will not update bloodyAD. You update it separately:

```plaintext
pipx upgrade bloodyAD
```

Or update everything pipx manages at once:

```plaintext
pipx upgrade-all
```

And to see what pipx is currently managing:

```plaintext
pipx list
```

### The shape of a bloodyAD command

That's the full installation. There's no need to clone the repository or build a virtual environment by hand. From here, nearly every bloodyAD command follows the same structure:

```plaintext
bloodyAD --host <DC_IP> -d <DOMAIN> -u <USER> -p '<PASSWORD>' <ACTION>
```

The host, the domain, the account you're authenticating as, and the action to run against the directory. You'll also see `-H` used as the short form of `--host`. The rest of this post focuses on that last part: the actions worth knowing, what each one abuses, the rights that enable it, and how to undo it. (Note a lot of the following commands come from the hackthebox machine TombWatcher this is a good box to follow along with and get really familiar with bloodhound bloodyAD and nxc.)

## Change an SPN (targeted Kerberoasting)

If you can write to a target account's `servicePrincipalName` attribute, you can make that account Kerberoastable on demand. You assign it an SPN, request a service ticket for that SPN, crack the ticket offline to recover the account's password, and then remove the SPN you added. The account doesn't need to be running any service; it only needs the SPN long enough for you to request and capture a ticket. This is targeted Kerberoasting.

![](https://cdn.hashnode.com/uploads/covers/68eab3ed7661b396360de06f/7cd05cfe-e5e2-46a4-8ade-a54168fc41d4.png align="center")

**Rights that enable it.** You need write access to the target's `servicePrincipalName`. In BloodHound terms, that's any of:

*   WriteSPN, or WriteProperty over servicePrincipalName
    
*   GenericWrite
    
*   GenericAll
    

If you only hold WriteDACL, WriteOwner, or ownership of the object, you can grant yourself one of the rights above first and then write the SPN. That chain is covered in the WriteDACL section.

**Writing the SPN.** The `set object` command writes an attribute. You give it the target, the attribute name, and the value with `-v`:

```plaintext
bloodyAD --host 10.129.46.58 -u henry -p 'H3nry_987TGV!' -d tombwatcher.htb set object alfred servicePrincipalName -v 'HTTP/alfred.tombwatcher.htb'
```

bloodyAD confirms the change with `[+] alfred's servicePrincipalName has been updated`.

With the SPN in place, you can request the ticket. NetExec's LDAP module handles this with `--kerberoasting`, which requests a ticket for every account that has an SPN. In this case that's the one account you just modified:

![](https://cdn.hashnode.com/uploads/covers/68eab3ed7661b396360de06f/76fce06c-8758-426c-a00a-1e7b947501a7.png align="center")

```plaintext
nxc ldap 10.129.46.58 -u henry -p 'H3nry_987TGV!' --kerberoasting kroast.txt
```

The output reports `Total of records returned 1` and writes the account's `$krb5tgs$23$` hash to the output file. From there it's a standard offline crack:

```plaintext
john kroast.txt --wordlist=/usr/share/wordlists/rockyou.txt
```

![](https://cdn.hashnode.com/uploads/covers/68eab3ed7661b396360de06f/b39ce4a6-95e3-405b-91d5-69e4abd3cb19.png align="center")

The hash cracks to the password `basketball`. Writing a single attribute turned an account you couldn't authenticate as into one you can.

**A couple of things to know.**

The service named in the SPN doesn't have to exist, and nothing needs to be listening for it. The SPN only has to be unique within the domain. `HTTP/` is a common choice by convention, but the attack doesn't depend on the service class you use.

Because you're modifying a live account, you should restore the original state when you're done. bloodyAD will give you the restore command if you add `--bak`:

```plaintext
bloodyAD --host 10.129.46.58 -u henry -p 'H3nry_987TGV!' -d tombwatcher.htb set object alfred servicePrincipalName -v 'HTTP/alfred.tombwatcher.htb' --bak
```

It performs the write and then prints the command that reverses it:

```plaintext
[+] Restore command:
    set object 'alfred' servicePrincipalName
```

Use `--bak` on your first change to the attribute, not a later one. It records whatever value the attribute holds at the moment you run it, so if you set the SPN first and add `--bak` on a second run, you'll back up the value you already planted rather than the original. The correct sequence is to back up first, perform the roast, and then run the restore command bloodyAD printed, prefixed with your usual `--host`, `-u`, and `-p`.

## Add a group member

Adding a principal you control to a group lets you inherit whatever rights, access, or privileges that group has been granted. If a group holds useful permissions somewhere in the domain, membership in it becomes a path to those permissions.

![](https://cdn.hashnode.com/uploads/covers/68eab3ed7661b396360de06f/c7c911f9-4fa9-4099-a9cc-68ff1a5a76a7.png align="center")

**Rights that enable it.** Any of:

*   AddMember, or WriteProperty over the group's member attribute
    
*   AddSelf (allows the principal holding the right to add itself to the group)
    
*   GenericWrite
    
*   GenericAll
    

As with the other operations, WriteDACL, WriteOwner, or ownership of the group can be used to grant yourself one of these first.

**The command.** `add groupMember` takes the group and the member to add:

```plaintext
bloodyAD --host 10.129.46.58 -u alfred -p 'basketball' -d tombwatcher.htb add groupMember Infrastructure alfred
```

A couple of notes. Group membership can unlock rights elsewhere in the domain through ACLs assigned to that group, which is the reason it's worth doing. And because your current token or Kerberos ticket was issued before you joined the group, you may need to re-authenticate or request a fresh ticket for the new membership to take effect.

**Cleanup.** Remove yourself when you're done:

```plaintext
bloodyAD --host 10.129.46.58 -u alfred -p 'basketball' -d tombwatcher.htb remove groupMember Infrastructure alfred
```

## Read a gMSA password

A group managed service account (gMSA) is a domain account intended for services and scheduled tasks, where Active Directory manages and rotates the password automatically. If you have the right to read that managed password, bloodyAD will retrieve it and parse the blob into an NT hash you can use for pass-the-hash authentication as the account.

![](https://cdn.hashnode.com/uploads/covers/68eab3ed7661b396360de06f/2256d78e-27a7-49fc-b897-482d42c060d3.png align="center")

**Rights that enable it.** Any of:

*   ReadGMSAPassword
    
*   Being listed in the gMSA's PrincipalsAllowedToRetrieveManagedPassword / msDS-GroupMSAMembership
    
*   GenericAll over the gMSA
    

WriteDACL, WriteOwner, or ownership can be used to grant yourself read access first.

**The command.** Read the managed password attribute directly:

```plaintext
bloodyAD --host 10.129.46.58 -u alfred -p 'basketball' -d tombwatcher.htb get object 'ansible_dev$' --attr msDS-ManagedPassword
```

In the output, look for `msDS-ManagedPassword.NT`. That's the NT hash you want. A few things worth knowing: gMSA account names end in `$`, but so do computer accounts, so don't identify a gMSA by the trailing `$` alone. NetExec's normal `--users` enumeration can skip gMSA accounts, so if you know the name, querying it directly like this is the reliable way to get the hash.

![](https://cdn.hashnode.com/uploads/covers/68eab3ed7661b396360de06f/82235823-1dee-4bcd-b689-f9488b7dd17d.png align="center")

That NT hash is what you carry into the next step. bloodyAD authenticates with a hash using `-p ':<NT_HASH>'`, with the leading colon, which is different from NetExec's `-H`.

## Reset a password

Setting a new password on a target account is the most direct takeover when you have the right to do it. This is a reset, not a change: you don't need to know the account's current password.

![](https://cdn.hashnode.com/uploads/covers/68eab3ed7661b396360de06f/ebdffb82-76e9-4da9-8dc6-4db5e55a7a0d.png align="center")

**Rights that enable it.** Any of:

*   ForceChangePassword (User-Force-Change-Password)
    
*   AllExtendedRights
    
*   GenericAll
    

WriteDACL, WriteOwner, or ownership can be used to grant yourself the reset right first.

**The command.** Here the authenticating account is the gMSA from the previous step, using its NT hash, resetting a different user's password:

```plaintext
bloodyAD --host 10.129.46.58 -u 'ansible_dev$' -p ':cb3161cb2c9d84b58ba3014f55040d75' -d tombwatcher.htb set password sam 'Password1$'
```

bloodyAD confirms with `[+] Password changed successfully!`.

One caution: resetting a password is disruptive. It locks the legitimate user out of the account and can break any service authenticating as it, so it's the noisiest of the takeover options. When the account is in active use and you have a less disruptive alternative such as shadow credentials, prefer that.

## Change an object's owner

Changing an object's owner to a principal you control is usually a stepping stone rather than the final move. An object's owner can rewrite that object's DACL, which lets you grant yourself whatever right you actually need: GenericAll, GenericWrite, ForceChangePassword, or control over group membership.

**Rights that enable it.** Either of:

*   WriteOwner
    
*   GenericAll
    

**The command.** The syntax is `set owner <TARGET> <NEW_OWNER>`:

```plaintext
bloodyAD --host 10.129.46.58 -u sam -p 'Password1$' -d tombwatcher.htb set owner john sam
```

bloodyAD reports the change, naming the previous owner's SID and the new one: `[+] Old owner S-1-5-21-...-512 is now replaced by sam on john`.

![](https://cdn.hashnode.com/uploads/covers/68eab3ed7661b396360de06f/6968d271-17d8-4be2-9735-1e0daa916fab.png align="center")

Two things to keep in mind. Becoming the owner does not automatically grant you GenericAll; what it gives you is the ability to rewrite the DACL and add the right you want. And if you care about cleanup, record the original owner (the SID bloodyAD prints) before you change it, so you can put it back.

## Add shadow credentials

Shadow credentials add a KeyCredential to the target's `msDS-KeyCredentialLink` attribute, which gives you certificate-based Kerberos authentication material for that account without changing its password. Because the account's password is untouched, shadow credentials are less disruptive than a password reset and won't lock the legitimate user out.

**Rights that enable it.** Any of:

*   AddKeyCredentialLink, or write access to msDS-KeyCredentialLink
    
*   GenericWrite
    
*   GenericAll
    

WriteDACL, WriteOwner, or ownership can be used to grant yourself the write first.

**The command.**

```plaintext
bloodyAD --host 10.129.46.58 -u sam -p 'Password1$' -d tombwatcher.htb add shadowCredentials john
```

![](https://cdn.hashnode.com/uploads/covers/68eab3ed7661b396360de06f/aa541b43-a38d-4b71-bb6a-4d945fabaa4c.png align="center")

This attack depends on the Key Trust PKINIT path, and there are a few things worth understanding before you rely on it. If the PKINIT authentication step fails, that does not necessarily mean the KeyCredential write failed. bloodyAD may have successfully written the credential and saved a PFX before the later authentication attempt errored, and a PKINIT failure here doesn't prove that all certificate-based authentication is broken in the domain, since another certificate-trust path may still work. If the credential can't authenticate and you also hold a simpler takeover right such as ForceChangePassword, it's reasonable to just reset the password instead.

One practical warning: don't blindly rerun the command after a failure, because each run can append another KeyCredential. Save the SHA256 key ID that bloodyAD prints so you can remove only the credential you added.

**Cleanup.** Remove the specific credential by its key ID:

```plaintext
bloodyAD --host 10.129.46.58 -u sam -p 'Password1$' -d tombwatcher.htb remove shadowCredentials john --key 4bbd1b02f3ea504eef3df222989bb46ef141f1c018b67932e7df659373f23a60
```

## Add a GenericAll ACE

Adding a GenericAll ACE for a principal you control gives that principal full control over the target object. From there you use whatever takeover fits the object type: reset a user's password, add shadow credentials, change an SPN, or modify group membership.

**Rights that enable it.** Any of:

*   WriteDACL
    
*   GenericAll
    
*   Ownership of the target
    

If you only have WriteOwner, take ownership first and then add GenericAll.

**The command.** The syntax is `add genericAll <TARGET> <TRUSTEE>`, where the trustee is the principal receiving control over the target:

```plaintext
bloodyAD --host 10.129.46.58 -u sam -p 'Password1$' -d tombwatcher.htb add genericAll john sam
```

Full control over the object is the result, but the useful follow-up depends on what kind of object the target is. If you already hold WriteDACL, this is often the simplest way to convert that into an immediately useful position.

**Cleanup.**

```plaintext
bloodyAD --host 10.129.46.58 -u sam -p 'Password1$' -d tombwatcher.htb remove genericAll john sam
```

## WriteDACL and GenericAll: the quick rule

Two rights come up constantly, and it helps to have a default response to each.

If you have **WriteDACL** over an object, the standard move is to grant a principal you control GenericAll over it:

```plaintext
bloodyAD --host <DC_IP> -u <USER> -p '<PASSWORD>' -d <DOMAIN> add genericAll <TARGET> <TRUSTEE>
```

If you only have WriteOwner, take ownership first, then grant GenericAll.

Once you have **GenericAll**, pick the simplest takeover that fits the target:

*   User: reset the password, or add shadow credentials.
    
*   Group: add a principal you control to the group.
    
*   Computer: shadow credentials are usually preferable to changing the machine password.
    
*   If a specific abuse such as targeted Kerberoasting fits the situation better, use that instead.
    

The general principle is not to overcomplicate an ACL path. Convert the control you have into the simplest usable primitive and move on.

## DCSync

DCSync abuses replication rights to pull secrets, including password hashes, directly from the domain. The point that trips people up: these rights apply to the **domain object** (the domain root), not to the domain controller's computer object.

**Rights that let you do it directly.** Any of:

*   The DCSync combination: both DS-Replication-Get-Changes and DS-Replication-Get-Changes-All
    
*   AllExtendedRights over the domain object
    
*   GenericAll over the domain object
    

If you already hold these, there's no reason to keep pivoting through user accounts. Dump the domain secrets directly.

**Granting yourself DCSync.** If you have WriteDACL over the domain object, grant a principal you control the replication rights:

```plaintext
bloodyAD --host <DC_IP> -u <USER> -p '<PASSWORD>' -d <DOMAIN> add dcsync <TRUSTEE>
```

WriteOwner can be chained the same way as elsewhere: take ownership, modify the DACL, then grant DCSync.

**Cleanup.**

```plaintext
bloodyAD --host <DC_IP> -u <USER> -p '<PASSWORD>' -d <DOMAIN> remove dcsync <TRUSTEE>
```

## Wrapping up

Every operation in this post follows the same shape: identify the right you hold, choose the write it enables, run it, and undo it afterward. That's the pattern behind targeted Kerberoasting, group membership changes, gMSA password reads, resets, ownership changes, shadow credentials, GenericAll, and DCSync alike. Once the pattern is familiar, bloodyAD stops being a list of commands to memorize and becomes a single tool you point at whatever right BloodHound put in front of you.

If you're following the series, the earlier chapters lead into this one:

*   **NetExec** covers gaining the initial credential, including the `--kerberoasting` module used here. [**https://www.hack2harden.com/the-oscp-toolkit-netexec**](https://www.hack2harden.com/the-oscp-toolkit-netexec)
    
*   **BloodHound + RustHound-CE** covers finding the edges that bloodyAD executes. [**https://www.hack2harden.com/the-oscp-toolkit-bloodhound-rusthound-ce**](https://www.hack2harden.com/the-oscp-toolkit-bloodhound-rusthound-ce)
    

**Find. Fix. Fortify.**
