diff --git a/README.md b/README.md new file mode 100644 index 0000000..ce14faa --- /dev/null +++ b/README.md @@ -0,0 +1,110 @@ +# 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 `