Skip to content

CreativeAcer/LeakLens

Repository files navigation

LeakLens – Credential Exposure Scanner for File Shares

LeakLens Banner

🔍 Catch exposed credentials on file shares before they become an incident

LeakLens scans SMB/UNC shares and local paths for passwords, keys, tokens, and secrets —
with confidence scoring, live browser streaming, and zero mounting required.

Built for pentesters • Blue teams • Security auditors • DFIR

Tools like truffleHog and gitleaks excel at scanning Git repositories and CI artifacts. LeakLens targets a different, often neglected attack surface: shared folders and internal file servers. It scans \\server\share directly without mounting or drive letters, and streams confidence-scored findings to a browser in real time.

Born from a pentest finding: a domain admin password sitting in a plaintext .ps1 file on an open file share, readable by every user in the domain. LeakLens exists to find those before an attacker does.

How LeakLens fits alongside other secret scanners

LeakLens complements tools like truffleHog and gitleaks by focusing on a different attack surface: file shares and internal paths.

Tool Primary focus Native SMB (no mount) Share enumeration Git history Live UI streaming Resume scans
LeakLens File shares, local paths ✅ Yes ✅ Yes ❌ No ✅ Yes ✅ Yes
truffleHog Git, CI, cloud, filesystem ⚠️ No (os mounted paths only) ❌ No ✅ Yes ❌ No ❌ No
gitleaks Git repos & commits ❌ No ❌ No ✅ Yes ❌ No ❌ No

LeakLens is not a replacement for repository secret scanners, it fills the gap where credentials leak into shared folders, scripts, and internal file servers. The biggest difference being Native SMB/UNC (and ease of use - personal taste)


What it does

Scans Windows file shares (and local paths) for exposed credentials:

  • Connects directly to SMB/UNC paths — no manual mount, no drive letter required
  • Enumerates shares on a server with one click
  • Scans text files for passwords, hashes, keys, tokens, and connection strings
  • Flags sensitive file types (.pfx, .ppk, .kdbx, .pem, ...) and risky filenames (id_rsa, credentials, .env, ...)
  • Scores every finding 1–10 so you spend time on what matters
  • Shows the exact matched line and line number for every content finding — no need to open the file
  • Streams results to a browser UI in real time using Server-Sent Events
  • Scans files in parallel using a configurable number of worker threads
  • Saves findings to a SQLite database per scan for fast historical querying
  • Can resume interrupted scans from a checkpoint without rescanning files already processed
  • Saves a timestamped JSON report of every scan, browsable in the Reports tab

Requirements

  • Python 3.11+
  • Dependencies (pinned):
    • flask==3.1.3
    • smbprotocol==1.16.0

Share enumeration — no extra packages required:

Platform Method used automatically
Windows Built-in net use + net view — supports credentials, works on all Windows versions
Linux smbclient binary — apt install smbclient
macOS smbclient binary — brew install samba

Optional: impacket>=0.12.0 is used first if installed (cross-platform, slightly faster). It is never required.


Quick Start

Windows

start.bat

Linux / macOS

chmod +x start.sh && ./start.sh

Or run directly:

python3 -m pip install -r requirements.txt
python3 leaklens.py

Opens at http://localhost:3000.

The server binds to 127.0.0.1:3000 by default. Override with environment variables: LEAKLENS_HOST and LEAKLENS_PORT.

For a full walkthrough of every feature — SMB share browsing, confidence scoring, custom patterns, suppression, webhooks, and CLI usage — see USAGE.md.


Scanning a file share

Option A — Enter a UNC path directly

Type a UNC path such as \\server\share into the Path field and click Start Scan. Credentials are read from the SMB: Browse Shares & Credentials modal if you need them.

Option B — Browse and select a share (recommended for SMB)

  1. Click ⬡ SMB: Browse Shares & Credentials in the Scan Configuration panel
  2. Enter the server address in the Server / Host field
    • Standard port: 192.168.1.10
    • Non-standard port: 192.168.1.10:4445 (or hostname:port)
  3. Optionally enter Username, Password, and Domain — leave blank for guest/anonymous access
  4. Click ⬡ Discover Shares — all visible shares appear in a list (admin shares are labelled separately)
  5. Click any share to auto-populate the Path field and close the modal
  6. Back in the main panel:
    • Set Max Size (default 10 MB) and Worker Threads (default 8)
    • Enable Resume to continue an interrupted scan from its checkpoint
  7. Click Start Scan — findings stream in as they are found
  8. Click any row to see the exact matched line, file metadata, and remediation advice
  9. Open the Reports tab at any time to reload a previous scan

What it detects

Content patterns

Pattern Examples Confidence
Plaintext Password password=, "password": "...", DB_PASSWORD= 8/10
Connection String Embedded passwords in connection strings 8/10
NTLM / LM Hash lm_hash:ntlm_hash pairs 8/10
Bcrypt Hash $2a$, $2b$, $2y$ 8/10
Base64 Credential Base64 values next to credential keywords 5/10
AWS Access Key AKIA… 9/10
GitHub / GitLab PAT ghp_…, gho_…, glpat-… 10/10
Stripe API Key sk_live_…, sk_test_… 10/10
Slack Token xoxb-…, xoxp-… 10/10
SendGrid API Key SG.xxxx.xxxx 10/10
Azure Client Secret / Storage Key Azure credential formats 8/10
DPAPI Encrypted Blob DPAPI blob headers (base64 or hex) 8/10
API Key / Bearer Token Key assignments and Bearer tokens 6/10
Private Key Header -----BEGIN … PRIVATE KEY----- 10/10
Net Use Credential net use /user: commands 8/10
PowerShell SecureString Hardcoded ConvertTo-SecureString literals 8/10
PowerShell PSCredential Hardcoded PSCredential objects 6/10
SQL sa Password sa password = 8/10
MD5 / SHA1 / SHA256 / SHA512 Hash strings by length 3–4/10

File type flags (no content scan needed)

.kdbx .kdb .pfx .p12 .ppk .pem .key .jks .wallet

Filename flags

Files whose name contains: password, creds, secret, token, id_rsa, apikey, .env, and similar.


Confidence scoring

Every finding gets a score from 1–10 based on how certain the match is:

Score Meaning
9–10 Near-certain — private key, AWS access key
7–8 High confidence — plaintext password, NTLM hash, connection string, SecureString
5–6 Moderate — API key pattern, PSCredential, suspicious filename
3–4 Low signal — hash strings that could be checksums rather than credentials

Confidence is reduced automatically for files in docs/, examples/, test/, and similar directories. Files where only generic hash patterns match are downgraded to LOW with a note.


False positive reduction

  • Common placeholders (changeme, example, ${password}, ***, etc.) are excluded from password matches
  • Docs and example directories reduce confidence by 3 points automatically
  • Hash-only findings are demoted to LOW with the note: "Hash strings detected — verify these are credential hashes and not integrity checksums."
  • Lockfiles (package-lock.json, yarn.lock, poetry.lock, etc.) are skipped entirely — they are dense with hash strings that would otherwise flood results

Suppressing known noise (.leaklensignore)

Drop a .leaklensignore file in your scan root to silence known-safe paths. The format mirrors .gitignore:

# Suppress entire paths
archive/**
docs/**
*.example.config

# Suppress a specific pattern type in specific paths
[md5_hash]
checksums/**
*.md5

Pattern IDs are listed in scanner/patterns.py.


Match evidence

Every content finding includes the exact line that triggered the match. The detail drawer shows each detected pattern alongside its line number and the full matched line, so you can confirm the finding without opening the file:

Plaintext Password    Line 4
  DB_PASSWORD=Tr0ub4dor&3

AWS Access Key        Line 7
  AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE

Lines are truncated at 120 characters. Findings flagged only by file type or filename (e.g. .pfx, id_rsa) show a note in place of a snippet.


Scan reports

Every completed scan is saved as a timestamped JSON file (LeakLens_<timestamp>.json) and a SQLite database (LeakLens_<timestamp>.db) in the reports/ directory.

The Reports tab in the UI lists all saved reports — click any entry to reload its findings into the main view, complete with filtering, search, and the detail drawer.

The SQLite database enables fast paginated queries against historical scans without loading the entire result set into memory.


Resume interrupted scans

If a scan is stopped before completion, LeakLens writes a checkpoint file to reports/. Enabling the Resume checkbox on the next scan of the same path will skip already-processed files and continue from where it left off.


Multi-threaded scanning

The scanner uses a producer/consumer architecture:

  • A single walk thread traverses the file tree and feeds a bounded queue
  • A configurable number of worker threads (default 8, max 16) analyse files in parallel
  • Results flow through an events queue back to the SSE generator

This allows I/O-bound SMB reads to overlap with CPU-bound pattern matching. The current scan rate (files/second) is shown live in the progress bar.


SMB metadata in findings

When scanning a UNC path, findings include share context that is useful in an audit report:

{
  "smbServer":       "fileserver01",
  "smbShare":        "\\\\fileserver01\\IT",
  "smbRelativePath": "scripts\\deploy.ps1",
  "lastModified":    "2024-11-14 08:23",
  "lastAccessed":    "2025-01-02 13:45"
}

The detail drawer also surfaces this information alongside tailored remediation advice that references the share name.


Risk levels

Level Criteria
🔴 HIGH Confidence ≥ 8 — private key, plaintext password, AWS key, sensitive binary file
🟡 MEDIUM Confidence 5–7 — API key pattern, PSCredential, suspicious filename
🟢 LOW Confidence ≤ 4 — hash strings, low-signal patterns

LeakLens scan screenshot


API reference

Method Endpoint Description
POST /api/scan Start a scan. Returns an SSE stream of events.
POST /api/scan/stop Stop the active scan.
GET /api/status {scanning: bool, version: str}
POST /api/shares Enumerate SMB shares on a host.
GET /api/scans List all SQLite-backed scan metadata.
GET /api/scans/<scan_id>/export Export all findings for a scan as JSON.
GET /api/findings Paginated findings query against a scan DB.

POST /api/scan — body parameters

Parameter Type Default Description
scanPath string UNC or local path to scan (required)
maxFileSizeMB int 10 Skip files larger than this
workers int 8 Number of parallel worker threads (1–16)
resume bool false Resume from the last checkpoint for this path
username string SMB username
password string SMB password
domain string SMB domain
smbPort int 445 SMB port (use when the server listens on a non-standard port)

GET /api/findings — query parameters

Parameter Default Description
scan_id Required. Timestamp ID of the scan (e.g. 20240101_120000)
page 0 0-based page number
per_page 100 Rows per page (1–500)
risk ALL Filter by risk: HIGH, MEDIUM, LOW, or ALL
search Substring filter on filename or full path

Test server

A Samba container pre-loaded with intentionally dirty files is included so you can verify LeakLens works without touching real infrastructure. Requires Docker or Podman.

Start it:

# Linux / macOS
./testserver/start-testserver.sh

# Windows
testserver\start-testserver.bat

The container runs Samba on port 4445. No mounting is needed — LeakLens connects directly over SMB.

Scan it with the UI:

  1. Click ⬡ SMB: Browse Shares & Credentials in the Scan Configuration panel
  2. Enter 127.0.0.1:4445 in the Server / Host field
  3. Leave Username and Password blank (the share allows guest access)
  4. Click ⬡ Discover Sharestestshare appears in the list
  5. Click testshare to select it — the Path field is populated automatically
  6. Click Start Scan

Scan it from the CLI:

python3 leaklens.py scan --path "\\\\127.0.0.1\\testshare" --smb-port 4445

Stop it:

./testserver/stop-testserver.sh   # Linux / macOS
testserver\stop-testserver.bat    # Windows

See USAGE.md for the complete test server walkthrough.

Expected findings across the 8 test files:

File Patterns triggered
deploy.ps1 Plaintext Password, Connection String, PowerShell SecureString, Hardcoded PSCredential
app.config Plaintext Password, Generic API Key/Token
passwords.txt NTLM Hash
.env AWS Access Key, Plaintext Password, Base64 Credential, Stripe API Key
nightly-backup.bat Net Use Credential
db_maintenance.py Plaintext Password, Bearer Token
id_rsa Private Key Header
project-notes.md (clean — no findings)

Running the tests

Install dev dependencies, then run pytest:

pip install -r requirements-dev.txt
python -m pytest tests/ -v

149 tests across three modules: pattern regexes, engine helpers, and API endpoints. See USAGE.md for a full walkthrough including the live test server.


Project structure

LeakLens/
├── leaklens.py                 # Entry point — Flask server + all API routes
├── requirements.txt            # Pinned runtime dependencies
├── requirements-dev.txt        # Adds pytest for development
├── start.bat / start.sh        # Launchers
├── scanner/
│   ├── engine.py               # Scan orchestrator — walk, workers, SQLite, SSE
│   ├── content.py              # Detection helpers, scan_content(), build_finding()
│   ├── patterns.py             # 25 detection rules (pre-compiled)
│   ├── smb.py                  # smbprotocol helpers
│   ├── suppress.py             # .leaklensignore parser
│   └── checkpoint.py           # Resume checkpoint helpers
├── frontend/
│   ├── index.html              # Browser UI shell
│   └── Assets/
│       ├── app.js              # SSE client, virtual scroll, filters
│       └── styles.css
├── tests/
│   ├── test_patterns.py        # Positive/negative tests for all 25 patterns
│   ├── test_engine.py          # scan_content, build_finding, suppression helpers
│   └── test_api.py             # Flask endpoint tests
├── reports/                    # Auto-created: SQLite + JSON per scan
└── testserver/                 # Samba container for testing

Architecture

Share enumeration

Triggered by clicking Discover Shares in the UI (POST /api/shares). Three methods are tried in order — the first one that succeeds wins:

POST /api/shares  {host, username, password, domain}
        │
        ▼
  list_shares()   [scanner/smb.py]
        │
        ├─ 1. impacket  (if installed — all platforms)
        │        DCE/RPC over SMB named pipe (\srvsvc)
        │        NetShareEnum Level 1
        │        ↳ Fastest, pure Python, supports non-standard ports
        │
        ├─ 2. net use + net view  (Windows built-in — no extra packages)
        │
        │    With credentials:
        │        net use \\server\IPC$ <password> /user:<domain\username>
        │        net view \\server /all
        │        net use \\server\IPC$ /delete /yes   ← always cleaned up
        │
        │    Without credentials (anonymous / current session):
        │        net view \\server /all
        │
        └─ 3. smbclient binary  (Linux / macOS)
                 smbclient -L //server -U user%pass
                 ↳ Installed via: apt install smbclient / brew install samba

        ↓
  [{name: "share1"}, {name: "share2"}, ...]  → returned to browser

Key design point: steps 2 and 3 separate authentication from enumeration — Windows has no single built-in command that does both. net use establishes an authenticated SMB session to IPC$ (the inter-process communication share used by Windows for RPC), which makes net view run under those credentials. The session is deleted immediately after enumeration regardless of success or failure.


Scanning

Triggered by clicking Start Scan (POST /api/scan). The response body is an SSE stream — findings appear in the browser as they are found, not after the scan completes.

POST /api/scan  {scanPath, workers, maxFileSizeMB, resume, ...}
        │
        ▼
  scan_path()   [scanner/engine.py]
        │
        ├─ Walk thread  (1 thread)
        │    Local path:  os.scandir() — recursive directory walk
        │    SMB path:    smbclient.scandir() via walk_smb()
        │                 ↳ Credit-limited semaphore (max 4 concurrent SMB ops)
        │    ↓
        │    Bounded file queue  (maxsize = workers × 8)
        │    ↳ Back-pressures the walk if workers are busy
        │
        ├─ Worker threads  (N threads, default 8, max 16)
        │    Pull file paths from the queue
        │    │
        │    ├─ Skip:    file too large / excluded extension / lockfile
        │    ├─ Read:    open() for local / smbclient.open_file() for SMB
        │    ├─ Match:   re.finditer() against 25 pre-compiled patterns
        │    ├─ Score:   confidence 1–10
        │    │           ↳ directory penalty (docs/, examples/, test/ → −3)
        │    │           ↳ hash-only findings capped at LOW
        │    ├─ Suppress: .leaklensignore rules checked per file + pattern
        │    └─ Emit:   finding event → bounded event queue
        │
        ├─ SSE generator  (Flask main thread)
        │    Pulls events from the event queue
        │    Writes findings to SQLite  (batched every 50 rows, WAL mode)
        │    Yields  "data: {...}\n\n"  to the HTTP response
        │    ↳ Checkpoint written every 500 files (enables resume)
        │
        └─ Browser  (frontend/Assets/app.js)
             fetch() + ReadableStream  (not EventSource — scan is a POST)
             Decodes SSE lines, pushes findings into filteredFindings[]
             Virtual-scrolled table — only visible rows rendered (rAF-throttled)
             Live progress bar shows files/second rate

Resume: when resume: true, a checkpoint file maps each file's SHA-256 to its result. Already-processed files are skipped on re-scan. The SQLite database is opened in append mode against the original scan row.


Data flow summary

Browser  ──POST /api/scan──►  Flask SSE stream
                                     │
                              Walk thread ──► file queue ──► Worker threads
                                                                    │
                                                             Event queue
                                                                    │
                              SSE generator ◄────────────────────────
                                     │
                              SQLite DB (reports/LeakLens_<id>.db)
                                     │
                              Browser ◄── paginated /api/findings (historical)

Images

LeakLens Evidence LeakLens Reports


Legal

Run LeakLens only on systems and file shares you are authorized to access. Scanning reads file contents — ensure you have the appropriate permissions before use.

LeakLens is open source and community-driven. Bug reports, feature requests, and PRs are welcome. Especially new detection patterns and test cases.

About

Native SMB/UNC credential scanner for file shares with live UI, confidence scoring, and resume support.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors