Find exposed files containing credentials on web services.
Basic scan:
python3 exposure-scanner.py https://test.comFollow redirects and re-classify — this is the mode you want, because your box returns 301 on HTTP→HTTPS and you never see the real status until you follow:
python3 exposure-scanner.py https://test.com --follow-redirectsSlow it down (be polite to your own box and any WAF):
python3 exposure-scanner.py https://test.com --delay 0.25 --workers 4Save results, compare next week:
# First run
python3 exposure-scanner.py https://test.com --json-out scan-2026-09-11.json
# Next week
python3 exposure-scanner.py https://test.com --json-out scan-2026-09-18.json --diff scan-2026-09-11.jsonOnly show what matters:
python3 exposure-scanner.py https://test.com --only-criticalShow everything including 404s (useful for verifying the scanner is actually hitting paths):
python3 exposure-scanner.py https://test.com --show-info| Category | Examples |
|---|---|
| .env variants | All the ones your adversary used, plus ~50 more (.env.php-bak, .env-smtp.log, .env-sendgrid, etc.) |
| Cloud creds | credentials.json, service-account.json, gcp-key.json, firebase-adminsdk.json, AWS/Azure/GCP configs |
| SSH/TLS keys | id_rsa, .pem, .key, keystore.jks, authorized_keys |
| VCS metadata | .git/config, .git/HEAD, .git-credentials, .svn/entries |
| CI/CD configs | .github/workflows/*.yml, .gitlab-ci.yml, Jenkinsfile, cloudbuild.yaml, buildspec.yml |
| Docker/K8s | docker-compose.yml, Dockerfile, k8s.yml, Helm values |
| IaC | terraform.tfstate, terraform.tfvars, Ansible vaults, Pulumi |
| AI/LLM configs | .claude/settings.json, .cursor/mcp.json, .continue/config.json, .openai/api_key, claude_desktop_config.json, mcp.json |
| Backups | backup.sql, www.zip, dump.sql.gz, .tar.gz variants |
| WordPress | wp-config.php.bak, wp-admin/install.php, xmlrpc.php, wp-content/debug.log |
| PHP probes | phpinfo.php, test.php, adminer.php, i.php, pi.php |
| Editor swaps | index.php~, config.php.swp, .DS_Store |
| Package files | package.json, composer.lock, yarn.lock, requirements.txt |
| Logs | debug.log, laravel.log, storage/logs/laravel.log, access.log |
Combined with ~200 directory prefixes (backend/, .git/, .claude/, staging/, api/, etc.), that's roughly 60,000+ paths to test.
- CRITICAL — A known-sensitive file returned 200 (e.g.,
.env,id_rsa,credentials.json,wp-config.php.bak). This is the "rotate everything now" bucket. - MEDIUM — 401 (auth challenge on a sensitive path), 500-series errors on a probe, a directory listing (200 on a path ending in
/), or an unclassified 200. Worth manually inspecting. - LOW — 403 (file exists but is blocked — this is actually good, but confirms the path), 405, 204, or an informational file (
README,package.json) returning 200. - INFO — 404, 301/302/307/308 redirects, "not found" results.
- ERROR — Connection failures, timeouts, unexpected exceptions.
- Authorization: Only run this against your own infrastructure. It's aggressive by nature. Running it against a host you don't own is unauthorized access in most jurisdictions.
- Redirects: The scanner does not follow redirects by default — because on your box, most
.envhits were 301s that turned out to be HTTP→HTTPS. Run with--follow-redirectsto get the real answer, but be aware this doubles the number of requests. - Download limits: It reads at most 4 KB per response — so a 200 on
/backup.sqlwon't download your entire database. It just confirms the resource is served. - SSL Verification:
verify=False— the scanner accepts self-signed certs. Since you're scanning your own box, this is fine and avoids failures if a vhost has a cert issue. If you want strict verification, changeverify=Falsetoverify=Truein the probe method. - Rate limiting: Rate limiting is per-worker — with
--workers 8 --delay 0.1, you'll do about 80 requests/second. That's aggressive for a small VPS. Start with--workers 4 --delay 0.5and see how the box handles it. - Customization: Extend the wordlist easily —
BASE_FILESandAPP_DIRSare plain Python lists. Add anything you want. The combinator is base × dirs, so 1 new base file × 200 dirs = 200 new probes.
Run the scan and log it:
cd /root/scans
python3 exposure-scanner.py https://test.com \
--follow-redirects \
--delay 0.2 --workers 4 \
--json-out "scan-$(date +%F).json" \
--quiet --only-critical 2>&1 | tee "scan-$(date +%F).log"Then diff next week:
python3 exposure-scanner.py https://test.com \
--follow-redirects \
--json-out "scan-$(date +%F).json" \
--diff "$(ls -1t scan-*.json | head -2 | tail -1)"The --diff output will tell you exactly what changed since last week — new critical findings, resolved ones — so you can act on deltas rather than re-reading the whole report.