mirror of
https://github.com/crypt0p3g/adexsnap
synced 2026-09-12 21:48:46 +00:00
304 lines
11 KiB
Markdown
304 lines
11 KiB
Markdown
# adexsnap
|
|
|
|
Active Directory snapshots from Linux and macOS, in the AD Explorer `.dat` format.
|
|
|
|
adexsnap collects a full Active Directory snapshot from the command line and writes it in the
|
|
`.dat` format used by Sysinternals AD Explorer. The resulting file can be opened in AD Explorer
|
|
and is also supported by ADExplorerSnapshot.py and BOFHound.
|
|
|
|
The writer includes directory objects, schema and class metadata, extended rights, security
|
|
descriptors, value back-references, and the tree index used for navigation. The built-in
|
|
verifier checks the finished file before you open it or give it to another tool.
|
|
|
|
This project is not affiliated with or endorsed by Microsoft or Sysinternals. AD Explorer is
|
|
a Microsoft product; the snapshot format was implemented from analysis of snapshot files.
|
|
|
|
Only use this against directories you are authorized to enumerate. A snapshot can contain
|
|
ACLs, credentials-related metadata, and other sensitive information from the directory.
|
|
|
|
## Authorized and defensive use
|
|
|
|
For penetration testing and red-team work, obtain written authorization, define the target
|
|
scope, and agree how snapshots will be stored and destroyed before collecting. For blue-team
|
|
work, use the tool to validate directory exposure, compare authorized captures over time, and
|
|
exercise LDAP/Kerberos detections in a lab or approved assessment. The tool is for collection
|
|
and analysis; it does not bypass authorization or make enumeration invisible.
|
|
|
|
Treat every `.dat` file as sensitive: store it encrypted, restrict access to the engagement
|
|
team, and delete it when the retention period ends. The `.gitignore` excludes `.dat`,
|
|
`.ccache`, `.kirbi`, and `.keytab` files so they are not committed by accident.
|
|
|
|
## Requirements
|
|
|
|
- Python 3.10 or newer
|
|
- `ldap3`
|
|
- `winacl`
|
|
- `pycryptodome` for NTLM support on Python versions without built-in MD4
|
|
- a platform GSSAPI provider when using Kerberos (`gssapi` on most Linux/macOS systems)
|
|
- `impacket` only when converting a kirbi/KRB-CRED file
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
python3 -m venv env
|
|
source env/bin/activate
|
|
python3 -m pip install -r requirements.txt
|
|
|
|
# Optional: Kerberos (gssapi) and kirbi/KRB-CRED conversion (impacket)
|
|
python3 -m pip install -r requirements-optional.txt
|
|
```
|
|
|
|
The base requirements are enough for NTLM, simple bind, and `verify`. The optional file adds
|
|
`gssapi` for Kerberos and `impacket` for kirbi conversion; install only the one you need if
|
|
you prefer:
|
|
|
|
```bash
|
|
python3 -m pip install gssapi # Kerberos with a ccache
|
|
python3 -m pip install impacket # kirbi/KRB-CRED input
|
|
```
|
|
|
|
The verifier does not need the LDAP collection dependencies, so it can also be used on a
|
|
machine that cannot build the collection stack. On minimal systems, install the operating
|
|
system's Kerberos development/runtime packages before installing `gssapi`.
|
|
|
|
The project is developed and tested on macOS and Linux with Python 3.10 or newer. Windows is
|
|
not currently part of the tested support matrix; the file format and LDAP logic are intended
|
|
to be portable, but Kerberos/GSSAPI setup is platform-specific.
|
|
|
|
## Quick start
|
|
|
|
NTLM prompts for the password without echoing it:
|
|
|
|
```bash
|
|
python3 adexsnap.py snapshot \
|
|
--dc dc01.example.com --dc-ip 192.0.2.10 \
|
|
-u auditor -d EXAMPLE --workstation AUDIT01 -o example.dat
|
|
```
|
|
|
|
Kerberos uses the current credential cache:
|
|
|
|
```bash
|
|
export KRB5CCNAME=FILE:/absolute/path/auditor.ccache
|
|
python3 adexsnap.py snapshot \
|
|
--dc dc01.example.com --dc-ip 192.0.2.10 --kerberos -o example.dat
|
|
```
|
|
|
|
Check the result:
|
|
|
|
```bash
|
|
python3 adexsnap.py verify example.dat
|
|
```
|
|
|
|
`--dc` is the logical DC name. It is stored in the snapshot header and is also used for the
|
|
Kerberos `ldap/<dc>` SPN. `--dc-ip` is only the address used for the network connection. Keep
|
|
the FQDN in `--dc` when connecting to a fixed IP with Kerberos.
|
|
|
|
## Connecting to a DC
|
|
|
|
Use the normal DNS name when it resolves:
|
|
|
|
```bash
|
|
python3 adexsnap.py snapshot \
|
|
--dc dc01.example.com -u auditor -d EXAMPLE -o example.dat
|
|
```
|
|
|
|
If DNS is unavailable, keep the name for the SPN and supply the address separately:
|
|
|
|
```bash
|
|
python3 adexsnap.py snapshot \
|
|
--dc dc01.example.com --dc-ip 192.0.2.10 \
|
|
-u auditor -d EXAMPLE -o example.dat
|
|
```
|
|
|
|
For LDAPS, use `--ssl` (port 636 by default). `--port` can override the LDAP port:
|
|
|
|
```bash
|
|
python3 adexsnap.py snapshot \
|
|
--dc dc01.example.com --ssl -u auditor -d EXAMPLE -o example.dat
|
|
```
|
|
|
|
The default settings are `--page-size 1000`, `--speed 100`, and `--timeout 30`. To add
|
|
delays and request smaller pages, for example:
|
|
|
|
```bash
|
|
python3 adexsnap.py snapshot \
|
|
--dc dc01.example.com -u auditor -d EXAMPLE \
|
|
--page-size 500 --speed 50 --timeout 60 -o example.dat
|
|
```
|
|
|
|
Changing page size or speed changes request timing; it is not a promise of reduced logging or
|
|
detected activity.
|
|
|
|
## Authentication
|
|
|
|
### NTLM
|
|
|
|
Use `-u USER -d DOMAIN` and leave out `-p` to get a password prompt. The `--workstation`
|
|
value is sent as NTLM authentication metadata. It does not rename the local computer. If it
|
|
is omitted, adexsnap generates a Windows-style `DESKTOP-XXXXXXX` value for that run.
|
|
|
|
Avoid `-p` where possible because command-line arguments can remain in shell history and may
|
|
be visible in the local process list.
|
|
|
|
### Kerberos
|
|
|
|
Credential sources are checked in this order:
|
|
|
|
1. `--ticket`, `--ccache`, or `--kirbi`
|
|
2. `KRB5CCNAME`
|
|
3. the platform's default credential cache
|
|
|
|
Examples:
|
|
|
|
```bash
|
|
# MIT FILE ccache; both PATH and FILE:PATH are accepted
|
|
python3 adexsnap.py snapshot \
|
|
--dc dc01.example.com --ccache /absolute/path/user.ccache -o example.dat
|
|
|
|
# Raw ccache, raw DER kirbi, or Base64 KRB-CRED detected automatically
|
|
python3 adexsnap.py snapshot \
|
|
--dc dc01.example.com --ticket ticket.bin -o example.dat
|
|
|
|
# Explicit kirbi/KRB-CRED; requires Impacket
|
|
python3 adexsnap.py snapshot \
|
|
--dc dc01.example.com --kirbi ldap-ticket.kirbi -o example.dat
|
|
```
|
|
|
|
`KRB5CCNAME` should keep its normal `TYPE:residual` form, such as `FILE:`, `DIR:`, or
|
|
`KCM:`. A kirbi is converted to a temporary mode-`0600` ccache and removed after the LDAP
|
|
session. The input ticket file is not changed.
|
|
|
|
Useful checks before a capture:
|
|
|
|
```bash
|
|
printf '%s\n' "$KRB5CCNAME"
|
|
klist
|
|
```
|
|
|
|
### Simple bind
|
|
|
|
Simple bind is accepted only over LDAPS:
|
|
|
|
```bash
|
|
python3 adexsnap.py snapshot \
|
|
--dc dc01.example.com --ssl --auth simple \
|
|
-u auditor@example.com -o example.dat
|
|
```
|
|
|
|
## SOCKS5 and network routing
|
|
|
|
There is currently no `--socks5` option. `ldap3` does not provide a supported proxy setting
|
|
for this workflow, and adding a socket monkey-patch would only cover the LDAP TCP connection.
|
|
Kerberos/GSSAPI may separately resolve names and contact a KDC, so an apparent SOCKS option
|
|
could give a false impression that all traffic was proxied.
|
|
|
|
When every connection must use the same route, use a system-level TUN/VPN or a process-level
|
|
wrapper that has been tested with both Python and the host Kerberos libraries. If a cache
|
|
already contains the exact LDAP service ticket, a KDC connection may not be needed, but DNS
|
|
and LDAP still need to use the intended route.
|
|
|
|
## What gets collected
|
|
|
|
The capture includes:
|
|
|
|
- the domain, Configuration, and Schema naming contexts
|
|
- objects from those contexts in the order AD Explorer expects
|
|
- normal attributes plus `objectClass` and `ntSecurityDescriptor`
|
|
- deleted objects using the Show Deleted control
|
|
- owner, group, and DACL security information (not SACLs)
|
|
- schema attributes and classes, DisplaySpecifiers, and Extended Rights
|
|
- the populated AD Explorer tree index
|
|
|
|
The default filter is `(objectGUID=*)`, matching AD Explorer's capture query. Use
|
|
`python3 adexsnap.py queries` to see the available equivalent filter variants:
|
|
|
|
```bash
|
|
python3 adexsnap.py snapshot ... --query native
|
|
```
|
|
|
|
The variants change only the filter. Scope, controls, attributes, metadata, paging, and file
|
|
encoding stay the same.
|
|
|
|
## Output and verification
|
|
|
|
The collector writes one LDAP page at a time and uses a 1 MiB file buffer. It loads the
|
|
metadata it needs before serializing directory objects, then finalizes the file only after all
|
|
queries succeed. A failed capture leaves the `win-ad-XX` signature, which the verifier will
|
|
reject as an incomplete snapshot.
|
|
|
|
```bash
|
|
python3 adexsnap.py verify example.dat
|
|
python3 adexsnap.py verify example.dat --dump 3
|
|
```
|
|
|
|
`RESULT: PASS` means the file's structural checks passed. It does not prove that the directory
|
|
stayed unchanged during enumeration. Two captures can differ because of replication, volatile
|
|
attributes, timestamps, or writes made while they were collected. Compare decoded content,
|
|
not just file sizes or hashes.
|
|
|
|
A successful verification looks like this (counts will differ between directories):
|
|
|
|
```text
|
|
signature : b'win-ad-ob\x00' (complete)
|
|
objects decoded : 3691/3691 (OK)
|
|
treeview : POPULATED (..., OK)
|
|
RESULT : PASS
|
|
```
|
|
|
|
Output color defaults to `auto` and is disabled for redirected output. Use `--no-color`,
|
|
`--color never`, or `NO_COLOR=1` for plain logs.
|
|
|
|
Output names receive a `YYYYMMDD-HHMMSS` suffix by default. If a file with that timestamp
|
|
already exists, a counter is added. Use `--force` when you deliberately want the exact output
|
|
name and are prepared to replace an existing file:
|
|
|
|
```bash
|
|
python3 adexsnap.py snapshot --dc dc01.example.com \
|
|
-u auditor -d EXAMPLE -o example.dat --force
|
|
```
|
|
|
|
See [LICENSE](LICENSE) for licensing terms.
|
|
|
|
## Troubleshooting
|
|
|
|
| Problem | What to check |
|
|
|---|---|
|
|
| `ldap3 package is required` | Activate the intended environment and install `requirements.txt`. |
|
|
| `winacl is not installed` | Install `winacl`; verification alone does not need it. |
|
|
| Kerberos fails when `--dc` is an IP | Put the DC FQDN in `--dc` and its address in `--dc-ip`. |
|
|
| `KRB5CCNAME file does not exist` | Use an absolute `FILE:/path/cache` or pass `--ccache`. |
|
|
| kirbi conversion asks for Impacket | Install `impacket`, or provide a ccache instead. |
|
|
| `--auth simple requires --ssl` | Add `--ssl`, or use NTLM/Kerberos. |
|
|
| Bind timeout | Check DNS, routing, firewall rules, port, TLS, and `--timeout`. |
|
|
| Naming contexts are missing | Confirm the endpoint is an AD DS RootDSE and the account can read it. |
|
|
| The file retains `win-ad-XX` | The capture stopped before finalization; rerun from the beginning. |
|
|
| AD Explorer cannot navigate the file | Run `verify` and do not use a file that returns `FAIL`. |
|
|
| Two captures differ | Check replication and volatile attributes; compare decoded content. |
|
|
|
|
For a useful bug report, keep the console output, the command with secrets removed, Python and
|
|
dependency versions, verifier output, and the smallest non-sensitive reproduction.
|
|
|
|
## Interoperability notes
|
|
|
|
Snapshots produced by this tool open in AD Explorer and are parsed by ADExplorerSnapshot.py
|
|
and BOFHound. AD Explorer is sensitive to record ordering, DN placement, value-cache offsets,
|
|
metadata offsets, and the tree index, and the serializer and verifier account for these
|
|
details. If a future AD Explorer release changes the format, run `verify` and open an issue
|
|
with the version that stopped working.
|
|
|
|
Trust graph output requires real `trustedDomain` objects and correctly encoded trust
|
|
attributes. Likewise, BOFHound/BloodHound output depends on the source directory containing
|
|
the relevant product-specific objects and security descriptors; the snapshot writer cannot
|
|
invent missing SCCM, PKI, trust, or other directory data.
|
|
|
|
## Development
|
|
|
|
Run the test suite with:
|
|
|
|
```bash
|
|
python3 -m unittest -v
|
|
```
|
|
|
|
The tests cover snapshot parsing and validation, CLI options, authentication inputs, and
|
|
ticket conversion helpers.
|