Skip to main content

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

  1. Infracast binds to a domain controller using a read-only service account
  2. LDAP queries enumerate all objects under the configured Base DN
  3. For each object type, Infracast retrieves a defined set of attributes
  4. GPO data is retrieved from SYSVOL via SMB (optional, but recommended)
  5. 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):

  1. Right-click the Service Accounts OU → New → User
  2. Fill in name: Infracast Discovery, logon name: svc-infracast
  3. Set a strong password, check Password never expires, User cannot change password
warning

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:

AttributeDefault AccessNotes
unicodePwdRestrictedInfracast never reads passwords
ms-Mcs-AdmPwd (LAPS)Restricted to authorized groupsInfracast reads LAPS metadata (not the password itself)
msDS-PSOAppliedRead by allFine-Grained Password Policies
userAccountControlRead by allAccount flags (enabled, locked, etc.)
memberOfRead by allGroup memberships
objectSidRead by allSecurity 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:

DomainBase DN
corp.example.comDC=corp,DC=example,DC=com
ad.company.orgDC=ad,DC=company,DC=org
Specific OU onlyOU=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
info

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

infracast.yaml
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

AttributeDescription
sAMAccountNameLogon name (pre-Windows 2000)
userPrincipalNameUPN (email-format login)
displayNameFull display name
mailEmail address
memberOfDirect group memberships
userAccountControlAccount flags (enabled, locked, password flags)
pwdLastSetLast password change date
lastLogonTimestampLast logon (replicated)
adminCountProtected by AdminSDHolder (privileged account)
servicePrincipalNameSPNs (Kerberoastable accounts)
msDS-AllowedToDelegateToConstrained delegation targets

Groups

AttributeDescription
sAMAccountNameGroup name
groupTypeSecurity vs Distribution, Domain Local/Global/Universal
memberDirect member objects (users, groups, computers)
memberOfParent group memberships (nested groups)
descriptionGroup description

Computers

AttributeDescription
dNSHostNameFQDN
operatingSystemOS name
operatingSystemVersionOS version
lastLogonTimestampLast seen on domain
userAccountControlAccount flags
servicePrincipalNameMachine SPNs
ms-Mcs-AdmPwdExpirationTimeLAPS expiry (metadata only)

Group Policy Objects (GPOs)

AttributeDescription
GPO nameDisplay name
GUIDUnique identifier
LinksOUs/containers the GPO applies to
EnforcementWhether link is enforced
StatusEnabled/disabled
WMI filterAssociated WMI filter
PoliciesSecurity 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:

  1. Verify username format — try both svc-infracast@corp.example.com and CORP\svc-infracast
  2. Check if the account is locked out:
    Get-ADUser svc-infracast -Properties LockedOut | Select LockedOut
    # Unlock if needed:
    Unlock-ADAccount -Identity svc-infracast
  3. 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