Active Directory Discovery
The Infracast Active Directory plugin queries Active Directory via LDAP/LDAPS to enumerate users, groups, computers, organizational units, Group Policy Objects, and trust relationships. This data powers identity-aware analysis — attack paths, privilege escalation routes, and access reviews.
How It Works
- Infracast binds to a domain controller using a read-only service account
- LDAP queries enumerate all objects under the configured Base DN
- For each object type, Infracast retrieves a defined set of attributes
- GPO data is retrieved from SYSVOL via SMB (optional, but recommended)
- Results are normalized and written to the Infracast graph
LDAP Bind User Setup
Creating the Service Account
Infracast only needs read access to Active Directory. A standard domain user account is sufficient — no elevated privileges are required.
PowerShell (run on a DC or management workstation):
# Create the service account
New-ADUser `
-Name "Infracast Discovery" `
-SamAccountName "svc-infracast" `
-UserPrincipalName "svc-infracast@corp.example.com" `
-AccountPassword (ConvertTo-SecureString "YourSecurePassword!" -AsPlainText -Force) `
-Enabled $true `
-PasswordNeverExpires $true `
-CannotChangePassword $true `
-Description "Infracast discovery service account - READ ONLY" `
-Path "OU=Service Accounts,DC=corp,DC=example,DC=com"
Via Active Directory Users and Computers (ADUC):
- Right-click the Service Accounts OU → New → User
- Fill in name:
Infracast Discovery, logon name:svc-infracast - Set a strong password, check Password never expires, User cannot change password
Do not add the service account to any privileged groups (Domain Admins, Schema Admins, etc.). The default domain user permissions are sufficient for all LDAP read operations Infracast requires.
Confirming Read Permissions
By default, all domain users can read most AD attributes. However, some attributes are restricted:
| Attribute | Default Access | Notes |
|---|---|---|
unicodePwd | Restricted | Infracast never reads passwords |
ms-Mcs-AdmPwd (LAPS) | Restricted to authorized groups | Infracast reads LAPS metadata (not the password itself) |
msDS-PSOApplied | Read by all | Fine-Grained Password Policies |
userAccountControl | Read by all | Account flags (enabled, locked, etc.) |
memberOf | Read by all | Group memberships |
objectSid | Read by all | Security identifiers |
If you want Infracast to discover confidential attributes (rare), use dsacls to grant read access:
dsacls "CN=Schema,CN=Configuration,DC=corp,DC=example,DC=com" /G "corp\svc-infracast:GR"
Base DN Configuration
The Base DN (Base Distinguished Name) is the starting point for all LDAP queries. Set this to your domain root or a specific OU.
Finding your Base DN:
# PowerShell: show domain root DN
(Get-ADDomain).DistinguishedName
# Output: DC=corp,DC=example,DC=com
# Or query via ldapsearch
ldapsearch -x -H ldap://dc01.corp.example.com -b "" -s base defaultNamingContext
Common Base DN patterns:
| Domain | Base DN |
|---|---|
corp.example.com | DC=corp,DC=example,DC=com |
ad.company.org | DC=ad,DC=company,DC=org |
| Specific OU only | OU=Production,DC=corp,DC=example,DC=com |
Registering the Credential in Infracast
# LDAP with username/password
infracast creds add \
--plugin active-directory \
--name "corp-ad" \
--type ldap \
--host "dc01.corp.example.com" \
--username "svc-infracast@corp.example.com" \
--password-file /run/secrets/ad-svc-infracast-password
# LDAPS (TLS) — recommended for production
infracast creds add \
--plugin active-directory \
--name "corp-ad-secure" \
--type ldaps \
--host "dc01.corp.example.com" \
--port 636 \
--username "svc-infracast@corp.example.com" \
--password-file /run/secrets/ad-svc-infracast-password \
--ca-cert-file /run/secrets/corp-ca-chain.pem
LDAP vs LDAPS: LDAP (port 389) sends authentication in plaintext (though the bind password is protected). LDAPS (port 636) wraps the entire session in TLS. Use LDAPS in production. If LDAPS isn't available, configure LDAP StartTLS:
--type ldap-starttls --port 389
Configuring the Discovery Job
discovery:
jobs:
- name: corp-active-directory
plugin: active-directory
credential: corp-ad-secure
schedule: "0 */2 * * *" # every 2 hours
config:
host: "dc01.corp.example.com"
port: 636
use_tls: true
base_dn: "DC=corp,DC=example,DC=com"
# Optional: use a Global Catalog for multi-domain forests
use_global_catalog: true
global_catalog_port: 3269 # GC with TLS
# Optional: specify a different DC for load balancing
# Leave as hostname for DNS round-robin to domain controllers
dc_hosts:
- "dc01.corp.example.com"
- "dc02.corp.example.com"
- "dc03.corp.example.com"
# Page size for large directories (default: 1000)
ldap_page_size: 1000
# What to discover
collect:
users: true
groups: true
computers: true
organizational_units: true
gpos: true
gpo_links: true # links between GPOs and OUs
trusts: true # domain and forest trusts
fine_grained_password_policies: true
admin_sd_holder: true # objects with AdminSDHolder protection
service_principal_names: true # SPNs (Kerberoast targets)
delegation: true # constrained/unconstrained delegation
# Optional: exclude specific OUs from discovery
exclude_ous:
- "OU=Disabled Users,DC=corp,DC=example,DC=com"
- "OU=Test,DC=corp,DC=example,DC=com"
What Gets Discovered
Users
| Attribute | Description |
|---|---|
sAMAccountName | Logon name (pre-Windows 2000) |
userPrincipalName | UPN (email-format login) |
displayName | Full display name |
mail | Email address |
memberOf | Direct group memberships |
userAccountControl | Account flags (enabled, locked, password flags) |
pwdLastSet | Last password change date |
lastLogonTimestamp | Last logon (replicated) |
adminCount | Protected by AdminSDHolder (privileged account) |
servicePrincipalName | SPNs (Kerberoastable accounts) |
msDS-AllowedToDelegateTo | Constrained delegation targets |
Groups
| Attribute | Description |
|---|---|
sAMAccountName | Group name |
groupType | Security vs Distribution, Domain Local/Global/Universal |
member | Direct member objects (users, groups, computers) |
memberOf | Parent group memberships (nested groups) |
description | Group description |
Computers
| Attribute | Description |
|---|---|
dNSHostName | FQDN |
operatingSystem | OS name |
operatingSystemVersion | OS version |
lastLogonTimestamp | Last seen on domain |
userAccountControl | Account flags |
servicePrincipalName | Machine SPNs |
ms-Mcs-AdmPwdExpirationTime | LAPS expiry (metadata only) |
Group Policy Objects (GPOs)
| Attribute | Description |
|---|---|
| GPO name | Display name |
| GUID | Unique identifier |
| Links | OUs/containers the GPO applies to |
| Enforcement | Whether link is enforced |
| Status | Enabled/disabled |
| WMI filter | Associated WMI filter |
| Policies | Security settings (parsed from SYSVOL when accessible) |
Organizational Units
The full OU tree structure is mapped, including:
- OU hierarchy (parent/child relationships)
- GPO link associations
- Object counts per OU
Trusts
For multi-domain or multi-forest environments:
- Trust direction (inbound/outbound/bidirectional)
- Trust type (external/forest/shortcut/realm)
- SID filtering status
- Transitivity
Multi-Domain and Forest Setup
Child Domains in the Same Forest
Use the Global Catalog to discover objects across all domains in a forest in a single query:
config:
use_global_catalog: true
global_catalog_host: "dc01.corp.example.com"
global_catalog_port: 3269 # with TLS
base_dn: "DC=corp,DC=example,DC=com" # forest root
Separate Jobs Per Domain
For complex forests or when domains have separate Infracast credentials:
discovery:
jobs:
- name: ad-corp
plugin: active-directory
credential: corp-ad
config:
host: "dc01.corp.example.com"
base_dn: "DC=corp,DC=example,DC=com"
- name: ad-subsidiary
plugin: active-directory
credential: subsidiary-ad
config:
host: "dc01.subsidiary.com"
base_dn: "DC=subsidiary,DC=com"
Infracast automatically correlates trust relationships between separately discovered domains.
Troubleshooting
LDAP_INVALID_CREDENTIALS (error code 49)
Symptom: Error: LDAP Result Code 49 "Invalid Credentials"
Checks:
- Verify username format — try both
svc-infracast@corp.example.comandCORP\svc-infracast - Check if the account is locked out:
Get-ADUser svc-infracast -Properties LockedOut | Select LockedOut
# Unlock if needed:
Unlock-ADAccount -Identity svc-infracast - Test the bind manually:
ldapwhoami -H ldap://dc01.corp.example.com \
-D "svc-infracast@corp.example.com" \
-W
LDAP_OPERATIONS_ERROR / Size Limit Exceeded
Symptom: Discovery truncates after a certain number of objects
Cause: The DC's MaxPageSize or administrative limit is being hit.
Fix: Verify paged results are enabled and reduce page size:
config:
ldap_page_size: 500 # reduce from 1000
use_paged_results: true # should be true by default
Or increase the MaxPageSize on the domain controller (requires Domain Admin):
ntdsutil "ldap policies" "connections" "connect to server dc01" q "set maxpagesize to 5000" "commit changes" q q
LDAPS certificate error
Symptom: Error: x509: certificate signed by unknown authority
Fix: Export and trust the AD certificate chain:
# Export the CA certificate from Active Directory
certutil -ca.cert corp-ca.crt # run on Windows domain member
# Or use openssl to grab it
echo | openssl s_client -connect dc01.corp.example.com:636 -showcerts 2>/dev/null \
| openssl x509 > dc01-cert.pem
GPO data missing
Symptom: GPOs appear in the graph but contain no policy settings
Cause: SYSVOL access requires SMB connectivity. Infracast needs access to \\dc01.corp.example.com\SYSVOL.
Fix: Ensure the collector host can reach the DC via SMB (TCP/445) and the service account has read access to SYSVOL:
# Test from the collector
smbclient //dc01.corp.example.com/SYSVOL \
-U 'CORP\svc-infracast%YourPassword' \
-c "ls"
If SMB access isn't possible, disable SYSVOL collection:
config:
collect:
gpos: true # GPO metadata via LDAP (still useful)
gpo_policies: false # disable SYSVOL-based policy parsing
Discovery is slow (large directory)
Symptom: AD discovery takes more than 30 minutes for a large directory
Fix: Use multiple DC hosts and increase concurrency, or narrow the base DN:
config:
# Narrow scope
base_dn: "OU=Active Users,DC=corp,DC=example,DC=com"
exclude_ous:
- "OU=Archived,DC=corp,DC=example,DC=com"
# Use Global Catalog for cross-domain efficiency
use_global_catalog: true
# Increase page size if the DC supports it
ldap_page_size: 2000
Referral errors in multi-domain environments
Symptom: Logs show LDAP_REFERRAL errors during discovery
Cause: When querying a non-GC domain controller, it returns referrals to other DCs for objects in other domains.
Fix: Enable Global Catalog mode (port 3268/3269) or disable referral chasing:
config:
use_global_catalog: true
# Or: disable referral chasing
follow_referrals: false