🦀 RustHound-CE Cheatsheet
Complete guide to using RustHound-CE for BloodHound Community Edition data collection.
RustHound-CE is a fast, cross-platform BloodHound Community Edition collector written in Rust, maintained by g0h4n. It talks to a Domain Controller over LDAP/LDAPS from Linux, macOS or Windows and emits CE-schema JSON (optionally zipped) for ingestion into the BloodHound CE web UI.
[!warning] CE-only output schema RustHound-CE produces BloodHound CE JSON, which is not interchangeable with legacy BloodHound. For legacy BloodHound, use the separate NeverHack-maintained RustHound (original),
bloodhound-python, or SharpHound v1. Uploading the wrong schema silently fails or mis-parses.
Related notes: SharpHound, bloodhound-ce-python, BloodHound, NetExec, Impacket, faketime, Kerberos Tickets.
📋 Table of Contents
- Overview
- Installation
- CLI Options Reference
- Basic Usage
- Authentication Methods
- Collection Methods
- Performance & Reliability
- DNS & Host Resolution
- Time Skew & Kerberos Gotchas
- Ingesting into BloodHound CE
- SharpHound / bloodhound-python Equivalents
- Troubleshooting
- OPSEC Notes
🎯 Overview
RustHound-CE enumerates Active Directory over LDAP to map attack paths, ACLs, sessions, trusts and (optionally) ADCS relationships for BloodHound CE.
Key features
- ✅ Single static binary, no .NET / Python runtime needed
- ✅ Cross-platform: Linux, macOS, Windows, ARM
- ✅ Runs remotely from a non-domain-joined attacker host
- ✅ Password, NTLM hash (pass-the-hash), Kerberos ccache, and client-certificate auth
- ✅ LDAPS support and custom LDAP filters
- ✅ On-disk caching and
--resumefor large / flaky environments - ✅ Outputs CE-compatible JSON, optionally zipped for direct upload
Compatibility
- Targets: BloodHound Community Edition only.
- Legacy BloodHound: use the original RustHound (NeverHack) or
bloodhound-python. - Always match collector output to your BloodHound server version; check the CE web UI under Settings → Download Collectors for the expected format.
📦 Installation
Via Cargo (recommended)
cargo install rusthound-ce
# binary lands in ~/.cargo/bin/rusthound-ce
Build dependencies (Debian/Ubuntu/Kali)
sudo apt install -y gcc clang libclang-dev \
libgssapi-krb5-2 libkrb5-dev libsasl2-modules-gssapi-mit \
musl-tools gcc-mingw-w64-x86-64
The Kerberos and SASL packages are needed for -k (GSSAPI) auth; the musl and mingw toolchains are only needed for static / cross-compiled builds.
From source
git clone https://github.com/g0h4n/RustHound-CE
cd RustHound-CE
make release # optimized build for the current system
# or
cargo build --release
Makefile cross-compile targets
make windows # cross-compile a Windows .exe
make linux_musl # static Linux binary (portable, no libc deps)
make linux_aarch64 # ARM64 Linux
make armv7 # ARMv7
make macos # macOS
make install # install locally / make uninstall to remove
Docker
# build the image, then run against the current dir as output volume
docker run --rm -v "$PWD":/usr/src/rusthound-ce rusthound-ce \
-d domain.local -u user@domain.local -p 'Password123' -o /usr/src/rusthound-ce -z
🧰 CLI Options Reference
rusthound-ce [OPTIONS] --domain <domain>
| Flag | Long form | Description |
|---|---|---|
-d | --domain <domain> | Required. Domain FQDN, e.g. DOMAIN.LOCAL |
-u | --ldapusername <user> | LDAP username, e.g. user@domain.local |
-p | --ldappassword <pass> | LDAP password |
-H | --hashes <hashes> | NT hash for pass-the-hash (NTLM) |
-k | --kerberos | Kerberos auth via ccache (KRB5CCNAME) |
-f | --ldapfqdn <fqdn> | DC FQDN, e.g. DC01.DOMAIN.LOCAL or just DC01 |
-i | --ldapip <ip> | Domain Controller IP address |
-P | --ldapport <port> | LDAP port (default 389, or 636 with --ldaps) |
--ldaps | Force LDAPS (TLS) for requests | |
--dns-tcp | Use TCP instead of UDP for DNS queries | |
-n | --name-server <ip> | Alternative DNS server IP |
--pfx <pfx> | PFX / PKCS#12 client certificate | |
--pfx-pass <pass> | PFX file password (optional) | |
--crt <crt> | PEM client certificate | |
--key <key> | PEM private key | |
-o | --output <dir> | Output directory (default ./) |
-z | --zip | Compress JSON files into a single archive |
-c | --collectionmethod [<M>] | All (LDAP,SMB,HTTP) or DCOnly (default All) |
--cache | Cache LDAP results to disk | |
--cache-buffer <n> | Buffer size when caching (default 1000) | |
--resume | Resume from last saved state | |
--ldap-filter <filter> | Custom LDAP filter (default (objectClass=*)) | |
--fqdn-resolver | Module to resolve computer IP addresses | |
-v | -v... | Increase verbosity (-v, -vv, -vvv) |
-h | --help | Print help |
-V | --version | Print version |
🚀 Basic Usage
# Minimal: password auth, zip the output
rusthound-ce -d domain.local -u user@domain.local -p 'Password123' -o output/ -z
# Point at a specific DC by IP (skip DNS discovery)
rusthound-ce -d domain.local -u user@domain.local -p 'Password123' \
-i 10.10.10.10 -o output/ -z
# Verbose (trace what LDAP queries run and why something is missing)
rusthound-ce -d domain.local -u user@domain.local -p 'Password123' \
-o output/ -z -vv
Output is a set of *_users.json, *_computers.json, *_groups.json, *_gpos.json, *_ous.json, *_domains.json, *_containers.json files (plus ADCS/cert objects when reachable). With -z they are bundled into a timestamped zip ready for upload.
🔐 Authentication Methods
Username + password
rusthound-ce -d domain.local -u user@domain.local -p 'Password123' -o output/ -z
Pass-the-hash (NTLM)
# NT hash only; leave the LM half empty
rusthound-ce -d domain.local -u user -H :2b576acbe6bcfda7294d6bd18041b8fe -o output/ -z
Kerberos (ccache)
# 1) Obtain a TGT (impacket) and export the ccache
getTGT.py domain.local/user:'Password123' -dc-ip 10.10.10.10
export KRB5CCNAME=$PWD/user.ccache
# 2) Collect with -k; -f names the DC so the SPN resolves
rusthound-ce -d domain.local -k -f DC01.domain.local -o output/ -z
On a domain-joined Windows host you can just run rusthound-ce -d domain.local -k -f DC01 to reuse the current logon session.
Client certificate (PKINIT / LDAPS mTLS)
# PEM cert + key
rusthound-ce -d DOMAIN.LOCAL -f DC01.DOMAIN.LOCAL \
--crt user.crt --key user.key --ldaps -o output/ -z
# PFX bundle
rusthound-ce -d DOMAIN.LOCAL -f DC01.DOMAIN.LOCAL \
--pfx user.pfx --pfx-pass 'certpass' --ldaps -o output/ -z
Useful after an ADCS/Shadow-Credentials attack where you hold a certificate but no cleartext password.
🗂️ Collection Methods
# All (default): LDAP + SMB + HTTP — full session/local-group data
rusthound-ce -c All -d domain.local -u user@domain.local -p 'pass' -o output/ -z
# DCOnly: LDAP against the DC only — no SMB/HTTP touch to member hosts
rusthound-ce -c DCOnly -d domain.local -u user@domain.local -p 'pass' -o output/ -z
- All — LDAP objects plus SMB/HTTP calls to member computers for sessions and local-group membership. Louder, needs reachability to hosts, but yields richer attack paths.
- DCOnly — talks only to the DC over LDAP. Quietest option: no direct contact with workstations/servers, so no per-host sessions or local admin data, but full objects, ACLs, trusts and GPOs.
Custom LDAP filter
# Narrow the collection (e.g. one OU or object class)
rusthound-ce -d domain.local -u user@domain.local -p 'pass' \
--ldap-filter '(objectClass=user)' -o output/ -z
⚡ Performance & Reliability
# Cache LDAP results to disk with a large buffer (big domains)
rusthound-ce -d domain.local -u user@domain.local -p 'pass' \
-o output/ --cache --cache-buffer 10000 -z
# Resume a collection that was interrupted (flaky link / disconnect)
rusthound-ce -d domain.local -u user@domain.local -p 'pass' \
-o output/ --cache --resume -z
--cache writes intermediate results so --resume can pick up where a dropped run left off. Raise --cache-buffer for very large directories to reduce disk churn.
🌐 DNS & Host Resolution
# Resolve computer IPs during collection
rusthound-ce -d domain.local -u user@domain.local -p 'pass' \
--fqdn-resolver -o output/ -z
# Force TCP for DNS (UDP blocked/filtered) and use a specific resolver
rusthound-ce -d domain.local -u user@domain.local -p 'pass' \
--fqdn-resolver --dns-tcp -n 10.10.10.10 -o output/ -z
--fqdn-resolver maps computer objects to IP addresses; combine with --dns-tcp when UDP/53 is filtered, and -n to point at the internal DNS server (usually the DC).
⏰ Time Skew & Kerberos Gotchas
Kerberos rejects tickets when the clock differs from the DC by more than ~5 minutes (KRB_AP_ERR_SKEW). Sync or fake your clock:
# Read DC time, then wrap the collector with faketime
sudo ntpdate 10.10.10.10 # if allowed
# or
faketime "$(date -u -d "$(net time -S 10.10.10.10)" '+%Y-%m-%d %H:%M:%S')" \
rusthound-ce -d domain.local -k -f DC01.domain.local -o output/ -z
See the faketime note for the reliable one-liner pattern.
📥 Ingesting into BloodHound CE
# Output is already CE-schema JSON; -z gives one zip to upload
rusthound-ce -d domain.local -u user@domain.local -p 'pass' -o output/ -z
- Open the BloodHound CE web UI.
- Go to Administration / File Ingest (or drag-and-drop onto the graph).
- Upload the
.zip(or the individual.jsonfiles). - Wait for the ingest job to finish, then run built-in queries (Shortest Paths to Domain Admins, Kerberoastable users, etc.).
[!tip] Automated upload You can push the JSON straight into CE with the
bloodhound-cli/ API or tools likebhcli, avoiding the web upload for repeatable pipelines.
🔄 SharpHound / bloodhound-python Equivalents
| Goal | RustHound-CE | SharpHound (v2, CE) | bloodhound-ce-python |
|---|---|---|---|
| Full collection, zipped | -c All ... -z | -c All --zippassword ... | -c All --zip |
| DC-only / quiet | -c DCOnly | -c DCOnly | -c DCOnly |
| Pass-the-hash | -H :<nt> | (via runas/pth) | --hashes :<nt> |
| Kerberos ccache | -k -f DC01 | --kerberos | -k |
| Target DC | -i <ip> / -f <fqdn> | --domaincontroller | -dc <fqdn> -ns <ip> |
| LDAPS | --ldaps | --secureldap | --use-ldaps |
| Output dir | -o <dir> | --outputdirectory | -op / cwd |
RustHound-CE fills the same niche as bloodhound-ce-python (remote, Linux-friendly) but as a fast native binary rather than a Python tool. See both companion notes.
🛠️ Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
KRB_AP_ERR_SKEW | Clock drift vs DC | Sync time or wrap with faketime |
| Empty / tiny output | Wrong domain or no LDAP reach | Verify -d, add -i <DC IP>, check port 389/636 |
TLS / cert errors on --ldaps | Untrusted DC cert / wrong FQDN | Use correct -f FQDN; confirm CA trust |
| Missing sessions / local admins | Ran DCOnly, or hosts unreachable | Use -c All and ensure SMB/HTTP reachability |
| Hangs on large domains | No caching, big directory | Add --cache --cache-buffer 10000 |
| Computer IPs missing | Resolver disabled | Add --fqdn-resolver (+ --dns-tcp -n <DNS>) |
| Auth fails with hash | LM half included | Use -H :<nthash> (empty LM) |
| “wrong schema” on upload | Legacy BloodHound server | Use a CE server, or a legacy collector instead |
Add -v, -vv, or -vvv to see the exact LDAP queries and where collection stalls.
🕶️ OPSEC Notes
Context — Authorised engagements / lab use only.
- LDAP enumeration against a DC is high-signal to defenders (BloodHound-style query patterns are widely alerted on). Prefer
-c DCOnlywhen you only need objects/ACLs and want to avoid touching member hosts. -c Allreaches out to workstations/servers over SMB/HTTP for sessions and local groups — noisier and leaves host-side artifacts.- LDAPS (
--ldaps) encrypts the query traffic but does not hide the volume or pattern of queries. - Throttle / scope with
--ldap-filterand target a single DC (-i/-f) to keep the footprint small. - Certificate and Kerberos auth avoid sending a cleartext password over the wire; pair with proper ticket hygiene.