GitHub - d-woosley/SambAudit: Find sensitive data hiding on your SMB file shares before someone else does

GitHub

8 min read Original article ↗

A multi-threaded SMB file secret scanner that discovers hosts and shares in parallel, BFS-crawls directory trees with regex-based pattern detection, and ships a web UI for triaging findings and exporting reports

Features

  • LDAP host discovery: query Active Directory for computer accounts, or supply your own host list
  • Parallel share enumeration: authenticate and list accessible shares across all hosts concurrently
  • BFS crawl: explores directory trees level-by-level across all shares simultaneously, with live progress bars
  • File classification: every file is categorised into one of 14 types by extension and filename
  • Content inspection: Snaffler-style regex rules scan file contents for secrets and promote matches to severity-rated findings; custom rules supported
  • OCR image scanning: runs Tesseract on images to catch passwords and secrets in screenshots or photos
  • Archive unpacking: downloads and indexes the contents of zip, tar, and 7z files
  • NTLM and Kerberos auth: full pass-the-hash and ccache support via Impacket
  • Persistent SQLite database: every host, share, file, and log entry is stored; kill and resume anytime
  • Web UI (sambaudit-web): dashboard, file explorer, and a findings triage workflow with report export

Triage Report WebUI Tab

Triage report

Web UI

sambaudit-web serves a single-page app with five tabs:

  • Overview: file/finding counts, category breakdown, and timelines of files and findings discovered over time
  • Explorer: browse hosts → shares → directories → files, and preview a file's contents in-browser
  • Findings: Filter findings and mark findings as true/false positive
  • Scan History: list, resume, or export/import past scan runs
  • Rules: browse, edit, and add custom secret-detection rules
  • Triage: review all marked findings and compile findings into a report

Authentication is required on first load; a generated password is printed to stdout on first run and must be changed before use.

Content inspection also extracts and scans text from documents and images, not just plain text files. This includeds Office documents (.docx, .xlsx, .pptx, and their legacy .doc/.xls/.ppt equivalents), PDFs, archives, and, with --ocr, text embedded in images.

Findings Triage WebUI Tab

Findings

Installation

pipx (recommended)

Requires Python 3.10+. Install directly from GitHub with pipx:

pipx install git+https://github.com/d-woosley/SambAudit

Or clone and install locally:

git clone https://github.com/d-woosley/SambAudit
cd SambAudit
pipx install .

System dependencies

sambaudit itself has no system-level requirements beyond Python 3+, however, the --ocr flag requires Tesseract to be installed separately. OCR is enabled by default on the full scan (--full-scan)

Platform Command
macOS brew install tesseract
Debian / Ubuntu sudo apt install tesseract-ocr
Fedora / RHEL sudo dnf install tesseract
Windows winget install UB-Mannheim.TesseractOCR (then restart your shell)

Quick Start

Create a .env file in your working directory with your target and credentials:

SAMBAUDIT_DC=dc01.corp.local
SAMBAUDIT_DC_IP=192.168.1.10
SAMBAUDIT_DOMAIN=corp.local
SAMBAUDIT_USERNAME=alice
SAMBAUDIT_CCACHE=/home/alice/.ccache/krb5.ccache

Then run a full audit and open the web UI once it finishes:

sambaudit --full-scan && sambaudit-web

Full Scan Screenshot

Full scan

File Explorer WebUI Tab

File explorer


Environment variables

All credential and targeting options can be set in a .env file in the working directory, or in ~/.sambaudit as a global fallback:

SAMBAUDIT_DC=dc01.corp.local
SAMBAUDIT_DC_IP=192.168.1.10
SAMBAUDIT_DNS_SERVER=192.168.1.10
SAMBAUDIT_DOMAIN=corp.local
SAMBAUDIT_USERNAME=alice
SAMBAUDIT_CCACHE=/home/alice/.ccache/krb5.ccache
SAMBAUDIT_DB=/path/to/sambaudit.db

Sharing scan results

Scan data can be exported as JSON and imported into another database.

Export a scan by name:

sambaudit --export-scan my-scan-name --output /tmp/findings.json

Import on another machine:

sambaudit --import-scan my-scan-name.json --db shared.db

How It Works

Phase 1: Host Discovery

Either query LDAP for all enabled computer objects in the domain, or read a flat file of hostnames and IPs. Each discovered host is written to the database.

sambaudit --share-discovery   # discover hosts and enumerate shares, then stop

Phase 2: Share Enumeration

A thread pool connects to every discovered host, authenticates, and calls NetShareEnum. Accessible shares are stored in the database. System shares (IPC$, SYSVOL, NETLOGON, PRINT$, FAX$) are skipped during crawl by default; use --include-system-shares to include them.

Phase 3: BFS Crawl

A Breadth-First Search across all accessible shares simultaneously. Two thread pools run concurrently: a per-share coordinator that drives the BFS loop, and a shared directory listing pool (--threads workers) that performs the actual directory calls. If the process is killed, discovered but not yet listed directories remain in the database and --resume picks up exactly where the run stopped.

sambaudit   # runs phases 1 through 3

Phase 4: Content Scanning

After the crawl, sambaudit fetches and scans file contents for sensitive patterns: API keys, connection strings, password assignments, private key material, and more. Rules are defined in TOML files bundled with the tool, ported and extended from Snaffler's rule set. Files that match are promoted to named findings with a severity rating. Scanning can be scoped to a single host or share and restricted by file size.

sambaudit --inspect                           # crawl then scan all files
sambaudit --inspect-host fileserver1          # scan only files from one host
sambaudit --reinspect --rules-dir ./my-rules  # re-scan with updated rules

Phase 5: Analysis

sambaudit-web opens the SQLite database read-only and serves a password-protected HTTPS web interface. It binds to 127.0.0.1 by default; pass --host 0.0.0.0 to expose it on the network (use with caution as the data includes sensitive findings).

sambaudit-web
sambaudit-web --db /path/to/sambaudit.db --port 9090

Examples

Single host, anonymous:

sambaudit --target 192.168.1.50

LDAP discovery with NTLM auth:

sambaudit --dc dc01.corp.local --dc-ip 192.168.1.10 -D CORP -u alice -p 'S3cr3t!'

Kerberos via ccache:

sambaudit --dc dc01.corp.local --dc-ip 192.168.1.10 --ccache /tmp/alice.ccache

Share discovery only (no crawl):

sambaudit --dc dc01.corp.local -D CORP -u alice -p 'S3cr3t!' -sd

Resume an interrupted run:

Crawl with 40 threads, max depth 5, and unpack archives:

sambaudit --hosts-file targets.txt -u alice -p 'S3cr3t!' -t 40 --max-depth 5 --unpack-archives

Hosts file with IP overrides:

# targets.txt: plain hostname or hostname=ip
dc01.corp.local=192.168.1.10
fileserver.corp.local=192.168.1.20

Re-inspect a specific share after adding new rules:

sambaudit --reinspect --inspect-share Finance --rules-dir ./my-rules/

Usage

sambaudit — SMB share auditing and monitoring for security engineers

options:
  -h, --help            show this help message and exit

authentication:
  -u, --username USER   Username for SMB/LDAP authentication (env: SAMBAUDIT_USERNAME)
  -p, --password PASS   Plaintext password for authentication
  --hash [LM:]NT        NTLM hash for pass-the-hash (LM:NT or bare NT hash)
  -k, --kerberos        Use Kerberos authentication
  --ccache FILE         Path to Kerberos ccache file — enables Kerberos automatically (env: SAMBAUDIT_CCACHE)
  -D, --domain DOMAIN   Windows domain name (e.g. CORP or corp.local) (env: SAMBAUDIT_DOMAIN)

targeting:
  --dc HOST             Domain controller FQDN — used for Kerberos SPN (env: SAMBAUDIT_DC)
  --dc-ip IP            Domain controller IP — used when DNS cannot resolve --dc (env: SAMBAUDIT_DC_IP)
  --dns-server IP       Custom DNS server IP — overrides system DNS and --dc-ip for name resolution (env: SAMBAUDIT_DNS_SERVER)
  --hosts-file FILE     File with one hostname or IP per line (skips LDAP)
  --target HOST         Single hostname or IP to audit

mode:
  -sd, --share-discovery
                        Stop after share enumeration — do not crawl file contents
  --resume [NAME]       Resume a previous scan by name; omit name to select interactively
  --name NAME           Name for this scan run (default: auto-generated three-word phrase)
  --include-system-shares
                        Crawl system shares (IPC$, SYSVOL, NETLOGON, PRINT$, FAX$) — skipped by default
  -fs, --full-scan      Full audit: discover and crawl with --unpack-archives, then --inspect with --ocr; exports the crawl and inspect scans to <name>-crawl.json and <name>-inspect.json

crawl:
  -t, --threads N       Concurrent directory-listing threads (default: 50)
  --max-depth N         Maximum directory depth to crawl (default: 10)
  --recrawl             Re-crawl shares already in the database without re-discovering hosts
  --crawl-host HOSTNAME
                        Limit crawl to a specific host (use with --recrawl for DB-only scope)
  --crawl-share SHARE   Limit crawl to a specific share name (use with --recrawl for DB-only scope)
  --no-dfs-dedup        Disable DFS namespace deduplication (enabled by default when --dc is used)
  --unpack-archives     Download and unpack archive files (zip, tar, 7z, iso) during crawl, indexing their contents
  --max-archive-bytes MB
                        Max per-archive size in MB to download and unpack (default: 500)

output:
  --db PATH             Path to the SQLite database file (default: /Users/duncan/Desktop/Code/SambAudit/.sambaudit.db)
  -d, --debug           Print debug messages to the console (always logged to the database)
  --export-scan NAME    Export a scan by name to JSON (use with --output)
  --import-scan FILE    Import a previously exported scan JSON file into the database
  --output FILE         Output file path for --export-scan (default: <scan-name>.json)

inspect:
  --inspect             Inspect file contents for sensitive patterns after crawling
  --reinspect           Re-inspect all files in the database, including previously inspected ones (implies --inspect)
  --inspect-host HOSTNAME
                        Limit content inspection to files from a specific host
  --inspect-share SHARE
                        Limit content inspection to files from a specific share name
  --inspect-ram-limit MB
                        Max RSS memory (MB) before skipping a file fetch (default: 2048)
  --inspect-threads N   Concurrent inspector threads per host (default: 5)
  --inspect-max-size MB
                        Max individual file size to fetch for inspection in MB (default: 200)
  --rules-dir PATH      Directory of TOML rule files for content inspection (default: bundled rules)
  --ocr                 Run Tesseract OCR on image files during inspection to extract text for secret scanning (requires tesseract system binary)
  --ocr-threads N       Concurrent Tesseract OCR threads (default: 2; CPU-bound — keep low unless you have spare cores)

by Duncan Woosley (github.com/d-woosley)

License

MIT License; see LICENSE for details.

Copyright (c) 2026 Duncan Woosley

Credits

sambaudit is inspired by Snaffler and shrawler.

Impacket (by Fortra / SecureAuth) is the protocol foundation that sambaudit is built on.

Snaffler inspired the file classification system and the content inspection engine. The TOML rule files bundled with sambaudit are ported from Snaffler's C# rule set and adapted for Python regex. The 14-category file classification scheme and the concept of promoting matched files to severity-rated findings both originate from Snaffler.

Sulla (by Praetorian) introduced the idea of querying Active Directory for DFS namespace configurations before scanning begins and deduplicating targets whose physical shares back the same DFS link. sambaudit's DFSQuerier and the --no-dfs-dedup flag implement this concept.