Skip to main content
              _____________________________ __    __    ______    __    __   __   __   _______
             |   _   /  /  __   / |   ____/|  |  |  |  /  __  \  |  |  |  | |  \ |  | |       \
             |  |_)  | |  |  |  | |  |__   |  |__|  | |  |  |  | |  |  |  | |   \|  | |  .--.  |
             |   _  <  |  |  |  | |   __|  |   __   | |  |  |  | |  |  |  | |  . `  | |  |  |  |
             |  |_)  | |  `--'  | |  |     |  |  |  | |  `--'  | |  `--'  | |  |\   | |  '--'  |
             |______/   \______/  |__|     |__|  |___\_\________\_\________\|__| \___\|_________\

                                         << @coffeegist | @Tw1sm >>

Python PyPi

BOFHound is an offline BloodHound ingestor and LDAP result parser compatible with TrustedSec's ldapsearch BOF, the Python adaptation, pyldapsearch and Brute Ratel's LDAP Sentinel. ldapsearch BOF output can also be parsed from Havoc logs, OutflankC2 logs, and Mythic callbacks.

By parsing log files generated by the aforementioned tools, BOFHound allows operators to utilize BloodHound's beloved interface while maintaining full control over the LDAP queries being run and the spped at which they are executed. This leaves room for operator discretion to account for potential honeypot accounts, expensive LDAP query thresholds and other detection mechanisms designed with the traditional, automated BloodHound collectors in mind.

Check this PR to the SA BOF repo for BOFs that collect session and local group membership data and can be parsed by BOFHound.

References

Blog Posts:

Title Date
BOFHound: AD CS Integration Oct 30, 2024
BOFHound: Session Integration Jan 30, 2024
Granularize Your AD Recon Game Part 2 Jun 15, 2022
Granularize Your AD Recon Game May 10, 2022

Presentations:

Conference Materials Date
SO-CON 2024 Slides & Recording Mar 11, 2024

Installation

BOFHound can be installed with pip3 install bofhound or by cloning this repository and running pip3 install .

Usage

 Usage: bofhound [OPTIONS]

 Generate BloodHound compatible JSON from logs written by the ldapsearch BOF, pyldapsearch and
 specific C2 frameworks

╭─ Options ─────────────────────────────────────────────────────────────────────────────────────╮
│ --input             -i      TEXT                             Directory or file containing     │
│                                                              logs of ldapsearch results       │
│                                                              [default:                        │
│                                                              /opt/cobaltstrike/logs]          │
│ --output            -o      TEXT                             Location to export bloodhound    │
│                                                              files                            │
│                                                              [default: .]                     │
│ --properties-level  -p      [Standard|Member|All]            Change the verbosity of          │
│                                                              properties exported to JSON:     │
│                                                              Standard - Common BH properties  │
│                                                              | Member - Includes MemberOf and │
│                                                              Member | All - Includes all      │
│                                                              properties                       │
│                                                              [default: Member]                │
│ --parser                    [ldapsearch|BRC4|Havoc|Outflank  Parser to use for log files.     │
│                             C2|Mythic]                       ldapsearch parser (default)      │
│                                                              supports ldapsearch BOF logs     │
│                                                              from Cobalt Strike and           │
│                                                              pyldapsearch logs                │
│                                                              [default: ldapsearch]            │
│ --debug                                                      Enable debug output              │
│ --zip               -z                                       Compress the JSON output files   │
│                                                              into a zip archive               │
│ --quiet             -q                                       Suppress banner                  │
│ --help              -h                                       Show this message and exit.      │
╰───────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Mythic Options ──────────────────────────────────────────────────────────────────────────────╮
│ --mythic-server        TEXT  IP or hostname of Mythic server to connect to                    │
│                              [default: 127.0.0.1]                                             │
│  --mythic-token         TEXT  Mythic API token [default: None]                                │
╰───────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ BloodHound CE Options ───────────────────────────────────────────────────────────────────────╮
│ --bh-token-id         TEXT  BloodHound API token ID [default: None]                           │
│ --bh-token-key        TEXT  BloodHound API token key [default: None]                          │
│ --bh-server           TEXT  BloodHound CE URL [default: http://127.0.0.1:8080]                │
╰───────────────────────────────────────────────────────────────────────────────────────────────╯

Example Usage

Parse ldapseach BOF results from Cobalt Strike logs (/opt/cobaltstrike/logs by default) to /data/

bofhound -o /data/

Parse pyldapsearch logs and only include all properties (vs other property levels)

bofhound -i ~/.pyldapsearch/logs/ --properties-level all

Parse LDAP Sentinel data from BRc4 logs (will change default input path to /opt/bruteratel/logs)

bofhound --parser brc4

Parse Havoc loot logs (will change default input path to /opt/havoc/data/loot) and zip the resulting JSON files

bofhound --parser havoc --zip

Uploading to BloodHound CE

Supply --bh-server, --bh-token-id, and --bh-token-key to upload after parsing. BOFHound checks connectivity and credentials before parsing, then creates an upload job after generating the files. Upload permissions are checked when that job starts. Requests use a 10-second connect timeout and a 300-second read timeout.

Retry an upload without parsing the logs again:

bofhound --upload-only --output ./output --bh-server http://127.0.0.1:8080 --bh-token-id TOKEN_ID --bh-token-key TOKEN_KEY

This uploads all regular *.json files directly inside --output (default: current directory). To upload existing *.zip archives instead, add --zip. In upload-only mode, --zip selects archives; it does not create them. Use a directory containing only the exports you intend to upload. Both token options are required; the server defaults to http://127.0.0.1:8080.

An unsuccessful upload or job completion returns a nonzero exit status. Generated files remain available for retrying; retrying a folder resends all selected files, including any that were accepted in an earlier attempt. A successful exit confirms upload acceptance and job completion, not the outcome of server-side ingestion.

Performance & Cache

--workers N controls parallel file and ACL processing. Use --workers 1 for sequential processing. The default uses about 90% of available CPU cores. Actual pools are limited to the number of tasks and the platform limit (61 processes on Windows). Files with incomplete records and custom stateful parsers use sequential parsing to preserve records spanning files. Pending tasks are bounded; input or ACL worker failures abort before output/cache updates.

Caching is enabled by default at --output/bofhound_cache.db; override it with --cache-file PATH, disable it with --no-cache, or inspect it with --cache-stats. The cache stores data-only raw LDAP snapshots. Each run combines cached and current LDAP data and rebuilds relationships against that complete snapshot. It does not skip ACL processing or reuse computed relationships, because newly collected objects can change existing relationships.

Present attributes replace earlier values, including empty strings. Cached group membership links follow known identities through renames and are removed when the known target is displaced; explicitly supplied membership attributes use current DNs. Objects and attributes absent from a partial collection remain cached; absence does not prove deletion. Use a new cache file to start a fresh snapshot. Local sessions and local group records are processed from the current logs and are not retained in the cache. --context-from PATH loads SID/domain/schema mappings and CA-host identities from a prior cache (or a directory containing bofhound_cache.db) without exporting its objects. Current collected computers take precedence over cached host identities. Legacy or incompatible cache versions are rejected without being loaded or changed; choose a new cache path or remove the old database and rebuild from the original logs.

bofhound -i ./logs --output ./out --workers 4
bofhound -i ./logs --output ./out --no-cache
bofhound --output ./out --cache-stats

ldapsearch

Specify *,ntsecuritydescriptor as the attributes to return to be able to parse ACL edges. You are missing a ton of data if you don't include this in your ldapsearch queries!

Required Data

The following attributes are required for proper functionality:

samaccounttype
distinguishedname
objectsid

Some object classes rely on domain objects being populated within BOFHound. Domains can be queried with either of the following commands

ldapsearch (objectclass=domain) --attributes *,ntsecuritydescriptor
ldapsearch (distinguishedname=DC=windomain,DC=local) --attributes *,ntsecuritydescriptor

Example ldapsearch Queries

# Get All the Data (Maybe Run BloodHound Instead?)
ldapsearch (objectclass=*) --attributes *,ntsecuritydescriptor

# Retrieve All Schema Info
ldapsearch (schemaIDGUID=*) --attributes name,schemaidguid --dn CN=Schema,CN=Configuration,DC=windomain,DC=local

# Retrieve Only the ms-Mcs-AdmPwd schemaIDGUID
ldapsearch (name=ms-mcs-admpwd) --attributes name,schemaidguid --count 1 --dn CN=Schema,CN=Configuration,DC=windomain,DC=local

# Retrieve Domain NetBIOS Names (useful if collecting data via `netsession2/netloggedon2` BOFs)
ldapsearch (netbiosname=*) --dn "CN=Partitions,CN=Configuration,DC=windomain,DC=local"

# Unroll a group's nested members
ldapsearch (memberOf:1.2.840.113556.1.4.1941:=CN=TargetGroup,CN=Users,DC=windomain,DC=local) --attributes *,ntsecuritydescriptor

# Query domain trusts
ldapsearch (objectclass=trusteddomain) --attributes *,ntsecuritydescriptor

# Query across a trust
ldapsearch (objectclass=domain) --attributes *,ntsecuritydescriptor --hostname dc1.trusted.windomain.local --dn "DC=TRUSTED,DC=WINDOMAIN,DC=LOCAL"

#####
# Queries below populate objects for AD CS parsing

# Query the domain object
ldapsearch (objectclass=domain) --attributes *,ntsecuritydescriptor

# Query Enterprise CAs
ldapsearch (objectclass=pKIEnrollmentService) --attributes *,ntsecuritydescriptor --dn "CN=Configuration,DC=domain,DC=local"

# Query AIACAs, Root CAs and NTAuth Stores
ldapsearch (objectclass=certificationAuthority) --attributes *,ntsecuritydescriptor --dn "CN=Configuration,DC=domain,DC=local"

# Query Certificate Templates
ldapsearch (objectclass=pKICertificateTemplate) --attributes *,ntsecuritydescriptor --dn "CN=Configuration,DC=domain,DC=local"

# Query Issuance Policies
ldapsearch (objectclass=msPKI-Enterprise-Oid) --attributes *,ntsecuritydescriptor --dn "CN=Configuration,DC=domain,DC=local"

Versions

Check the tagged releases to download a specific version

  • v0.4.0 and onward support parsing AD CS objects and edges
  • v0.3.0 and onward support session/local group data
  • v0.2.1 and onward are compatible with BloodHound CE
  • v0.2.0 is the last release supporting BloodHound Legacy

Development

bofhound uses Poetry to manage dependencies. Install from source and setup for development with:

git clone https://github.com/fortalice/bofhound
cd bofhound
poetry install
poetry run bofhound --help

Release maintainers: see the release guide for the manual GitHub Actions publishing process.

References and Credits

Metadata

Release files for bofhound 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bofhound 0.5.0
File Size Uploaded Provenance
bofhound-0.5.0.tar.gz 72.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for bofhound 0.5.0
File Interpreter ABI Platform
bofhound-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 161.2 kB

Release files / bofhound-0.5.0.tar.gz

Download URL bofhound-0.5.0.tar.gz
Size 72.5 kB
Tags Source
SHA-256 checksum
How to use checksums
b71744000d07b1dd8c879a0d3632fa69b3532b0c2c8ece3faeecfc1ccb9117d9
BLAKE2b-256 checksum
How to use checksums
2f1ae69535af675309aca04f7c04c499ab0a2e903dfc4602d28317a5f497d29a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / bofhound-0.5.0-py3-none-any.whl

Download URL bofhound-0.5.0-py3-none-any.whl
Size 88.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c145574164ea4669cd80b19dae68589d71655b91e808d6af591aeb1f21238f3e
BLAKE2b-256 checksum
How to use checksums
a92f57b256e1d7256d3292e8643cf29fbba6de2951430e0060d4efc553dc5bba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Security for bofhound 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for bofhound-0.5.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page