Active Directory Vulnerability Scanner is a modular, Python-based tool that connects to domain controllers via LDAP, LDAPS, and/or SMB, runs a comprehensive battery of security checks, and produces a self-contained HTML dashboard report with a risk score.
- Multi-protocol: LDAP, LDAPS, SMB (user-selectable; defaults to all three)
- Flexible auth: password, pass-the-hash (NTLM
LM:NTorNT), Kerberos ccache, or interactive prompt - Risk score: starts at 100, deducted per finding (floor 0), letter grade A–F
- HTML report: fully self-contained, light/dark mode toggle, severity chips, collapsible finding cards
- JSON / CSV output: machine-readable exports alongside the HTML report
- Modular: drop a new
check_*.pyfile inchecks/; it's auto-discovered at runtime - 36 security checks covering the most critical AD attack surfaces
uv is a fast Python package manager. If you don't have it yet:
curl -LsSf https://astral.sh/uv/install.sh | shThen install ADScan:
git clone https://github.com/dehobbs/ADScan.git
cd ADScan
uv venv && source .venv/bin/activate
uv pip install -e .git clone https://github.com/dehobbs/ADScan.git
cd ADScan
python3 -m venv .venv
source .venv/bin/activate
pip install -e .Kali / externally-managed Python: If pip blocks the install with an "externally managed environment" error, create a virtual environment first (shown above).
pip install -e ".[docx]" # adds Word report export support
pip install -e ".[kerberos]" # adds gssapi for Kerberos over LDAP
pip install -e ".[dev]" # adds pytest, ruff, mypy for developmentAfter an editable install, adscan is available as a command directly:
adscan -d corp.local --dc-ip 10.10.10.5 -u alice -p 'P@ssw0rd!'Some checks use external CLI tools that run as subprocesses. These are installed into isolated virtual environments via uv tool install so their dependencies never conflict with ADScan's own packages.
One-command setup (installs all tools at once):
python adscan.py --setup-toolsOr install individually:
uv tool install certipy-ad # ADCS/PKI vulnerability scanner (requires Python 3.12+)
uv tool install netexec # SMB signing & SMBv1 detection (nxc command)These tools are optional. If a tool is missing when a check runs, ADScan will attempt to auto-install it via
uv tool install. Ifuvis not available, the check is skipped gracefully with an informational finding.
Core Python libraries (installed automatically with pip install -e .):
| Package | Purpose |
|---|---|
ldap3 |
LDAP / LDAPS connectivity |
impacket |
SMB connectivity, pass-the-hash, Kerberos auth |
pyOpenSSL |
LDAPS certificate handling |
gssapi |
Kerberos GSSAPI bindings for LDAP (optional — only needed for --kerberos) |
External CLI tools (installed separately via uv tool install):
| Tool | Package | Purpose |
|---|---|---|
certipy |
certipy-ad |
ADCS / PKI vulnerability enumeration (ESC1–ESC16) |
nxc |
netexec |
SMB signing enforcement & SMBv1 detection |
python adscan.py -d <domain> --dc-ip <dc_ip> -u <username> -p <password> [options]
python adscan.py -d <domain> --dc-ip <dc_ip> -u <username> --hash <LM:NT or NT> [options]
python adscan.py -d <domain> --dc-ip <dc_ip> -u <username> # prompts for password
python adscan.py -d <domain> --dc-ip <dc_ip> -u <username> --kerberos # use KRB5CCNAME
python adscan.py -d <domain> --dc-ip <dc_ip> -u <username> --ccache /tmp/user.ccache
| Flag | Description | Default |
|---|---|---|
-d / --domain |
Target domain FQDN (e.g. corp.local) |
required |
--dc-ip |
Domain controller IP or hostname | required |
-u / --username |
Username | required |
-p / --password |
Password (omit to be prompted securely) | — |
--hash |
NTLM hash (LM:NT or NT) |
— |
--kerberos |
Authenticate using a Kerberos ticket; reads ccache from KRB5CCNAME env var |
— |
--ccache PATH |
Path to a Kerberos ccache file (implies --kerberos; overrides KRB5CCNAME) |
— |
--protocol |
ldap | ldaps | smb | all |
all |
--timeout |
Connection timeout in seconds | 30 |
--format |
html | json | csv | all |
html |
--log-file PATH |
Write all log output (including DEBUG detail) to a file in addition to the console | off |
-o / --output |
Output report path stem | Reports/adscan_report_<timestamp> |
-v / --verbose |
Show DEBUG-level detail on the console (finding details, affected objects) | off |
--setup-tools |
Install external CLI tools (certipy-ad, netexec) into isolated venvs via uv and exit | — |
--list-checks |
Print all available check modules and exit | — |
Interactive password prompt: if neither
-p,--hash, nor--kerberosis supplied, ADScan prompts for a password without echoing it to the terminal.
ADScan supports Kerberos ticket reuse via a ccache file; the standard Linux credential store written by kinit, getTGT.py, and similar tools. This is useful when:
- The target environment has NTLM disabled (common in hardened AD configurations).
- You are operating in an assumed-breach scenario and already hold a valid TGT or service ticket.
- You want to avoid transmitting cleartext passwords or NT hashes over the wire.
Workflow using KRB5CCNAME:
# Obtain a TGT with impacket
getTGT.py corp.local/alice:'P@ssw0rd!' -dc-ip 10.10.10.5
export KRB5CCNAME=$(pwd)/alice.ccache
# ADScan picks it up automatically
python adscan.py -d corp.local --dc-ip 10.10.10.5 -u alice --kerberosUsing an explicit ccache path (no environment variable needed):
python adscan.py -d corp.local --dc-ip 10.10.10.5 -u alice --ccache /tmp/alice.ccacheNote: Kerberos authentication over LDAP requires the
gssapiPython package (pip install gssapi) and a working/etc/krb5.confpointing at your KDC. For SMB, impacket handles Kerberos natively with no extra dependencies beyond what is already inrequirements.txt.
# Password auth, all protocols
python adscan.py -d corp.local -u alice -p 'P@ssw0rd!' --dc-ip 10.10.10.5
# Interactive password prompt (no -p flag)
python adscan.py -d corp.local -u alice --dc-ip 10.10.10.5
# Pass-the-hash (NT only)
python adscan.py -d corp.local -u alice --hash aad3b435b51404eeaad3b435b51404ee:8846f7eaee8fb117ad06bdd830b7586c
# Kerberos ticket reuse (KRB5CCNAME set in the environment)
export KRB5CCNAME=/tmp/alice.ccache
python adscan.py -d corp.local -u alice --dc-ip 10.10.10.5 --kerberos
# Kerberos with an explicit ccache path
python adscan.py -d corp.local -u alice --dc-ip 10.10.10.5 --ccache /tmp/alice.ccache
# LDAPS only, custom output
python adscan.py -d corp.local -u alice -p 'Secret1' --protocol ldaps -o results/scan.html
# All output formats (HTML + JSON + CSV)
python adscan.py -d corp.local -u alice -p 'Secret1' --dc-ip 10.10.10.5 --format all
# Custom timeout
python adscan.py -d corp.local -u alice -p 'Secret1' --dc-ip 10.10.10.5 --timeout 60
# Write a persistent log file for post-engagement review
python adscan.py -d corp.local -u alice -p 'Secret1' --dc-ip 10.10.10.5 --log-file scan.log
# Verbose console output + full debug log file
python adscan.py -d corp.local -u alice -p 'Secret1' --dc-ip 10.10.10.5 -v --log-file debug.logADScan supports running targeted subsets of checks which is useful for time-constrained engagements or when you only want to assess a specific attack surface.
python adscan.py --list-checksPrints a formatted table of every check module with its slug, category, and display name:
ORDER SLUG CATEGORY CHECK NAME
----------------------------------------------------------------------------------------------------
1 password_policy Account Hygiene Domain Password Policy
2 account_hygiene Account Hygiene Account Hygiene
3 kerberos Kerberos Kerberos Attack Surface
...
Slugs are matched against the module filename, the words in CHECK_NAME, and the CHECK_CATEGORY value so kerberos, attack, and surface all match the Kerberos check.
# Run only Kerberos and delegation checks
python adscan.py -d corp.local --dc-ip 10.10.10.5 -u alice -p 'P@ss' --checks kerberos,delegation
# Run only ADCS-related checks
python adscan.py -d corp.local --dc-ip 10.10.10.5 -u alice -p 'P@ss' --checks adcs# Skip GPP and SMB checks (e.g. SMB is firewalled)
python adscan.py -d corp.local --dc-ip 10.10.10.5 -u alice -p 'P@ss' --skip gpp,smb
# Skip DNS and replication checks
python adscan.py -d corp.local --dc-ip 10.10.10.5 -u alice -p 'P@ss' --skip dns,replication--checks and --skip can be combined: --checks restricts the candidate set first, then --skip removes from it.
ADScan starts each scan at a score of 100 and deducts points per finding based on its severity. The final score maps to a letter grade:
| Score | Grade |
|---|---|
| 90–100 | A |
| 75–89 | B |
| 60–74 | C |
| 40–59 | D |
| 0–39 | F |
| Severity | Default Deduction |
|---|---|
| Critical | 20 pts |
| High | 15 pts |
| Medium | 8 pts |
| Low | 5 pts |
| Info | 0 pts |
Copy or edit scoring.toml (included in the repo) to tune deductions for your engagement without touching any check code.
Change the severity curve (affects all findings of that tier):
[severity_weights]
critical = 25
high = 15
medium = 5
low = 2
info = 0Override a single finding (exact title match, takes precedence over severity weights):
[overrides]
# Raise impact (unconstrained delegation is critical in your environment)
"User Accounts with Unconstrained Delegation" = 25
# Lower impact (compensating control is already in place)
"Kerberoastable Service Accounts" = 3
# Suppress from score entirely (finding still appears in the report)
"Lockout Observation Window Too Short" = 0Pass a custom config at runtime with --scoring-config:
python adscan.py -d corp.local --dc-ip 10.10.10.5 -u alice -p 'P@ss' --scoring-config my_scoring.tomlADScan is intended for authorised security assessments only. Always obtain written permission before scanning an Active Directory environment. Unauthorised use may violate computer crime laws.