Skip to content

Muhammad-Tayab/advalidate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ” advalidate

Native, lockout-aware, multi-protocol credential validation & access-mapping
for authorized Active Directory penetration testing.

CI python protocols license scope


πŸ“– Why I built this

On engagements, the moment you get a working credential the next question is always the same: "Okay β€” where else does this work, and where am I admin?" The usual answer is to spray it with NetExec/CrackMapExec across the subnet.

That works, but two things always bothered me:

  1. Lockouts. Spraying is the fastest way to lock out a real employee's account, trip a SOC alert, and burn the engagement. Most tools count nothing and trust you to do the math in your head.
  2. Black-box shelling out. Wrapping a dozen external binaries and grepping their stdout is fragile, and it hides why an attempt was skipped or failed.

So I built advalidate: it validates one set of credentials against many hosts over five protocols using native Python libraries in-process β€” no shelling out β€” and gates every single attempt behind a lockout budget it computes from the domain's own password policy. It tells you where your creds work and where you're admin, and it refuses to push any account toward its lockout threshold.

It's a scoping tool, not an exploitation tool β€” see what it deliberately does not do.


✨ What it does

  • 🧩 Five protocols, one process β€” SMB/WMI, LDAP(S), WinRM, SSH and RDP, each a native Python library. No NetExec, no CrackMapExec, no smbclient subprocess.
  • πŸ”‘ Three credential materials β€” user:password, user:NTLMhash (pass-the-hash), and Kerberos ccache tickets.
  • πŸ›‘οΈ Lockout-safety engine on every attempt β€” reads the domain password policy + fine-grained PSOs, keeps a per-account budget, and stops before the threshold. Falls back to a loud SAFE MODE when policy can't be read.
  • 🧠 Impossible-combo aware β€” a declarative capability map means it never fires an attempt that can't work (e.g. an NTLM hash over SSH). Those are skipped and logged β€” and a skip costs no lockout budget.
  • πŸ“Š Access matrix + prioritized findings β€” a rich table of fail / auth / admin per host Γ— protocol, plus a rule-based engine that surfaces what matters first (admin on many hosts, reaches the DC, admin not yet enumerated). All scoring is explainable β€” no AI/LLM calls.
  • 🌐 In-process SOCKS5 pivoting β€” --socks HOST:PORT routes every native handler through a proxy inside the process (proxychains can't β€” here's why).
  • πŸ’Ύ Persistent, cumulative state β€” a SQLite ledger keeps the budget honest across re-runs; add a credential later and the matrix/scoring build on everything learned so far.

🚫 What it's not

advalidate is a defensive-boundary tool. It validates credentials and maps access β€” nothing more. It does not:

  • ❌ extract credentials (no SAM / LSASS / NTDS dumping)
  • ❌ exploit vulnerabilities or run exploitation modules
  • ❌ self-propagate or move laterally on its own
  • ❌ ingest into BloodHound

Those are out of scope by design. This tool tells you where your existing, authorized credentials already work.


⚠️ Authorized use only

This software is for authorized security testing only β€” a signed penetration test, red-team authorization, or a lab/CTF you own. Validating credentials against systems you don't own or aren't authorized to test is illegal in most jurisdictions, and account-lockout activity is disruptive enough to count as denial of service. You alone are responsible for staying within your scope and rules of engagement. The lockout-safety engine reduces β€” but does not eliminate β€” the risk of locking out accounts.


⚑ Quick start

# 1. clone
git clone https://github.com/Muhammad-Tayab/advalidate.git
cd advalidate

# 2. isolated environment (recommended)
python3 -m venv .venv && source .venv/bin/activate

# 3. install dependencies + the `advalidate` command
pip install -r requirements.txt
pip install -e .

Confirm it's on your PATH:

advalidate --version
advalidate capabilities        # print the credential Γ— protocol map and exit

Prefer not to install? Run it straight from the repo β€” identical behaviour:

python -m advalidate capabilities
Kali / Debian notes (PEP 668, and aardwolf's Rust build)

On externally-managed Python (Kali/Debian) add --break-system-packages, or just use the venv above. On Kali the transport libraries are usually pre-installed; if aardwolf tries to build from source and fails needing setuptools_rust/a Rust toolchain, install the package alone against the already-present deps:

pip install -e . --no-build-isolation --no-deps --break-system-packages

πŸ“‹ Usage

advalidate is the installed command; python -m advalidate is the no-install equivalent.

# Validate one credential across a subnet, all protocols, lockout-safe
advalidate run --targets 10.10.0.0/24 --domain corp.local --cred 'jdoe:Summer2025!'

# Pass-the-hash (impossible combos are skipped automatically)
advalidate run --targets hosts.txt --domain corp.local \
  --cred 'administrator:aad3b4...:31d6cfe0d16ae931b73c59d7e0c089c0'

# Kerberos ticket from a ccache
KRB5CCNAME=jdoe.ccache advalidate run -t dc01.corp.local -d corp.local -c 'jdoe:@'

# Route everything through an in-process SOCKS5 pivot
advalidate run -t 10.10.0.0/24 -d corp.local -c 'jdoe:Summer2025!' --socks 127.0.0.1:1080

# Preview what WOULD be attempted, without sending a packet
advalidate run -t 10.10.0.0/24 -d corp.local -c 'jdoe:Summer2025!' --dry-run

# Enforce rules of engagement: refuse anything outside the authorized scope
advalidate run ... --scope 10.10.0.0/24 --scope @scope.txt

# Pace attempts (fixed delay + random jitter) to reduce timing/detection risk
advalidate run ... --delay 1 --jitter 3

# Manual policy override + persistent state + JSON report
advalidate run ... --lockout-threshold 5 --observation-window 30 --state corp.db --json report.json

Common options

Flag Purpose
-t, --targets host / IP / CIDR / @file (repeatable)
-d, --domain AD domain (FQDN)
-c, --cred user:password | user:[LM:]NT | user:@[ccache] (repeatable)
--scope authorized CIDR/host/@file; refuses everything outside it
--socks HOST:PORT in-process SOCKS5 routing for every native handler
--lockout-threshold / --observation-window supply domain policy manually
--safety-margin N headroom kept below the threshold (default 1)
--force override the budget entirely (requires interactive confirmation)
--dry-run print the attempt plan; send nothing
--delay / --jitter fixed + random pacing between attempts
--state SQLite ledger + results file (enables cross-run safety)
--json write access matrix + findings to a JSON file
-v, -vv show every skip / budget HOLD / the moment SAFE MODE is left

Run advalidate run -h for the full list.


🧠 The lockout-safety model

Account lockout is the single biggest risk when spraying, and it's the reason this tool exists. The model works in layers:

  1. Policy discovery. On the first valid credential, advalidate reads the domain password policy over LDAP/SAMR β€” lockoutThreshold and lockoutObservationWindow β€” and enumerates fine-grained password policies (PSOs) that can override the domain default per account or group, applying the most specific one to each target.

  2. Per-account budget. The budget is keyed on the account being authenticated, not the host. Every attempt across all protocols and all hosts decrements that one account's budget. Timestamps let entries age out of the observation window and free budget back up.

  3. The stop rule. It never lets an account reach its threshold:

    attempts_in_window  β‰₯  threshold βˆ’ safety_margin   β†’   HOLD
    

    With the default safety_margin = 1 and a threshold of 5, it uses at most 4 attempts per account per window, then holds.

  4. SAFE MODE. If policy can't be read (pre-auth, or the read fails), it does not guess optimistically β€” it drops to conservative hardcoded defaults (threshold 3 / 30 min / margin 1 β†’ 2 attempts per window) and prints a loud warning.

  5. Always visible, always overridable. It prints the active mode and per-account budget before spraying. --lockout-threshold / --observation-window supply policy manually, --safety-margin changes the headroom, and --force overrides the budget entirely β€” behind an explicit interactive confirmation, for accounts you know have no lockout policy (break-glass, lab targets).

Persistence. Lockout windows are long (often 30 min). Stop and re-run inside a window and an in-memory budget would forget prior attempts and could lock accounts β€” so the timestamped attempt ledger is persisted to SQLite (--state) and reloaded. The budget is honored across invocations.


🧩 Credential Γ— protocol capability matrix

advalidate knows which auth material each protocol can actually consume, and skips impossible combinations instead of failing them (a failed attempt burns lockout budget β€” a skipped one doesn't). The map is declarative and printable via advalidate capabilities.

Credential material SMB / WMI LDAP(S) WinRM SSH RDP
Password βœ… βœ… βœ… βœ… βœ…
NTLM hash (PtH) βœ… βœ… βœ… ❌ ⚠️ Restricted Admin only
Kerberos ticket βœ… βœ… βœ… ⚠️ GSSAPIΒΉ ⚠️ env-dependent
  • ❌ = skipped and logged β€” never attempted (e.g. SSH can't consume an NTLM hash).
  • ⚠️ RDP pass-the-hash works only when the target has Restricted Admin mode enabled; otherwise it's skipped.
  • ΒΉ SSH-over-Kerberos needs GSSAPI/GSS support on both ends; treated as environment-dependent.

🌐 Proxy routing: SOCKS5 vs proxychains

TL;DR β€” for the native handlers, use --socks. proxychains does not and cannot proxy them.

proxychains works by LD_PRELOAD-ing a shim that hooks libc's connect() for a child process it launches. advalidate's handlers are in-process Python libraries β€” impacket, ldap3, paramiko and aardwolf all open sockets inside this very process. There's no child process to wrap, so proxychains python -m advalidate ... would route nothing from the handlers.

So advalidate does the proxying itself:

  • --socks HOST:PORT (primary) wires every handler to a SOCKS5 proxy in-process β€” paramiko via a pre-connected socket, impacket via a SOCKS-wrapped socket, ldap3 through a proxied socket source, aardwolf via its native asysocks support.
  • --proxychains (secondary) prepends proxychains only to genuine subprocess calls (if any) β€” it has no effect on the native handlers, and the tool says so explicitly when you pass it.

πŸ—ΊοΈ How it fits together

advalidate/                 # repo root
β”œβ”€β”€ advalidate/             # the Python package
β”‚   β”œβ”€β”€ __main__.py         #   enables `python -m advalidate`
β”‚   β”œβ”€β”€ cli.py              #   click entry point (-> `advalidate` command)
β”‚   β”œβ”€β”€ handlers/           #   one native handler per protocol
β”‚   β”‚   β”œβ”€β”€ smb.py          #     impacket β€” SMB + WMI (+ admin detection)
β”‚   β”‚   β”œβ”€β”€ ldap.py         #     ldap3 β€” LDAP/LDAPS, NTLM + simple bind
β”‚   β”‚   β”œβ”€β”€ winrm.py        #     pywinrm β€” NTLM
β”‚   β”‚   β”œβ”€β”€ ssh.py          #     paramiko
β”‚   β”‚   └── rdp.py          #     aardwolf β€” NLA/CredSSP check (async, bridged)
β”‚   β”œβ”€β”€ core/
β”‚   β”‚   β”œβ”€β”€ credential.py   #     credential model (password / hash / ticket)
β”‚   β”‚   β”œβ”€β”€ capability.py   #     declarative cred Γ— protocol legality map
β”‚   β”‚   β”œβ”€β”€ engine.py       #     orchestrator: gates + sequences every attempt
β”‚   β”‚   β”œβ”€β”€ targets.py      #     host/CIDR/file expansion + in-scope guard
β”‚   β”‚   β”œβ”€β”€ store.py        #     validated-result store (persistent SQLite)
β”‚   β”‚   β”œβ”€β”€ matrix.py       #     access matrix: fail | auth | admin
β”‚   β”‚   └── scoring.py      #     rule-based prioritization / re-scoring engine
β”‚   β”œβ”€β”€ safety/
β”‚   β”‚   β”œβ”€β”€ policy.py       #     domain policy + PSO reader (LDAP/SAMR)
β”‚   β”‚   └── budget.py       #     per-account lockout budget + SAFE MODE + ledger
β”‚   └── proxy/socks.py      #   in-process SOCKS5 wiring for each library
β”œβ”€β”€ tests/                  # pytest suite (safety/scoring/CLI logic, no network)
β”œβ”€β”€ .github/workflows/ci.yml
β”œβ”€β”€ pyproject.toml          # packaging + `advalidate` console script
β”œβ”€β”€ requirements.txt
└── LICENSE

The entry point is advalidate.cli:main, exposed as both the advalidate console command and python -m advalidate.


πŸ“Š Output

A rich access matrix β€” one row per host, one column per protocol β€” followed by a prioritized findings block from the rule-based scoring engine:

Access Matrix β€” corp.local
┏━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━┳━━━━━━━┳━━━━━┳━━━━━┓
┃ host          ┃ SMB   ┃ LDAP ┃ WinRM ┃ SSH ┃ RDP ┃
┑━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━╇━━━━━━━╇━━━━━╇━━━━━┩
β”‚ dc01          β”‚ admin β”‚ auth β”‚ admin β”‚  –  β”‚ authβ”‚
β”‚ fs01          β”‚ admin β”‚ auth β”‚ auth  β”‚  –  β”‚  –  β”‚
β”‚ web01         β”‚ auth  β”‚  –   β”‚ fail  β”‚ authβ”‚  –  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”˜

Prioritized findings
 1. [CRITICAL] jdoe is local admin on 2 hosts (dc01, fs01)
 2. [CRITICAL] jdoe authenticates to the Domain Controller (dc01)
 3. [INFO]     admin on fs01 not yet enumerated

Add --json report.json for a machine-readable matrix + findings you can drop into a report.


πŸ§ͺ Tests

The suite covers the safety, scoring, capability and CLI logic β€” no network, no live targets:

pip install pytest
pytest -q

CI runs the same suite on Python 3.10 / 3.11 / 3.12 on every push (see .github/workflows/ci.yml).


πŸ“¦ Dependencies

Package Purpose
impacket SMB + WMI, SAMR/LDAP policy reads
ldap3 LDAP / LDAPS (NTLM + simple bind)
pywinrm WinRM (NTLM)
paramiko SSH
aardwolf RDP NLA / CredSSP credential check
python-socks, PySocks, asysocks in-process SOCKS5 routing
click CLI
rich access-matrix tables + colored logging

All pinned in requirements.txt. Not required: NetExec / CrackMapExec / evil-winrm / smbclient β€” every protocol is implemented natively.


πŸ—ΊοΈ Roadmap

Already implemented beyond the base spec: persistent cross-run attempt ledger (SQLite), in-scope CIDR scope-guard (--scope), per-attempt jitter/throttle, and --dry-run plan mode.

Planned: config-file support, richer group-aware PSO resolution, structured NDJSON streaming.

Permanently out of scope (by design): credential extraction (SAM/LSASS/NTDS), exploitation modules, autonomous propagation/lateral movement, BloodHound ingestion.


🀝 Contributing

Issues and pull requests are welcome. If you're adding a protocol handler, follow the handlers/base.py interface and keep it native and in-process β€” no shelling out to external toolkits. Please make sure pytest -q passes and add tests for any new safety/scoring logic.


πŸ“œ License

MIT β€” see LICENSE. Provided for authorized security testing and education only; use is entirely at your own risk and responsibility.


Built by Muhammad Tayab β€” because a working credential is only useful if you know exactly where it works, and locking out the client on day one isn't the way to find out.

About

Native, lockout-aware, multi-protocol credential validation & access-mapping engine for authorized AD penetration testing.

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages