CertiHound
Linux-native AD CS collector for BloodHound CE
Features • Installation • Quick Start • Usage • BloodHound • API
CertiHound enumerates Active Directory Certificate Services (AD CS) via LDAP and exports BloodHound CE-compatible data for attack path visualization. Identify ESC1-ESC17 vulnerabilities and visualize certificate-based attack paths.
Screenshots
ESC1 - Enrollee Supplies Subject Detection
CertiHound detects ESC1 vulnerable templates and creates enrollment edges to Enterprise CAs, enabling attack path analysis in BloodHound CE.
ESC4 - Template ACL Abuse Detection
WriteDacl, WriteOwner, and other dangerous permissions on certificate templates are identified and mapped as attack edges.
Features
| Feature | Description |
|---|---|
| Linux-Native | No Windows dependencies - pure Python LDAP enumeration |
| BloodHound CE v6+ | Direct JSON/ZIP import with full node and edge support |
| Vulnerability Detection | ESC1-ESC11, ESC13-ESC17, GoldenCert |
| Multiple Backends | Works with ldap3, impacket, or any compatible LDAP adapter |
| NetExec Integration | Seamless integration with NetExec's --bloodhound option |
| Comprehensive Coverage | Certificate templates, Enterprise CAs, Root CAs, NTAuth, AIA CAs |
Supported Vulnerabilities
| Vulnerability | Description |
|---|---|
| ESC1 | Enrollee supplies subject with low-privilege enrollment |
| ESC2 | Any Purpose EKU or no EKU allows enrollment agent abuse |
| ESC3 | Enrollment agent templates + vulnerable targets |
| ESC4 | Dangerous ACL permissions on certificate templates |
| ESC5 | Vulnerable PKI object access control on AD containers |
| ESC6 | EDITF_ATTRIBUTESUBJECTALTNAME2 on Enterprise CA |
| ESC7 | Dangerous CA permissions (ManageCA / ManageCertificates) |
| ESC8 | NTLM relay to AD CS web enrollment (HTTP) endpoints |
| ESC9 | No security extension + weak certificate mapping |
| ESC10 | Weak certificate mapping without strong binding |
| ESC11 | NTLM relay to AD CS RPC endpoints (no encryption enforcement) |
| ESC13 | Issuance policy with OID group link abuse |
| ESC14 | Weak explicit certificate mappings (altSecurityIdentities) |
| ESC15 | EKUwu - Application policy abuse on Schema V1 templates |
| ESC16 | Security extension globally disabled on CA |
| ESC17 | Server Authentication EKU + enrollee supplies subject (TLS MITM) |
| GoldenCert | CA private key extraction from hosting computer |
Installation
From PyPI (Recommended)
pipx install certihound
# Or, if you need Kerberos (-k) auth:
pipx install 'certihound[kerberos]'
# On an existing install, inject gssapi:
pipx inject certihound gssapi
From Source
git clone https://github.com/0x0Trace/certihound.git
cd certihound
pip install -e . # core
pip install -e '.[kerberos]' # with Kerberos support
Verify Installation
certihound --version
certihound --help
Quick Start
Command Line
# Basic enumeration with password authentication
certihound -d corp.local -u 'user' -p 'password' --dc 10.10.10.10 -o output/
# Pass-the-Hash (NTHASH only, or LMHASH:NTHASH)
certihound -d corp.local -u 'user' -H :31d6cfe0d16ae931b73c59d7e0c089c0 --dc 10.10.10.10
# LDAPS (SSL/TLS) connection
certihound -d corp.local -u 'user' -p 'password' --dc 10.10.10.10 --ldaps -o output/
# Kerberos authentication (uses ccache; requires the 'kerberos' extra)
KRB5CCNAME=user.ccache certihound -d corp.local -k --dc dc01.corp.local -o output/
# Output as ZIP (default) or JSON
certihound -d corp.local -u 'user' -p 'password' --dc 10.10.10.10 --format zip
certihound -d corp.local -u 'user' -p 'password' --dc 10.10.10.10 --format json
Python Library
from certihound import ADCSCollector, BloodHoundCEExporter
from certihound.ldap.connection import LDAPConnection, LDAPConfig
# Configure connection
config = LDAPConfig(
domain="corp.local",
username="user",
password="password",
dc_ip="10.10.10.10",
use_ldaps=True,
)
# Collect and export
with LDAPConnection(config) as conn:
collector = ADCSCollector(conn)
data = collector.collect_all()
exporter = BloodHoundCEExporter(data.domain, data.domain_sid)
result = exporter.export(data)
result.write_zip("bloodhound_adcs.zip")
Usage
CLI Options
Usage: certihound [OPTIONS]
Options:
-d, --domain TEXT Target domain FQDN (e.g., corp.local) [required]
-u, --username TEXT Username for authentication
-p, --password TEXT Password for authentication
-H, --hashes TEXT NTLM hash for pass-the-hash (LMHASH:NTHASH or NTHASH)
--dc TEXT Domain Controller IP or hostname
-k, --kerberos Use Kerberos authentication (ccache)
--ldaps Use LDAPS (SSL/TLS)
--ca-cert PATH CA certificate file for LDAPS validation
--port INTEGER LDAP port (default: 389 or 636 for LDAPS)
-o, --output TEXT Output directory (default: ./output)
--format [json|zip|both] Output format (default: zip)
--enum-only Only enumerate, skip vulnerability detection
-v, --verbose Increase verbosity (-v, -vv)
--version Show the version and exit.
--help Show this message and exit.
Output Files
CertiHound generates BloodHound CE v6 compatible files:
| File | Description |
|---|---|
certtemplates.json |
Certificate template nodes with properties and edges |
enterprisecas.json |
Enterprise CA nodes with template publishing |
rootcas.json |
Root CA hierarchy nodes |
ntauthstores.json |
NTAuth store configuration |
aiacas.json |
AIA CA entries |
BloodHound Integration
Importing Data
- Run CertiHound to generate the ZIP file
- Open BloodHound CE
- Click Import → Select the generated ZIP file
- Use the built-in ADCS queries or create custom ones
Example Cypher Queries
Find ESC1 Vulnerable Templates:
MATCH p = (:Base)-[:Enroll|GenericAll|AllExtendedRights]->
(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)
WHERE ct.enrolleesuppliessubject = True
AND ct.authenticationenabled = True
RETURN p
Find ESC4 Template ACL Abuse:
MATCH p = (principal)-[:WriteDacl|WriteOwner|GenericWrite|GenericAll|WriteAllProperties]->
(ct:CertTemplate)-[:PublishedTo]->(ca:EnterpriseCA)
WHERE NOT principal.objectid ENDS WITH '-512'
AND NOT principal.objectid ENDS WITH '-519'
AND NOT principal.objectid ENDS WITH '-544'
RETURN p
NetExec Integration
CertiHound integrates with NetExec for ADCS enumeration.
Status: Integration PR pending review at Pennyw0rth/NetExec#1054
Try it now: Use thefeature/adcs-collectionbranch from 0x0Trace/NetExec
# ADCS only collection
nxc ldap 10.10.10.10 -u user -p pass --bloodhound -c ADCS
# Full collection including ADCS
nxc ldap 10.10.10.10 -u user -p pass --bloodhound -c All --dns-server 10.10.10.10
NetExec Integration Code
from certihound import ADCSCollector, BloodHoundCEExporter, ImpacketLDAPAdapter
# In NetExec's ldap.py:
adapter = ImpacketLDAPAdapter(
search_func=self.search,
domain=self.domain,
domain_sid=self.sid_domain,
)
collector = ADCSCollector.from_external(
ldap_connection=adapter,
domain=self.domain,
domain_sid=self.sid_domain,
)
data = collector.collect_all()
exporter = BloodHoundCEExporter(data.domain, data.domain_sid)
result = exporter.export(data)
result.write_zip("adcs_bloodhound.zip")
API Reference
Core Classes
| Class | Description |
|---|---|
ADCSCollector |
Main collector for ADCS enumeration |
BloodHoundCEExporter |
Exports data to BloodHound CE format |
ImpacketLDAPAdapter |
Adapter for impacket-based LDAP (NetExec) |
LDAPConnection |
Standalone LDAP connection wrapper |
LDAPConfig |
Configuration dataclass for LDAP connections |
Data Models
| Class | Description |
|---|---|
ADCSData |
Container for all collected ADCS data |
CertTemplate |
Certificate template with properties and ACLs |
EnterpriseCA |
Enterprise CA with enabled templates |
RootCA |
Root CA node |
NTAuthStore |
NTAuth certificate store |
AIACA |
AIA CA entry |
ExportResult |
Export result with write_zip(), write_json(), to_dict() |
Detection Functions
from certihound import (
detect_esc1,
detect_esc2,
detect_esc3_agent,
detect_esc3_target,
detect_esc4,
detect_esc5,
detect_esc6,
detect_esc7,
detect_esc8,
detect_esc9,
detect_esc10,
detect_esc11,
detect_esc13,
detect_esc14,
detect_esc15,
detect_esc16,
detect_esc17,
)
Usage Example
from certihound import (
ADCSCollector,
BloodHoundCEExporter,
ADCSData,
ExportResult,
)
# Collect data
collector = ADCSCollector.from_external(adapter, domain, domain_sid)
data: ADCSData = collector.collect_all()
# Access collected objects
print(f"Templates: {len(data.templates)}")
print(f"Enterprise CAs: {len(data.enterprise_cas)}")
print(f"Root CAs: {len(data.root_cas)}")
# Export to BloodHound
exporter = BloodHoundCEExporter(data.domain, data.domain_sid)
result: ExportResult = exporter.export(data)
# Output options
result.write_zip("output.zip") # ZIP for BloodHound import
result.write_json("output/") # Individual JSON files
output_dict = result.to_dict() # Python dictionary
Development
Setup Development Environment
# Clone repository
git clone https://github.com/0x0Trace/certihound.git
cd certihound
# Install with dev dependencies
pip install -e ".[dev]"
Running Tests
# Run all tests
pytest
# Run with coverage
pytest --cov=certihound
# Run specific test file
pytest tests/test_detection.py -v
Code Quality
# Format code
black certihound/
# Lint
ruff check certihound/
# Type checking
mypy certihound/
Dependencies
| Package | Purpose |
|---|---|
ldap3 |
LDAP operations (standalone mode) |
impacket |
Kerberos authentication & NetExec integration |
cryptography |
Certificate parsing and analysis |
pydantic |
Data validation and models |
click |
CLI framework |
rich |
Terminal output formatting |
License
This project is licensed under the MIT License - see the LICENSE file for details.
Credits & References
- Inspired by Certipy by Oliver Lyak
- BloodHound CE format based on BloodHound by SpecterOps
- ADCS vulnerability research: Certified Pre-Owned by Will Schroeder & Lee Christensen
Made with :heart: for the security community
Metadata
Release files for certihound 0.3.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| certihound-0.3.2.tar.gz | 72.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| certihound-0.3.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 165.1 kB
Release files / certihound-0.3.2.tar.gz
| Download URL | certihound-0.3.2.tar.gz |
|---|---|
| Size | 72.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b92a08e1bcf98771aecbc4ed7c78ec934ce8f4b34bd8127965114c14f936529d
|
|
BLAKE2b-256 checksum How to use checksums |
45309a6ccbefa74b399abbc2a556937eb62e87e44ae6fd6cd238d585421da420
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.9
|
Release files / certihound-0.3.2-py3-none-any.whl
| Download URL | certihound-0.3.2-py3-none-any.whl |
|---|---|
| Size | 92.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c68b6f8efde09843685b616772445f0d68bff0865f54df5b12f576c1eebd1edd
|
|
BLAKE2b-256 checksum How to use checksums |
e641a42e7351d28f2ef7c30edf27fd412365b89ff3efe4907d1839c9ece25990
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.9
|
Security for certihound 0.3.2
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
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.