# ad-probe Read-only Active Directory structure/health audit. Two equivalent implementations depending on what access you have to the target domain, plus a converter for turning the report into a shareable HTML page. - **`src/ad_audit.py`** — queries over raw LDAP (`ldap3`) with a basic, non-admin bind account. Works from any Linux/macOS/Windows box with network access to a DC; no RSAT or domain membership required. - **`src/Invoke-ADAudit.ps1`** — the same audit using the `ActiveDirectory` PowerShell module (RSAT). Run it on a domain-joined machine, typically under your own logon. - **`src/md_to_html.py`** — renders either script's Markdown report as a single self-contained, styled HTML file (sortable tables, sidebar TOC, local-time toggle, severity-coded findings). Neither audit script writes to the directory or requires elevated rights — they only read what a standard authenticated user/account can already see. ## What it collects - **Object type counts** across the whole subtree - **OUs** — full list, nesting depth - **Users** — enabled/disabled, locked out, password-never-expires, password-not-required (blank password allowed), never-logged-on, stale (configurable inactivity threshold), `adminCount=1`, unconstrained delegation, AS-REP roastable (Kerberos pre-auth disabled) - **Computers** — enabled/disabled, stale, OS breakdown - **Groups** — security vs. distribution, scope (domain-local/global/ universal), member counts, empty groups, largest groups Each report opens with an **Executive Summary**: headline counts plus a severity-ranked (Critical → Info) **Risk & Cleanup Findings** table, with every finding expanding into the actual list of matching accounts/ computers/groups — not just a count — so it's actionable straight out of the box. ## Usage ### Python / LDAP ```bash pip install -r requirements.txt python3 src/ad_audit.py \ --host dc01.corp.example.com \ --base-dn "DC=corp,DC=example,DC=com" \ [--ssl | --starttls] \ [--stale-days 90] \ [--out-dir reports] ``` You'll be prompted for a bind DN and password at runtime; nothing is stored on disk. Pass `--no-verify-cert` for a DC with a self-signed certificate. Writes `ad_audit_report_.md` and `ad_audit_raw_.json` to `--out-dir` (default `reports/`). ### PowerShell / RSAT ```powershell .\src\Invoke-ADAudit.ps1 ``` Runs under your current logon by default. Optional parameters: ```powershell .\src\Invoke-ADAudit.ps1 ` -Server dc01.corp.example.com ` -SearchBase "OU=Corp,DC=corp,DC=example,DC=com" ` -StaleDays 120 ` -Credential (Get-Credential) ` -OutDir .\reports ``` Requires the `ActiveDirectory` module (RSAT): `Add-WindowsCapability -Online -Name 'Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0'` ### HTML report ```bash python3 src/md_to_html.py # newest report in reports/ python3 src/md_to_html.py reports/ad_audit_report_....md -o out.html ``` Produces one `.html` file with no external dependencies — click any table header to sort, use the sidebar to jump to a section or an individual finding, and toggle timestamps between UTC and your local offset (both RFC 3339 either way). ## Notes - All timestamps in every report (Markdown, JSON, HTML) are RFC 3339 UTC (`YYYY-MM-DDTHH:MM:SSZ`). - `reports/` is gitignored — audit output contains real directory data and should never be committed. - AD attribute values (descriptions, sAMAccountName, OS strings, DNs) are directory content that could be attacker-influenced, not report-generated text. Both audit scripts escape them before they reach a Markdown table cell, and `md_to_html.py` independently neutralizes any raw HTML in its input as defense-in-depth — so a stray `