From 445579fceefb2e02b77f9ceef368b8d7c9af60f4 Mon Sep 17 00:00:00 2001 From: Brian Richter Date: Tue, 23 Jun 2026 17:17:17 -0700 Subject: [PATCH 1/6] feat: add stdlib-only local recon dashboard Tactical-HUD localhost console (no third-party deps) that wraps the bundled helpers behind a browser UI: - Secret Scan tab: recursive path scan via secret_scan.py with severity/category aggregation, live filtering, JSON/CSV export - Paste & Scan tab: fully offline blob scan against the 48-pattern catalog - HackerOne Ref tab: disclosed-report lookups via h1_reference.py (the only tab that touches the network) Binds to 127.0.0.1 by default; non-loopback --host prints a warning. Reuses secret_scan.py directly (no pattern drift). Documents usage in docs/usage.md. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/usage.md | 26 + skills/offensive-osint/scripts/dashboard.py | 827 ++++++++++++++++++++ 2 files changed, 853 insertions(+) create mode 100644 skills/offensive-osint/scripts/dashboard.py diff --git a/docs/usage.md b/docs/usage.md index abeaa40..7627c50 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -166,3 +166,29 @@ See [`../examples/`](../examples/) for end-to-end walkthroughs: - `02-bug-bounty-workflow.md` — full HackerOne engagement - `03-identity-fabric-mapping.md` — M365 deep enum - `04-secret-hunting.md` — leaked-credential workflow + +## Local dashboard (optional) + +A zero-dependency (stdlib-only) localhost web UI ships alongside the skill at +[`../skills/offensive-osint/scripts/dashboard.py`](../skills/offensive-osint/scripts/dashboard.py). +It wraps the bundled helpers in a browser console — no Flask, no pip install. + +```bash +python3 skills/offensive-osint/scripts/dashboard.py # http://127.0.0.1:8765 +python3 skills/offensive-osint/scripts/dashboard.py --open # also open a browser tab +python3 skills/offensive-osint/scripts/dashboard.py --port 9000 +``` + +Three tabs: + +- **Secret Scan** — point it at a repo/file path; runs `secret_scan.py` recursively + (binaries, `.git`, `node_modules` skipped) and renders findings with severity/category + aggregation, live filtering, and JSON/CSV export. +- **Paste & Scan** — paste JS, configs, env dumps, or response bodies; scanned fully + **offline** against the 48-pattern catalog. +- **HackerOne Ref** — queries the public disclosed-report corpus via `h1_reference.py` + (this tab is the only one that makes outbound HTTPS requests). + +**Safety:** binds to `127.0.0.1` only by default. The scan API reads local files the +operator points it at, so binding to a non-loopback address prints a warning — only do +that on a trusted, isolated host. diff --git a/skills/offensive-osint/scripts/dashboard.py b/skills/offensive-osint/scripts/dashboard.py new file mode 100644 index 0000000..f7c24dc --- /dev/null +++ b/skills/offensive-osint/scripts/dashboard.py @@ -0,0 +1,827 @@ +#!/usr/bin/env python3 +"""dashboard.py — local recon console for claude-osint. + +A zero-dependency (stdlib-only) localhost web UI that wraps the bundled +recon helpers: + + * secret_scan.py — scan a filesystem path or pasted blob for 48 secret + patterns, with severity/category aggregation. + * h1_reference.py — query HackerOne's public disclosed-report corpus. + +Design goals mirror the rest of the repo: no third-party packages, runs on +any box with python3, and binds to 127.0.0.1 by default so the scan surface +(which can read arbitrary local files the operator points it at) is never +exposed to the network. + +Usage: + python3 dashboard.py # http://127.0.0.1:8765 + python3 dashboard.py --port 9000 + python3 dashboard.py --open # also open a browser tab + python3 dashboard.py --host 0.0.0.0 # EXPOSE to LAN (prints a warning) + +Exit codes: + 0 — clean shutdown (Ctrl-C) + 2 — invalid arguments / bind failure +""" +from __future__ import annotations + +import argparse +import json +import os +import subprocess +import sys +import threading +import webbrowser +from collections import Counter +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer + +# Make the bundled helpers importable regardless of CWD. +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +if SCRIPT_DIR not in sys.path: + sys.path.insert(0, SCRIPT_DIR) + +try: + from secret_scan import scan_path, scan_text # noqa: E402 +except ImportError as exc: # pragma: no cover - defensive + print(f"[!] Could not import secret_scan.py from {SCRIPT_DIR}: {exc}", file=sys.stderr) + sys.exit(2) + +H1_SCRIPT = os.path.join(SCRIPT_DIR, "h1_reference.py") + +# Cap returned findings so a huge tree can't produce a multi-MB JSON payload. +MAX_FINDINGS = 5000 +# Hard wall-clock cap for the HackerOne subprocess. +H1_TIMEOUT_SEC = 60 + +SEVERITY_ORDER = {"critical": 0, "high": 1, "medium": 2, "low": 3} + + +# -------------------------------------------------------------------------- +# Core logic (kept separate from HTTP plumbing so it stays testable) +# -------------------------------------------------------------------------- + +def _summarize(findings: list[dict]) -> dict: + """Aggregate a flat findings list into dashboard summary counters.""" + by_sev = Counter(f["severity"] for f in findings) + by_cat = Counter(f["category"] for f in findings) + files = {f["source"] for f in findings} + return { + "total": len(findings), + "by_severity": dict(by_sev), + "by_category": dict(by_cat), + "files": len(files), + } + + +def run_scan(target: str) -> dict: + """Scan a filesystem path (file or directory).""" + target = os.path.expanduser(target.strip()) + if not target: + return {"ok": False, "error": "Empty path."} + if not os.path.exists(target): + return {"ok": False, "error": f"Path does not exist: {target}"} + + findings: list[dict] = [] + truncated = False + for hit in scan_path(target): + findings.append(hit) + if len(findings) >= MAX_FINDINGS: + truncated = True + break + + findings.sort(key=lambda f: (SEVERITY_ORDER.get(f["severity"], 9), f["source"], f["line"])) + return { + "ok": True, + "target": target, + "truncated": truncated, + "summary": _summarize(findings), + "findings": findings, + } + + +def run_scan_text(blob: str) -> dict: + """Scan a pasted text blob.""" + if not blob.strip(): + return {"ok": False, "error": "Empty input."} + findings = list(scan_text(blob, source="")) + findings.sort(key=lambda f: (SEVERITY_ORDER.get(f["severity"], 9), f["line"])) + return { + "ok": True, + "target": "", + "truncated": False, + "summary": _summarize(findings), + "findings": findings, + } + + +def run_h1(params: dict) -> dict: + """Shell out to h1_reference.py --json and parse the array it prints.""" + if not os.path.exists(H1_SCRIPT): + return {"ok": False, "error": "h1_reference.py not found alongside dashboard."} + + mode = params.get("mode", "top-voted") + cmd = [sys.executable, H1_SCRIPT, "--json"] + if mode == "top-voted": + cmd.append("--top-voted") + elif mode == "top-bounty": + cmd.append("--top-bounty") + + query = (params.get("query") or "").strip() + if query: + cmd += ["--query", query] + program = (params.get("program") or "").strip() + if program: + cmd += ["--program", program] + + try: + pages = max(1, min(int(params.get("pages", 3)), 20)) + except (TypeError, ValueError): + pages = 3 + try: + limit = max(1, min(int(params.get("limit", 25)), 50)) + except (TypeError, ValueError): + limit = 25 + cmd += ["--pages", str(pages), "--limit", str(limit)] + + severities = params.get("severities") or [] + if isinstance(severities, list) and severities: + cmd += ["--severity", *[str(s) for s in severities]] + + try: + proc = subprocess.run( + cmd, capture_output=True, text=True, timeout=H1_TIMEOUT_SEC, + ) + except subprocess.TimeoutExpired: + return {"ok": False, "error": f"HackerOne query timed out after {H1_TIMEOUT_SEC}s."} + except OSError as exc: + return {"ok": False, "error": f"Failed to launch h1_reference.py: {exc}"} + + if proc.returncode != 0: + err = (proc.stderr or proc.stdout or "unknown error").strip() + return {"ok": False, "error": err[:1000]} + + try: + results = json.loads(proc.stdout or "[]") + except json.JSONDecodeError: + return {"ok": False, "error": "Could not parse h1_reference.py output as JSON."} + + return {"ok": True, "count": len(results), "results": results} + + +# -------------------------------------------------------------------------- +# HTTP layer +# -------------------------------------------------------------------------- + +class Handler(BaseHTTPRequestHandler): + server_version = "claude-osint-dashboard/1.0" + + # Silence default per-request logging; keep stderr clean for warnings. + def log_message(self, *args): # noqa: D401 + return + + def _send_json(self, obj: dict, status: int = 200) -> None: + body = json.dumps(obj).encode("utf-8") + self.send_response(status) + self.send_header("Content-Type", "application/json; charset=utf-8") + self.send_header("Content-Length", str(len(body))) + self.send_header("Cache-Control", "no-store") + self.end_headers() + self.wfile.write(body) + + def _send_html(self, html: str) -> None: + body = html.encode("utf-8") + self.send_response(200) + self.send_header("Content-Type", "text/html; charset=utf-8") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + self.wfile.write(body) + + def _read_json_body(self) -> dict: + length = int(self.headers.get("Content-Length", 0) or 0) + if length <= 0: + return {} + raw = self.rfile.read(length) + try: + return json.loads(raw or b"{}") + except json.JSONDecodeError: + return {} + + def do_GET(self): # noqa: N802 + if self.path in ("/", "/index.html"): + self._send_html(PAGE_HTML) + elif self.path == "/api/health": + self._send_json({"ok": True, "service": "claude-osint-dashboard"}) + else: + self._send_json({"ok": False, "error": "not found"}, status=404) + + def do_POST(self): # noqa: N802 + body = self._read_json_body() + try: + if self.path == "/api/scan": + self._send_json(run_scan(body.get("path", ""))) + elif self.path == "/api/scan-text": + self._send_json(run_scan_text(body.get("text", ""))) + elif self.path == "/api/h1": + self._send_json(run_h1(body)) + else: + self._send_json({"ok": False, "error": "not found"}, status=404) + except Exception as exc: # pragma: no cover - last-resort guard + self._send_json({"ok": False, "error": f"internal error: {exc}"}, status=500) + + +# -------------------------------------------------------------------------- +# Embedded single-page UI — tactical HUD command console. +# No external assets / no CDN — fully offline. +# -------------------------------------------------------------------------- + +PAGE_HTML = r""" + + + + +claude / osint · recon.live + + + +
+ + +
+
+
+
⌖ N 00°00'00" · SECTOR-A
+
+
# BUILD:STABLE · SHA:A7F3C91 · NO TELEMETRY
+
+
SYS://RECON.LIVE · 0XC0DE
+
+
+
+ + +
+
+ + + + + + + +
+
+

claude / osint

+
External Red-Team Recon · Bug-Bounty Arsenal
+
+
+ Operational + VERSION v2.1.1 · MIT + AUTHOR @elementalsouls + REPO github.com/elementalsouls/Claude-OSINT +
+
+ + +
+ Recon Arsenal + // 4 Domains  // 12 Surfaces  // 9 Validators + + 0X01  Load · Enum · Validate · Chain +
+ + +
+
+
[01]Identity
+
M365 / Entra
tenant GUID · SharePoint · OWA
ID-01
+
Anthropic · OpenAI
key catalog · live validators
ID-02
+
LinkedIn Enum
people · roles · job postings
ID-03
+
+
+
[02]Infrastructure
+
AWS
STS · IAM enum · S3 buckets
IN-01
+
Kubernetes
kubelet · etcd · K8s API
IN-02
+
Citrix · F5 · Fortinet
vendor fingerprint · KEV CVEs
IN-03
+
+
+
[03]Code & APIs
+
GitHub
PAT scope · code dorks · secrets
CO-01
+
GraphQL
introspection · field-suggestion
CO-02
+
Postman PMAK
workspace leak · collection scan
CO-03
+
+
+
[04]Intel & Comms
+
HudsonRock · Wayback
breach corpus · CDX archive
IT-01
+
Slack · Discord
webhook leaks · token scan
IT-02
+
Atlassian · DataDog
jira leaks · dashboard exposure
IT-03
+
+
+ + +
+
Kill Chain
+
01RECONpassive
+
+
02ENUMERATEsurface
+
+
03VALIDATElive
+
+
04CHAINexploit
+
+
05REPORTdeliver
+
+ + +
+
90+
Modules
+
48
Patterns
+
80+
Dorks
+
9
Validators
+
27
Attack Paths
+
5,500+
Lines
+
+ + +
+ ▸ Methodology · Arsenal + + Paired · Production-Ready · Offensive · Documented + + Ship It ◂ +
+ + +
+ Live Console + // interactive · stdlib · localhost + + 0X02  Scan · Paste · H1-Ref +
+ +
+ +
+ +
+
+ + +
+
+
+ +
+ +
+
+
+ +
+
+ + + + + +
+
↑ This tab makes outbound HTTPS requests to hackerone.com. The two scan tabs are fully offline.
+
+
+ +
+
+ +
+ + + + +""" + + +# -------------------------------------------------------------------------- +# Entry point +# -------------------------------------------------------------------------- + +def main() -> int: + ap = argparse.ArgumentParser(description="Local recon console for claude-osint.") + ap.add_argument("--host", default="127.0.0.1", help="Bind address (default: 127.0.0.1).") + ap.add_argument("--port", type=int, default=8765, help="Bind port (default: 8765).") + ap.add_argument("--open", action="store_true", help="Open a browser tab on startup.") + args = ap.parse_args() + + if args.host not in ("127.0.0.1", "localhost", "::1"): + print( + f"[!] WARNING: binding to {args.host} exposes a file-reading scan API to the " + "network. Only do this on a trusted, isolated host.", + file=sys.stderr, + ) + + try: + httpd = ThreadingHTTPServer((args.host, args.port), Handler) + except OSError as exc: + print(f"[!] Could not bind {args.host}:{args.port} — {exc}", file=sys.stderr) + return 2 + + url = f"http://{args.host}:{args.port}" + print(f"==> claude-osint recon console: {url}") + print(" Ctrl-C to stop.") + if args.open: + threading.Timer(0.6, lambda: webbrowser.open(url)).start() + + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\n==> shutting down.") + finally: + httpd.server_close() + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From f2a798aa6f4d25882ae945027f1bae261af35eb0 Mon Sep 17 00:00:00 2001 From: Brian Richter Date: Tue, 23 Jun 2026 20:36:26 -0700 Subject: [PATCH 2/6] feat: UX restructure, mobile, tactical motion, display font for dashboard Live design-review pass on the recon console: - Task-first IA: console (Run a Scan) promoted above the fold with a hero CTA; arsenal/kill-chain/stats demoted to a labeled "Capability Map" reference with false-clickable hover removed. - Interaction polish: designed hover/focus-visible states; reduced-motion honored across pulses, scanline, boot reveal, transitions. - Tactical motion: scanline sweep, live uptime ticker, staggered boot reveal. - Mobile: sub-560px breakpoint (1-col arsenal, stacked scan row, wrapped tabs, hidden status segments, fluid type, overflow-x safety). - Back-to-scan affordance: floating button reveals on scroll past the console. - Display font: bundled Archivo Black (OFL-1.1), served locally at /font/display.woff2 with Arial Black/system fallback. No runtime CDN. Backend unchanged; scan/paste/h1 endpoints reverified. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../scripts/assets/FONT-LICENSE.txt | 13 + .../scripts/assets/archivo-black.woff2 | Bin 0 -> 14332 bytes skills/offensive-osint/scripts/dashboard.py | 261 ++++++++++++++---- 3 files changed, 214 insertions(+), 60 deletions(-) create mode 100644 skills/offensive-osint/scripts/assets/FONT-LICENSE.txt create mode 100644 skills/offensive-osint/scripts/assets/archivo-black.woff2 diff --git a/skills/offensive-osint/scripts/assets/FONT-LICENSE.txt b/skills/offensive-osint/scripts/assets/FONT-LICENSE.txt new file mode 100644 index 0000000..cffc602 --- /dev/null +++ b/skills/offensive-osint/scripts/assets/FONT-LICENSE.txt @@ -0,0 +1,13 @@ +archivo-black.woff2 +==================== + +Font: Archivo Black +Designer: Omnibus-Type +License: SIL Open Font License, Version 1.1 (OFL-1.1) +Source: https://fonts.google.com/specimen/Archivo+Black + https://github.com/Omnibus-Type/Archivo + +This font is bundled and served locally by dashboard.py (route +/font/display.woff2) so the recon console has no runtime CDN dependency. +The OFL permits bundling and redistribution; this file preserves the +required attribution. Full license text: https://openfontlicense.org/ diff --git a/skills/offensive-osint/scripts/assets/archivo-black.woff2 b/skills/offensive-osint/scripts/assets/archivo-black.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..85cf1745ce2eec0f34648881d448de91d490abbb GIT binary patch literal 14332 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 None: self.end_headers() self.wfile.write(body) + def _send_static(self, path: str, content_type: str) -> None: + try: + with open(path, "rb") as fh: + body = fh.read() + except OSError: + self._send_json({"ok": False, "error": "not found"}, status=404) + return + self.send_response(200) + self.send_header("Content-Type", content_type) + self.send_header("Content-Length", str(len(body))) + self.send_header("Cache-Control", "max-age=86400") + self.end_headers() + self.wfile.write(body) + def _read_json_body(self) -> dict: length = int(self.headers.get("Content-Length", 0) or 0) if length <= 0: @@ -211,6 +225,8 @@ def do_GET(self): # noqa: N802 self._send_html(PAGE_HTML) elif self.path == "/api/health": self._send_json({"ok": True, "service": "claude-osint-dashboard"}) + elif self.path == "/font/display.woff2": + self._send_static(os.path.join(SCRIPT_DIR, "assets", "archivo-black.woff2"), "font/woff2") else: self._send_json({"ok": False, "error": "not found"}, status=404) @@ -241,6 +257,13 @@ def do_POST(self): # noqa: N802 claude / osint · recon.live +
@@ -475,7 +570,7 @@ def do_POST(self): # noqa: N802
# BUILD:STABLE · SHA:A7F3C91 · NO TELEMETRY
-
SYS://RECON.LIVE · 0XC0DE
+
SYS://RECON.LIVE · UP 00:00:00
+
@@ -493,6 +588,7 @@ def do_POST(self): # noqa: N802

claude / osint

External Red-Team Recon · Bug-Bounty Arsenal
+
Operational @@ -502,12 +598,65 @@ def do_POST(self): # noqa: N802
- + +
+ Run a Scan + // point it at a path, or paste a blob — runs locally, nothing leaves this box + + 0X01  Scan · Paste · H1-Ref +
+ +
+ +
+ +
+

Scan a local repo or file for leaked secrets — 48 patterns, recursive, offline.

+
+ + +
+
+
+ +
+

Paste any text — JS, configs, env dumps, response bodies. Checked locally against the same 48 patterns.

+ +
+
+
+ +
+

Look up disclosed HackerOne reports for techniques and impact framing. This tab reaches the network.

+
+ + + + + +
+
↑ This tab makes outbound HTTPS requests to hackerone.com. The two scan tabs are fully offline.
+
+
+ +
+
+ +
- Recon Arsenal - // 4 Domains  // 12 Surfaces  // 9 Validators + Capability Map + // reference — what these skills cover · not interactive - 0X01  Load · Enum · Validate · Chain + 0X02  4 Domains · 12 Surfaces · 9 Validators
@@ -571,57 +720,8 @@ def do_POST(self): # noqa: N802 Ship It ◂ - -
- Live Console - // interactive · stdlib · localhost - - 0X02  Scan · Paste · H1-Ref -
- -
- -
- -
-
- - -
-
-
- -
- -
-
-
- -
-
- - - - - -
-
↑ This tab makes outbound HTTPS requests to hackerone.com. The two scan tabs are fully offline.
-
-
- -
-
- + From d89a38999874730e024bf871040f76644e15ba94 Mon Sep 17 00:00:00 2001 From: Brian Richter Date: Thu, 25 Jun 2026 08:02:46 -0700 Subject: [PATCH 3/6] feat(sync): mirror recon skills from Claude-BugHunter (re-export model) Claude-BugHunter is now the canonical monorepo home for all skills; this repo re-exports the two recon skills (offensive-osint, osint-methodology). - add scripts/sync-from-bughunter.sh (sync + --check drift guard) - add .github/workflows/sync-check.yml (CI fails on drift; PR + weekly) - README: declare the mirror relationship; drop the dead sync-skill-content.sh step Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/sync-check.yml | 33 +++++++++++++ README.md | 4 +- scripts/sync-from-bughunter.sh | 81 ++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/sync-check.yml create mode 100644 scripts/sync-from-bughunter.sh diff --git a/.github/workflows/sync-check.yml b/.github/workflows/sync-check.yml new file mode 100644 index 0000000..369379b --- /dev/null +++ b/.github/workflows/sync-check.yml @@ -0,0 +1,33 @@ +name: Skill sync check + +# Claude-BugHunter is the canonical home for the two recon skills this repo +# re-exports (offensive-osint, osint-methodology). This guard fails if our copies +# drift from BugHunter's — either because someone edited them here (edit them in +# BugHunter instead) or because BugHunter advanced and we haven't re-synced. + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + schedule: + - cron: '17 6 * * 1' # weekly Monday — catch upstream drift even without a PR + workflow_dispatch: + +jobs: + drift: + name: Mirrored skills match Claude-BugHunter + runs-on: ubuntu-latest + steps: + - name: Checkout claude-osint + uses: actions/checkout@v4 + + - name: Checkout Claude-BugHunter (canonical source) + uses: actions/checkout@v4 + with: + repository: elementalsouls/Claude-BugHunter + ref: main # pin to a release tag / SHA for reproducible releases + path: _bughunter + + - name: Verify the 2 mirrored skills are in sync + run: bash scripts/sync-from-bughunter.sh --from "$GITHUB_WORKSPACE/_bughunter" --check diff --git a/README.md b/README.md index bb18e09..31a1315 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,8 @@ claude-osint/ Each skill directory is self-contained. Drop into `~/.claude/skills/` and Claude auto-triggers on relevant phrases. +> **Mirrored from [Claude-BugHunter](https://github.com/elementalsouls/Claude-BugHunter).** That repo is the canonical monorepo home for all skills; this repo re-exports the two recon skills. **Edit them in BugHunter, not here** — maintainers re-sync with `scripts/sync-from-bughunter.sh`, and a CI guard (`.github/workflows/sync-check.yml`) fails on drift. + --- ## Skill Index @@ -285,8 +287,6 @@ flowchart TD # Install both skills (one-time, after clone) git clone https://github.com/elementalsouls/Claude-OSINT.git cd Claude-OSINT -chmod +x ./scripts/sync-skill-content.sh -./scripts/sync-skill-content.sh mkdir -p ~/.claude/skills cp -r skills/osint-methodology ~/.claude/skills/ cp -r skills/offensive-osint ~/.claude/skills/ diff --git a/scripts/sync-from-bughunter.sh b/scripts/sync-from-bughunter.sh new file mode 100644 index 0000000..8680a2e --- /dev/null +++ b/scripts/sync-from-bughunter.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# sync-from-bughunter.sh — mirror the shared recon skills FROM Claude-BugHunter. +# +# Claude-BugHunter is the canonical monorepo home for ALL skills. Claude-OSINT +# re-exports two of them (offensive-osint, osint-methodology). This script copies +# those two skill directories from a BugHunter checkout into this repo so the two +# copies never drift. EDIT THE SKILLS IN BUGHUNTER, NOT HERE. +# +# Usage: +# ./scripts/sync-from-bughunter.sh --from /path/to/Claude-BugHunter # sync (overwrites local copies) +# ./scripts/sync-from-bughunter.sh --from /path/to/Claude-BugHunter --check # drift check only (CI; non-zero on drift) +# BUGHUNTER_DIR=/path/to/Claude-BugHunter ./scripts/sync-from-bughunter.sh # env-var form +# +# With no --from / $BUGHUNTER_DIR, a few common sibling locations are probed. +# --check is intended for CI, where .gitattributes guarantees LF on both sides. + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SKILLS=(offensive-osint osint-methodology) + +BUGHUNTER_DIR="${BUGHUNTER_DIR:-}" +CHECK_ONLY=false + +while [ $# -gt 0 ]; do + case "$1" in + --from) BUGHUNTER_DIR="${2:-}"; shift 2 ;; + --check|-c) CHECK_ONLY=true; shift ;; + --help|-h) grep '^#' "$0" | sed 's/^# \?//'; exit 0 ;; + *) echo "Unknown argument: $1 (try --help)" >&2; exit 2 ;; + esac +done + +# Probe common locations if not given explicitly. +if [ -z "$BUGHUNTER_DIR" ]; then + for c in \ + "$REPO_ROOT/Bug Hunter/Claude-BugHunter" \ + "$REPO_ROOT/../Claude-BugHunter" \ + "$HOME/security-research/Claude-BugHunter"; do + if [ -d "$c/skills" ]; then BUGHUNTER_DIR="$c"; break; fi + done +fi + +if [ -z "$BUGHUNTER_DIR" ] || [ ! -d "$BUGHUNTER_DIR/skills" ]; then + echo "✗ Claude-BugHunter repo not found. Pass --from or set BUGHUNTER_DIR." >&2 + exit 2 +fi + +echo "==> Source of truth: $BUGHUNTER_DIR" +drift=0 + +for s in "${SKILLS[@]}"; do + SRC="$BUGHUNTER_DIR/skills/$s" + DST="$REPO_ROOT/skills/$s" + if [ ! -d "$SRC" ]; then + echo " ✗ source missing: $SRC" >&2 + exit 2 + fi + if [ "$CHECK_ONLY" = true ]; then + if diff -rq "$SRC" "$DST" >/dev/null 2>&1; then + echo " ✓ $s: in sync" + else + echo " ✗ $s: DRIFT (run without --check to re-sync, or edit in BugHunter)" + drift=1 + fi + else + rm -rf "$DST" + mkdir -p "$DST" + cp -r "$SRC/." "$DST/" + find "$DST" -name __pycache__ -type d -prune -exec rm -rf {} + 2>/dev/null || true + echo " ✓ $s: synced from BugHunter" + fi +done + +if [ "$CHECK_ONLY" = true ]; then + [ "$drift" -eq 0 ] && echo "==> In sync with BugHunter." || echo "==> DRIFT detected." >&2 + exit "$drift" +fi + +echo "==> Done. The 2 recon skills are mirrored from BugHunter." +echo " Reminder: edit these skills in Claude-BugHunter, then re-run this script." From 5f3305bbffce61856ef6c70b18213328e0079107 Mon Sep 17 00:00:00 2001 From: Brian Richter Date: Thu, 25 Jun 2026 08:20:42 -0700 Subject: [PATCH 4/6] fix(docs): remove dead sync-skill-content.sh + guard workspace cruft (post-verification) Verification found the re-export migration left the dead sync-skill-content.sh and stale references after its docs/full-skills/ source was removed. - remove scripts/sync-skill-content.sh (silent no-op; sources gone) - docs/installation.md + SECURITY.md: drop the obsolete populate step and the outline-vs-full troubleshooting (skills ship full content directly) - .gitignore: guard the sibling Bug Hunter/ repo + stale self-nested skill dirs so git add -A can't sweep them in Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 6 +++ SECURITY.md | 2 +- docs/installation.md | 23 ++------- scripts/sync-skill-content.sh | 91 ----------------------------------- 4 files changed, 10 insertions(+), 112 deletions(-) delete mode 100644 scripts/sync-skill-content.sh diff --git a/.gitignore b/.gitignore index b809c33..9355d2f 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,9 @@ node_modules/ # Local sync artifacts *.local.md *.synced + +# Workspace layout: the sibling Claude-BugHunter repo lives here in this checkout. +# Never let `git add -A` sweep it (or stale self-nested skill dirs) into a commit. +/Bug Hunter/ +skills/offensive-osint/offensive-osint/ +skills/osint-methodology/osint-methodology/ diff --git a/SECURITY.md b/SECURITY.md index 579203d..d047ac4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -60,7 +60,7 @@ If you used these skills during an authorized engagement and found a vulnerabili ## Security best practices for users - Pin the skill version (`v2.1`) in any production deployment. -- Run `scripts/sync-skill-content.sh` (or manual cp) only against this repo's bundled `docs/full-skills/` files; don't fetch from arbitrary sources. +- Install skills only by copying this repo's bundled `skills/*/` directories; don't fetch skill content from arbitrary sources. - Verify SHA-256 of any binary helper scripts before execution. - Don't commit your engagement-specific notes into a fork of this repo. - Use sock-puppet GitHub accounts when contributing if your engagement persona shouldn't be linked to your contributor identity. diff --git a/docs/installation.md b/docs/installation.md index 146e4ea..569ac25 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -12,10 +12,8 @@ Claude Code looks for skills in `~/.claude/skills/` by default. git clone https://github.com/elementalsouls/Claude-OSINT.git cd Claude-OSINT -# Optional: populate full SKILL.md content from bundled full-skills (one-time after clone) -./scripts/sync-skill-content.sh - # Copy both skills into your local Claude Code skills directory +# (skills/*/SKILL.md ship with full content — no populate step needed) mkdir -p ~/.claude/skills cp -r skills/osint-methodology ~/.claude/skills/ cp -r skills/offensive-osint ~/.claude/skills/ @@ -29,12 +27,9 @@ mkdir -p ~/.claude/skills ln -sf ~/.local/share/Claude-OSINT/skills/osint-methodology ~/.claude/skills/osint-methodology ln -sf ~/.local/share/Claude-OSINT/skills/offensive-osint ~/.claude/skills/offensive-osint - -cd ~/.local/share/Claude-OSINT -./scripts/sync-skill-content.sh # one-time ``` -Then `git -C ~/.local/share/Claude-OSINT pull && ./scripts/sync-skill-content.sh` periodically. +Then `git -C ~/.local/share/Claude-OSINT pull` periodically to stay current. ### Verify install @@ -116,23 +111,11 @@ The skill's `triggers:` list controls auto-activation. If your prompt's wording - Try rephrasing with a phrase from the SKILL.md `triggers:` list. - If your phrasing is a common practitioner term, [open an issue](https://github.com/elementalsouls/Claude-OSINT/issues) to add it. -### "I get the structured-outline SKILL.md, not the full content" - -By default we ship structured-outline SKILL.md files (small, fast to load). To get full inline content: - -```bash -cd -./scripts/sync-skill-content.sh -``` - -This populates `skills/*/SKILL.md` with the full content from `docs/full-skills/*.SKILL.full.md`. - ### "Skill is too large for my model's context" Both skills together are ~5,500 lines / ~150 KB. This fits comfortably in modern Claude context windows (200K+). If you're using an older model with smaller context: -- Use the structured-outline SKILL.md files (don't run sync-skill-content.sh). -- Or attach only one skill at a time, depending on the task. +- Attach only one skill at a time, depending on the task. - Or run a model with larger context (Claude Sonnet 4.6+, Opus 4.6+). ### "I want to filter the skill content" diff --git a/scripts/sync-skill-content.sh b/scripts/sync-skill-content.sh deleted file mode 100644 index 76e9d83..0000000 --- a/scripts/sync-skill-content.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env bash -# sync-skill-content.sh — populate skills/*/SKILL.md with full v2.1 inline content -# from the bundled docs/full-skills/ canonical sources. -# -# Run this once after `git clone` to convert the structured-outline SKILL.md files -# into the full inline-content versions that Claude can load directly. -# -# Usage: -# ./scripts/sync-skill-content.sh # interactive, prompts before overwrite -# ./scripts/sync-skill-content.sh --force # non-interactive overwrite -# ./scripts/sync-skill-content.sh --check # verify checksums; no changes - -set -euo pipefail - -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" - -# Mapping: full-skills source → SKILL.md destination -SKILLS=( - "osint-methodology" - "offensive-osint" -) - -FORCE=false -CHECK_ONLY=false - -for arg in "$@"; do - case "$arg" in - --force|-f) FORCE=true ;; - --check|-c) CHECK_ONLY=true ;; - --help|-h) - grep '^#' "$0" | sed 's/^# \?//' - exit 0 - ;; - *) - echo "Unknown argument: $arg" >&2 - echo "Run with --help for usage." >&2 - exit 2 - ;; - esac -done - -echo "==> Syncing SKILL.md content from docs/full-skills/" - -for skill in "${SKILLS[@]}"; do - SRC="$REPO_ROOT/docs/full-skills/$skill.SKILL.full.md" - DST="$REPO_ROOT/skills/$skill/SKILL.md" - - if [ ! -f "$SRC" ]; then - echo " ⚠ Source missing: $SRC" - echo " (Skipping. The structured-outline SKILL.md will remain in place.)" - continue - fi - - if [ "$CHECK_ONLY" = true ]; then - SRC_HASH=$(sha256sum "$SRC" | awk '{print $1}') - if [ -f "$DST" ]; then - DST_HASH=$(sha256sum "$DST" | awk '{print $1}') - if [ "$SRC_HASH" = "$DST_HASH" ]; then - echo " ✓ $skill: in sync" - else - echo " ✗ $skill: DRIFT (run without --check to update)" - fi - else - echo " ✗ $skill: destination missing (run without --check to populate)" - fi - continue - fi - - if [ -f "$DST" ] && [ "$FORCE" = false ]; then - echo - echo " $skill: existing SKILL.md will be overwritten with full v2.1 content." - read -p " Continue? [y/N] " -n 1 -r REPLY - echo - if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then - echo " Skipping $skill." - continue - fi - fi - - cp "$SRC" "$DST" - LINES=$(wc -l < "$DST") - BYTES=$(wc -c < "$DST") - echo " ✓ $skill: $LINES lines, $BYTES bytes installed at $DST" -done - -if [ "$CHECK_ONLY" = false ]; then - echo - echo "==> Done. Skills are ready to load." - echo " Verify: head -5 skills/*/SKILL.md" - echo " Install: cp -r skills/* ~/.claude/skills/" -fi From c1b82c807677faa174b42e7287ab74f9d0df06aa Mon Sep 17 00:00:00 2001 From: Brian Richter Date: Thu, 25 Jun 2026 12:17:37 -0700 Subject: [PATCH 5/6] chore(sync): run first mirror from Claude-BugHunter (canonical) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First execution of the re-export — the two recon skills now mirror Claude-BugHunter exactly, so the sync-check CI guard goes green. - offensive-osint: 4,168-line monolith -> lean 398-line SKILL.md + 15 references/ files + dashboard.py / h1_reference.py / secret_scan.py; gains the 127-trigger frontmatter - osint-methodology: 455 -> 1,703 lines (BugHunter's expanded v2.3) - sync-from-bughunter.sh: --check now excludes __pycache__/*.pyc (no spurious local drift) - README: Structure + line counts updated (~6,000 lines); stale 4,168-line monolith layout removed Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 14 +- scripts/sync-from-bughunter.sh | 5 +- skills/offensive-osint/SKILL.md | 4027 +---------------- .../references/breach-and-credentials.md | 128 + .../offensive-osint/references/dork-corpus.md | 180 + .../references/helpers-and-automation.md | 231 + .../references/identity-fabric.md | 305 ++ .../references/people-osint.md | 105 + .../references/probes-and-wordlists.md | 1266 ++++++ .../offensive-osint/references/recon-stack.md | 282 ++ .../references/recon-techniques.md | 341 ++ .../references/saas-public-surfaces.md | 138 + .../references/secret-patterns.md | 67 + .../references/secret-validators.md | 329 ++ .../references/sector-notes.md | 72 + .../references/severity-matrix.md | 108 + .../references/specialized-osint.md | 199 + .../references/tooling-install.md | 188 + skills/osint-methodology/SKILL.md | 1696 ++++++- 19 files changed, 5529 insertions(+), 4152 deletions(-) create mode 100644 skills/offensive-osint/references/breach-and-credentials.md create mode 100644 skills/offensive-osint/references/dork-corpus.md create mode 100644 skills/offensive-osint/references/helpers-and-automation.md create mode 100644 skills/offensive-osint/references/identity-fabric.md create mode 100644 skills/offensive-osint/references/people-osint.md create mode 100644 skills/offensive-osint/references/probes-and-wordlists.md create mode 100644 skills/offensive-osint/references/recon-stack.md create mode 100644 skills/offensive-osint/references/recon-techniques.md create mode 100644 skills/offensive-osint/references/saas-public-surfaces.md create mode 100644 skills/offensive-osint/references/secret-patterns.md create mode 100644 skills/offensive-osint/references/secret-validators.md create mode 100644 skills/offensive-osint/references/sector-notes.md create mode 100644 skills/offensive-osint/references/severity-matrix.md create mode 100644 skills/offensive-osint/references/specialized-osint.md create mode 100644 skills/offensive-osint/references/tooling-install.md diff --git a/README.md b/README.md index 31a1315..9e24ce1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # claude-osint -> 2 paired Claude skills · **90+ recon modules** · 48 secret-regex patterns · 80+ dorks · 9 read-only credential validators · 27 attack-path templates · 4,600+ lines of structured tradecraft. Drop-in `SKILL.md` files that turn Claude into a god-mode external recon operator for authorized red-team and bug-bounty engagements. +> 2 paired Claude skills · **90+ recon modules** · 48 secret-regex patterns · 80+ dorks · 9 read-only credential validators · 27 attack-path templates · 6,000+ lines of structured tradecraft. Drop-in `SKILL.md` files that turn Claude into a god-mode external recon operator for authorized red-team and bug-bounty engagements. Built by **[ElementalSoul](https://github.com/elementalsouls)** — GenAI Security Research. @@ -28,7 +28,7 @@ Built by **[ElementalSoul](https://github.com/elementalsouls)** — GenAI Securi Drop both into your Claude environment and it behaves like a senior recon analyst: it knows the techniques, the tooling, the edge cases, and the escalation paths — and it stays in scope. -~4,600 lines of structured tradecraft · 96.9% PASS on a 32-prompt self-evaluation · ~85–90% practitioner coverage for the recon phase of authorized engagements. +~6,000 lines of structured tradecraft · 96.9% PASS on a 32-prompt self-evaluation · ~85–90% practitioner coverage for the recon phase of authorized engagements. --- @@ -37,11 +37,13 @@ Drop both into your Claude environment and it behaves like a senior recon analys ``` claude-osint/ ├── skills/ -│ ├── osint-methodology/SKILL.md # how to think (455 lines) +│ ├── osint-methodology/SKILL.md # how to think (~1,700 lines) │ └── offensive-osint/ -│ ├── SKILL.md # what to reach for (4,168 lines) -│ ├── scripts/secret_scan.py # stdlib-only secret scanner -│ └── scripts/h1_reference.py # HackerOne disclosed-reports reference agent +│ ├── SKILL.md # what to reach for (lean index, ~400 lines) +│ ├── references/ # 15 modular reference files (~3,900 lines) +│ ├── scripts/secret_scan.py # stdlib-only 48-pattern secret scanner +│ ├── scripts/h1_reference.py # HackerOne disclosed-reports reference agent +│ └── scripts/dashboard.py # local recon console (stdlib web UI) ├── docs/ # architecture · coverage · install · usage ├── examples/ # 4 end-to-end engagement walk-throughs ├── tests/smoke-test-prompts.md # 32-prompt self-evaluation diff --git a/scripts/sync-from-bughunter.sh b/scripts/sync-from-bughunter.sh index 8680a2e..10241c6 100644 --- a/scripts/sync-from-bughunter.sh +++ b/scripts/sync-from-bughunter.sh @@ -57,7 +57,10 @@ for s in "${SKILLS[@]}"; do exit 2 fi if [ "$CHECK_ONLY" = true ]; then - if diff -rq "$SRC" "$DST" >/dev/null 2>&1; then + # Exclude python caches: sync strips them from the destination, so a stray + # __pycache__ in the source would otherwise report spurious drift (locally; + # CI checkouts never have one). + if diff -rq --exclude=__pycache__ --exclude='*.pyc' "$SRC" "$DST" >/dev/null 2>&1; then echo " ✓ $s: in sync" else echo " ✗ $s: DRIFT (run without --check to re-sync, or edit in BugHunter)" diff --git a/skills/offensive-osint/SKILL.md b/skills/offensive-osint/SKILL.md index f6d7dbf..7ce6754 100644 --- a/skills/offensive-osint/SKILL.md +++ b/skills/offensive-osint/SKILL.md @@ -1,7 +1,7 @@ --- name: offensive-osint -description: "Operational arsenal for external red-team and bug-bounty reconnaissance. Concrete wordlists (28 Swagger paths, 13 GraphQL paths, 35 high-risk ports, 6 missing-header findings, 15 always-on HTTP checks, 5 SAML paths, cloud bucket permutations, JS guess-paths, vendor product fingerprints for Citrix/F5/Pulse/Fortinet/Cisco/PaloAlto/VMware/Exchange, cloud-native service fingerprints, container/K8s exposure paths, CI/CD platform paths, documentation/wiki leak paths, WHOIS/RDAP, DNS record catalog, Wayback CDX recipes), 43+-pattern secret-regex catalog (incl. modern AI API keys: Anthropic/OpenAI/HuggingFace/Cloudflare/DigitalOcean/npm/PyPI/Docker Hub/Atlassian/DataDog/Sentry/ngrok), 80+ dork corpus across 9 categories, GitHub code-search dorks, copy-paste curl/httpie probes for every check, post-discovery enumeration workflows (AWS/GitHub/Slack/JWT/PMAK/Anthropic/OpenAI), endpoint interest scoring rubric (0–100), mobile app ownership confidence, identity-fabric endpoints (Entra/Okta/ADFS/Google/SAML/M365 Teams+SharePoint+OneDrive+OAuth + user-enum), GraphQL field-suggestion enumeration when introspection disabled, 9 read-only secret validators (Postman/AWS/GitHub/Slack/Anthropic/OpenAI/npm/Atlassian/DataDog), Postman workspace search (verified endpoint), Stack Exchange sweep, public SaaS dorks, email security analysis (SPF/DMARC/DKIM/BIMI/MTA-STS/DNSSEC), origin-discovery / CDN bypass techniques, TLS deep audit (sslyze/testssl.sh/JA3/JA4), reverse-DNS sweep + IPv6 enum, vulnerability prioritization data sources (NVD/EPSS/CISA KEV/ExploitDB/Metasploit), 27 attack-path hint templates, 80+ severity-matrix examples, LinkedIn employee enumeration, job posting tech-stack analysis, Slack/Discord workspace discovery, package registry leak hunting (npm/PyPI/Docker Hub/Quay/GHCR), sat imagery for physical recon, tooling quick-install one-liners, sector-specific recon notes (healthcare/finance/ICS-SCADA/IoT/government), runnable stdlib-only secret_scan.py helper, plus the existing tool references for username/email/phone/people/social/breach/infrastructure/crypto/media/geospatial/AI/archiving/automation. Use when you need concrete probe paths, regexes, payloads, scoring rules, curl one-liners, and tool URLs for an authorized external recon engagement." -version: 2.1.1 +description: "Operational arsenal for authorized external red-team and bug-bounty recon. Concrete probes, wordlists, regexes, dorks, curl one-liners for: subdomain enum, GraphQL/Swagger/REST discovery, identity fabric (Entra/Okta/ADFS/Google/SAML/M365 deep — Teams/SharePoint/OneDrive), cloud bucket enum (S3/GCS/Azure), CDN/WAF bypass, origin discovery, vendor fingerprinting (Citrix/F5/Pulse/Fortinet/PaloAlto/Cisco/VMware), CI/CD exposure, 48-pattern secret-scan catalog (AWS/GCP/GitHub/Stripe/Slack/Anthropic/OpenAI/Atlassian/DataDog/npm/PyPI), Postman workspaces, breach correlation (HudsonRock/HIBP/DeHashed/IntelX), TLS/JA3 audit, certificate transparency, JS endpoint extraction, package registry leaks, mobile/APK recon, sat imagery, sector-specific recon (healthcare DICOM, finance SWIFT, ICS/SCADA Modbus/BACnet). Detail content in 15 modular reference files, loaded on demand. Use for any authorized recon: scoping, asset discovery, attack-path mapping, secret triage, severity scoring." +version: 3.0.0 triggers: - external recon - external red team @@ -134,7 +134,8 @@ triggers: # Offensive OSINT — External Red-Team Arsenal -> Companion skill: `osint-methodology` (the "how to think" skill). This skill is the "what to reach for." Use them together. +> **v3.0** — Refactored 2026-05-02 from a 4,168-line monolith into a lean SKILL.md (~400 lines) plus 15 modular reference files in `references/`. Detail content loads on demand — Claude reads only the reference files relevant to the current task. + ## 0. When to use / When NOT @@ -152,3166 +153,140 @@ triggers: ## 1. Authorization & Legal Posture -For assets the operator owns or has written authorization to assess. Soft scope check before acting against an unverified third-party target — see methodology skill §1 for the full posture. - ---- - -## 2. Confidence Levels - -- **TENTATIVE** — plausible based on indirect evidence (snippet-only dork match, single-source asset, inferred email pattern). -- **FIRM** — directly observed (subdomain resolves, HEAD-confirmed bucket exists, banner returned). -- **CONFIRMED** — verified via independent corroboration OR direct verification (live PMAK validation, multiple sources agree, listable bucket with object retrieval). - ---- - -## 3. Output Format Conventions - -Findings should carry: `id`, `module`, `asset_key`, `category`, `severity` (info/low/medium/high/critical), `confidence`, `title`, `description`, `evidence` (url + UTC timestamp + sha256 + raw ≤ 2 KiB), `references`, `remediation`. UTC timestamps everywhere. - ---- - -## 4. Source Hygiene & Citations - -URL + UTC timestamp + SHA-256 + tool version + run_id, every artifact. PNG screenshots, JSONL run logs, raw HTTP captures capped at 2 KiB body. - ---- - -## 5. Do NOT - -- Don't paste creds/PII/session tokens into cloud LLMs. -- Don't run destructive probes outside DEEP/`--aggressive`. -- Don't use validated credentials for anything except read-only liveness check. -- Don't single-source attribute. -- Don't assume vendor labels are ground truth. - ---- - -## 6. General OSINT (curated tool refs) - -- [OSINT Bookmarks](https://tools.myosint.training/) — comprehensive bookmarks. -- [OSINT Framework](https://osintframework.com/) — tool/resource directory. -- [IntelTechniques Tools](https://inteltechniques.com/tools/) — investigative suite. -- [Bellingcat Toolkit](https://www.bellingcat.com/resources/2024/09/24/bellingcat-online-investigations-toolkit/) — investigative journalism. -- [CyberSudo OSINT Toolkit](https://docs.google.com/spreadsheets/d/1EC0sKA_W9znzsxUt0wye9UYtyATXw5m8) — OSINT websites list. -- [Google Dorks](https://dorksearch.com/) — efficient Google searching. -- [Distributed Denial of Secrets](https://ddosecrets.com/) — leaked datasets. -- [Country-Specific Resources](https://digitaldigging.org/osint/) — country-targeted OSINT. - -## 7. Search Engines - -| Tool | Notes | -|------|-------| -| [Carrot2](https://search.carrot2.org/#/search/web) | Clusters results by topic | -| [etools](https://www.etools.ch/) | Metasearch | -| [Kagi](https://kagi.com/) | Privacy-first, non-personalized | -| [Brave Search](https://search.brave.com/) | Independent index; Goggles for custom ranking | -| [PDF Search](https://www.pdfsearch.io/) | PDF + table of contents | -| [Google Fact Check Explorer](https://toolbox.google.com/factcheck/explorer) | Cross-site fact-check | - ---- - -## 8. Username & Email Investigation - -| Tool | Purpose | -|------|---------| -| [Sherlock](https://github.com/sherlock-project/sherlock) | Username search across social networks | -| [Maigret](https://github.com/soxoj/maigret) | Profile collector by username | -| [What's My Name](https://whatsmyname.app/) | Username search | -| [Holehe](https://github.com/megadose/holehe) | Email registration check | -| [Epieos](https://epieos.com/) | Email pivots and metadata | -| [OSINT Industries](https://osint.industries/) | Email/username/phone lookups | -| [Hunter.io](https://hunter.io/) | Domain → emails | -| [EmailRep](https://emailrep.io/) | Email reputation | -| [Emailable](https://emailable.com/) | Email verification | -| [Mugetsu](https://mugetsu.io/) | X/Twitter username history | -| [RocketReach](https://rocketreach.co/) / [Apollo](https://www.apollo.io/) | Email enrichment + pattern guessing | -| [PhoneInfoga](https://github.com/sundowndev/phoneinfoga) | Phone number intelligence | - -Browser extensions: [GetProspect](https://chromewebstore.google.com/detail/email-finder-getprospect/bhbcbkonalnjkflmdkdodieehnmmeknp), [SignalHire](https://chrome.google.com/webstore/detail/signalhire-find-email-or/aeidadjdhppdffggfgjpanbafaedankd). - ---- - -## 9. People Search - -- [TruePeopleSearch](https://www.truepeoplesearch.com/) — free U.S. people search. -- [WhitePages](https://www.whitepages.com/), [Spokeo](https://www.spokeo.com/), [Webmii](https://webmii.com/), [Pipl](https://pipl.com/) (paid). -- [Clearbit](https://clearbit.com/) — company/individual data enrichment. -- [FaceCheck](https://facecheck.id/) / [FaceSeek](https://faceseek.online/) — reverse face search. - ---- - -## 10. Phone Number OSINT - -- [TrueCaller](https://www.truecaller.com/) — caller ID + spam blocking. -- [ThatsThem](https://thatsthem.com/) — reverse phone search. -- [Infobel](https://infobel.com/) — non-USA phone search. -- [FreeCarrierLookup](https://freecarrierlookup.com/) — carrier/type (US). -- [NumlookupAPI](https://numlookupapi.com/) [Freemium] — programmatic carrier checks. -- [CallerIDTest](https://calleridtest.com/), [Advanced Background Checks](https://www.advancedbackgroundchecks.com/). - ---- - -## 11. Email-Pattern Inference (TENTATIVE candidates) - -Given a `(first_name, last_name, domain)`, generate these 8 candidate addresses for breach pre-hits, phishing list curation, and downstream enrichment. Mark as **TENTATIVE** confidence until corroborated. - -``` -{first}.{last}@{domain} # john.doe@example.com -{first}{last}@{domain} # johndoe@example.com -{first}@{domain} # john@example.com -{first[0]}{last}@{domain} # jdoe@example.com -{first}.{last[0]}@{domain} # john.d@example.com -{last}@{domain} # doe@example.com -{first}_{last}@{domain} # john_doe@example.com -{first}-{last}@{domain} # john-doe@example.com -``` - -Lowercase before lookup. Strip diacritics for ASCII fallback. If the org uses a known pattern (e.g., Hunter.io shows `{first}.{last}` is dominant), prioritize that one and mark FIRM. - ---- - -## 12. Email-Harvest Source Stack - -Six parallel sources, dedup at the end: - -1. **IntelX phonebook API** — 2-step search + poll. Largest single source for breach-era addresses. -2. **Hunter.io** — domain-search endpoint. ~25 free/month. Returns verified emails + roles. -3. **crt.sh** — extract X.509 SAN extensions. Many certs include admin/contact emails. -4. **DuckDuckGo SERP scrape** — HTML scrape of `"@{target-domain}"` results. -5. **Bing SERP scrape** — same query, complementary index. -6. **Wayback CDX** — historic snapshots of the target's homepage / contact / about pages often contain emails removed from the live site. - -**Email regex:** -```regex -\b[A-Za-z0-9._%+\-]+@[A-Za-z0-9.\-]+\.[A-Za-z]{2,}\b -``` - -**Noise filter (reject numeric-only locals):** -```regex -^[0-9]+$ -``` -(Discards garbage like `12345@example.com` from random tokens.) - ---- - -## 13. Social Media - -| Platform | Tool | -|----------|------| -| Instagram | [Picuki](https://www.picuki.com/) — profile view without account | -| X/Twitter | [snscrape](https://github.com/snscrape/snscrape) — preferred CLI scraper; Twint as fallback | -| Facebook | [Graph Search](https://inteltechniques.com/tools/Facebook.html), [sowsearch.info](https://sowsearch.info/), [lookup-id.com](https://lookup-id.com/), [whopostedwhat.com](https://whopostedwhat.com/) | -| Facebook (research) | [Meta Content Library](https://transparency.meta.com/researcher) — CrowdTangle successor (researcher-gated) | -| YouTube/Twitch | [Social Blade](https://socialblade.com/) — analytics | -| TikTok | [Tokboard](https://tokboard.com/) — trends + profile analytics | -| Reddit | [Reveddit](https://www.reveddit.com/) — removed content; [RedTrack.social](https://redtrack.social/) — user history | -| Bluesky | [Firesky](https://firesky.tv/) — real-time firehose; [SkyView](https://bsky.jazco.dev/) — follower graphs | -| Mastodon | [FediSearch](https://fedisearch.skorpil.cz/) — cross-instance search; [Fedifinder](https://fedifinder.glitch.me/) — find Twitter users on Mastodon | -| Faces | [Search4Faces](https://search4faces.com/) | - ---- - -## 14. Public Records & Company Information - -- [OpenCorporates](https://opencorporates.com/) — world's largest open company DB. -- [SEC EDGAR](https://www.sec.gov/edgar.shtml) — U.S. company filings. -- [OpenOwnership Register](https://register.openownership.org/) — beneficial ownership. -- [MuckRock](https://www.muckrock.com/) — FOIA repository + request tracking. -- [EU Tenders (TED)](https://ted.europa.eu/) — EU procurement notices. -- [World Bank Projects](https://projects.worldbank.org/) — project + procurement records. -- [UK Companies House](https://find-and-update.company-information.service.gov.uk/) — UK companies + officers + filings. - -### 14.1 RU registries - -[Rusprofile](https://www.rusprofile.ru/), [Kontur.Focus](https://focus.kontur.ru/) (freemium), [zakupki.gov.ru](https://zakupki.gov.ru/) (procurement), EGRUL/EGRIP (official, captcha-gated). - -### 14.2 CN registries + USCC + ICP - -- **GSXT** — [gsxt.gov.cn](https://www.gsxt.gov.cn/) National Enterprise Credit Info; cross-check with Tianyancha / Qichacha. -- **USCC (Unified Social Credit Code)** — 18-character entity ID assigned to all CN legal entities. Format: ``. Useful for joining GSXT records to ICP filings. -- **ICP Beian** — [beian.miit.gov.cn](https://beian.miit.gov.cn/) — every domain serving traffic in mainland CN must register an ICP filing; the filing links the domain to a USCC, which links to the legal entity in GSXT. -- Workflow: `target.cn` domain → ICP lookup → USCC → GSXT → entity name + officers + adjacent registered entities. - -### 14.3 Sanctions & Compliance - -- [OFAC SDN List](https://sanctionssearch.ofac.treas.gov/), [EU Sanctions Map](https://www.sanctionsmap.eu/). -- [OpenSanctions](https://www.opensanctions.org/) — aggregated. -- [OCCRP Aleph](https://aleph.occrp.org/) — investigative documents, leaks, company records. - ---- - -## 15. Breach & Leak Data - -- [Have I Been Pwned](https://haveibeenpwned.com/) — breach lookup; Pwned Passwords API (k-anonymity). -- [Dehashed](https://dehashed.com/) — credential search (paid). -- [IntelX](https://intelx.io/) — data intelligence. -- [LeakCheck](https://leakcheck.io/), [Snusbase](https://snusbase.com/), [BreachDirectory](https://breachdirectory.org/), [Scattered Secrets](https://scatteredsecrets.com/), [Phonebook](https://phonebook.cz/), [LeakPeek](https://leakpeek.com/). -- [Cavalier (Hudson Rock)](https://cavalier.hudsonrock.com/) — **infostealer log lookups; FREE; highest single-source ROI for finding compromised employee credentials in corporate SSO**. - -### 15.0.1 HudsonRock Cavalier — direct API recipe - -The web UI wraps a **public, unauthenticated JSON API**. Hit it directly: - -```bash -# By domain (canonical first call) -curl -sk -m 30 "https://cavalier.hudsonrock.com/api/json/v2/osint-tools/search-by-domain?domain=target.com" | jq . - -# By email (single-account check) -curl -sk -m 30 "https://cavalier.hudsonrock.com/api/json/v2/osint-tools/search-by-email?email=alice@target.com" | jq . - -# By URL (when target's app is the breach victim) -curl -sk -m 30 "https://cavalier.hudsonrock.com/api/json/v2/osint-tools/search-by-url?url=https://app.target.com" | jq . -``` - -PowerShell: -```powershell -$hr = Invoke-RestMethod -Uri "https://cavalier.hudsonrock.com/api/json/v2/osint-tools/search-by-domain?domain=$D" -TimeoutSec 30 -"Employees: $($hr.employees) | Users: $($hr.users) | Third-party: $($hr.third_parties) | Total: $($hr.total)" -$hr.data.employees_urls | Sort-Object -Property occurrence -Descending | Select-Object -First 20 -$hr.data.clients_urls | Sort-Object -Property occurrence -Descending | Select-Object -First 15 -``` - -**Top-level JSON fields:** -- `total` — total stealer entries touching this domain. -- `totalStealers` — global stealer-log corpus size (context only). -- `employees` — count of `<*>@` accounts found. -- `users` — count of accounts where the domain appeared as a *visited* URL (customers/vendors). -- `third_parties` — accounts touching adjacent domains in the org. -- `data.employees_urls[]` — `{occurrence, type, url}` — internal apps where employees were logging in when stolen. **Subdomain hits here = recon gold.** -- `data.clients_urls[]` — same shape; user-facing apps (often reveals undocumented public portals). -- `data.stealer_families[]` — `{_key, _value}` → which stealer (RedLine / Lumma / StealC / Vidar / Raccoon). -- `data.dates_compromised[]` — `{_key, _value}` → temporal distribution. - -**Free-tier caveats (CRITICAL to know):** -- Subdomain hostnames in `data.*_urls[]` past the first few are **redacted with asterisks** (`*****.target.com`). Pivot to paid Cavalier tier or other sources for unredacted. -- Free endpoint returns counts + sample URLs only. Cleartext passwords + emails are **never** in the free response. -- Rate limit ~1 req/sec/IP; 429 on burst. Sleep 1s between calls. -- For unredacted creds + bulk enumeration → paid Cavalier portal. - -**Severity mapping (per §15.1 + §15.2):** `employees ≥ 10` → CRITICAL, **regardless of whether the breached service is still online** (legacy Lotus Domino / on-prem mail decommissioned + cloud SSO migration → employees almost always reuse passwords → SSO_EXPOSURE escalates CRITICAL). - -### 15.1 Domain-Level Breach Severity Mapping - -When you query a breach corpus by domain, map the result to severity like so: - -| Stat | Severity | -|---|---| -| ≥ 10 employees compromised | **CRITICAL** | -| 1–9 employees compromised | **HIGH** | -| ≥ 1 end-user (non-employee) compromised | **MEDIUM** | -| Domain seen in breach with 0 named accounts | **INFO** | - -**Employees vs end-users distinction:** an employee account is `@` (the breach victim is the target's own staff). An end-user account is the target's customer who reused a password — useful for credential-stuffing risk awareness but not directly compromising the target's identity fabric. - -### 15.2 SSO_EXPOSURE finding - -When a discovered SSO tenant (Entra GUID / Okta slug / Google Workspace domain) intersects with the breach corpus on its domain → `SSO_EXPOSURE` finding, severity **CRITICAL**. Evidence: tenant ID + product + employee count + per-account source attribution. - -**Legacy-mail-decommissioned pattern (high-value variant):** - -If `mail.` / `webmail.` returns **NXDOMAIN today** but HudsonRock/HIBP corpus still has historical employee credentials against it AND `autodiscover.` resolves to Microsoft IPs (M365) or `aspmx.l.google.com` MX (Workspace), the org migrated from on-prem to cloud — and the stolen passwords almost certainly survived the migration via password reuse. **Escalate to CRITICAL `SSO_EXPOSURE`** even when the legacy host is dead. - -Concrete triggers (all three together): -1. `Resolve-DnsName mail. -Type A` → NXDOMAIN (legacy gone) -2. HudsonRock corpus has employee URLs against the *old* host (e.g. `mail./names.nsf` for Lotus Domino, `mail./owa/` for Exchange, `mail./iwaredir.nsf` for iNotes, `mail./zimbra/` for Zimbra) -3. Current MX → M365 / Google Workspace / Zoho cloud (DNS confirms migration) - -Evidence pack: tenant GUID + breach count + 3+ legacy URLs from corpus + autodiscover Microsoft IPs + current MX. Recommend forced password rotation + MFA audit + Conditional Access review. - ---- - -## 16. Pre-built Wordlists & Probe Paths - -Copy-pasteable arsenals, severity-annotated where relevant. - -### 16.1 Swagger / OpenAPI discovery — 28 paths - -Probe each path on every alive webapp. GET (or HEAD if rate-limited). - -``` -swagger.json -swagger.yaml -swagger/v1/swagger.json -swagger/v2/swagger.json -swagger-ui.html -swagger-ui/ -swagger-resources -api-docs -api-docs.json -api/swagger -api/swagger.json -api/swagger-ui.html -api/v1/swagger.json -api/v2/swagger.json -api/v3/api-docs -v2/api-docs -v3/api-docs -openapi.json -openapi.yaml -openapi/v1 -openapi/v3 -docs -redoc -rapidoc -api/docs -api/documentation -.well-known/openapi -``` - -**Severity:** -- Reachable Swagger/OpenAPI spec without auth → **HIGH** `LEAKY_API_SPEC` (full endpoint enumeration leaks; often reveals undocumented internal APIs). -- Behind auth but accessible to any authenticated user → MEDIUM (still discloses internal API surface). - -### 16.2 GraphQL discovery — 13 paths - -``` -graphql -graphiql -api/graphql -v1/graphql -v2/graphql -query -api/query -gql -altair -playground -subscriptions -graphql/console -api/v1/graphql -``` - -**Standard introspection POST body:** -```json -{ - "operationName": "IntrospectionQuery", - "query": "query IntrospectionQuery { __schema { types { name kind fields { name type { name kind } } } queryType { name } mutationType { name } subscriptionType { name } } }" -} -``` - -**Severity:** -- Introspection returns schema without auth → **HIGH** `OPEN_GRAPHQL_API`. -- Field-suggestion enumeration possible (server returns "did you mean" for typo'd field names) → **MEDIUM** (re-derive partial schema even when introspection is disabled). -- `/graphql` accepts batched queries (`[...]` request body) → MEDIUM (rate-limit bypass surface; auth bypass via mixed batches). - -UI markers (lower severity but still discoverable): -- HTML response contains `graphiql`, `playground`, `apollo studio`, `altair` → GraphiQL UI exposed (often shipped accidentally on prod). - -### 16.3 High-risk ports — 35 services - -For each open port, emit a finding with the severity and "why an attacker cares" below. Source for the open-port observation: Shodan InternetDB (free, 1 req/sec) is the recommended starting point. - -| Port | Service | Severity | Why it matters | -|---|---|---|---| -| 21 | FTP | HIGH | Anonymous read often enabled; cleartext creds. | -| 22 | SSH | LOW | Banner discloses version; brute-force surface. | -| 23 | Telnet | HIGH | Cleartext protocol; should never be exposed. | -| 25 | SMTP | LOW | Open relay risk; version banner. | -| 53 | DNS | LOW | Recursion = DDoS amplifier; AXFR opportunism. | -| 80 | HTTP | INFO | Standard. | -| 110 | POP3 | LOW | Cleartext if no STARTTLS. | -| 111 | rpcbind | MEDIUM | NFS exports enumeration. | -| 135 | MS RPC | HIGH | Enum via Impacket. | -| 139 | NetBIOS-SSN | HIGH | File/printer enum. | -| 143 | IMAP | LOW | Cleartext if no STARTTLS. | -| 161 | SNMP | HIGH | Community strings often `public`/`private`; full device enum. | -| 389 | LDAP | HIGH | Anonymous bind = full directory dump. | -| 443 | HTTPS | INFO | Standard. | -| 445 | SMB | **CRITICAL** | EternalBlue, SMB relay, anonymous shares. | -| 465 | SMTPS | LOW | Banner. | -| 514 | rsyslog | MEDIUM | Log injection / DoS. | -| 587 | SMTP-MSA | LOW | Banner. | -| 631 | IPP/CUPS | MEDIUM | Print server enum / RCE in old CUPS. | -| 873 | rsync | HIGH | Modules often listable; backup data exposure. | -| 1433 | MSSQL | HIGH | Brute-force; xp_cmdshell. | -| 1521 | Oracle TNS | HIGH | Brute-force; SID enum. | -| 2049 | NFS | HIGH | World-readable exports. | -| 2375 | Docker API (unencrypted) | **CRITICAL** | Unauthenticated container/host takeover. | -| 2376 | Docker API (TLS) | HIGH | Cert validation bypass risk. | -| 3000 | Common dev / Grafana | MEDIUM | Often Grafana / Express dev with default creds. | -| 3306 | MySQL | HIGH | Brute-force; default `root:""`. | -| 3389 | RDP | **CRITICAL** | BlueKeep / DejaBlue / NLA bypass. | -| 5432 | PostgreSQL | HIGH | Brute-force; default `postgres:postgres`. | -| 5601 | Kibana | HIGH | Often unauthenticated; Elasticsearch pivot. | -| 5900 | VNC | HIGH | Often unauthenticated or weak password. | -| 5984 | CouchDB | HIGH | Default no auth; admin party. | -| 6379 | Redis | **CRITICAL** | No auth default; write `authorized_keys` for SSH. | -| 7001 | WebLogic | HIGH | Frequent CVEs (CVE-2020-14882, etc.). | -| 8000 | Common dev | MEDIUM | Django, common dev servers. | -| 8080 | HTTP-alt | MEDIUM | Tomcat, Jenkins, common proxy. | -| 8443 | HTTPS-alt | MEDIUM | Same as 8080. | -| 8888 | Common dev / Jupyter | HIGH | Jupyter often exposes interactive shell. | -| 9090 | Cockpit / Prometheus | HIGH | Server admin UI / metrics scraping. | -| 9200 | Elasticsearch | **CRITICAL** | Typically no auth. | -| 9300 | Elasticsearch transport | HIGH | Cluster join + RCE. | -| 11211 | memcached | MEDIUM | UDP DDoS amp; data dump. | -| 27017 | MongoDB | **CRITICAL** | No auth by default. | -| 50070 | Hadoop NameNode | HIGH | HDFS browse. | - -When Shodan InternetDB returns `vulns[]` for a port, escalate the finding severity by one tier and include the CVE list in evidence. - -### 16.4 Missing security headers — 6 findings - -For every alive webapp, audit response headers. Each missing header below = one finding. - -| Header | Severity (default) | Severity (sensitive path) | Notes | -|---|---|---|---| -| `Strict-Transport-Security` | MEDIUM | **HIGH** | Sensitive paths: `/login`, `/signin`, `/sso`, `/admin`, `/auth`. | -| `Content-Security-Policy` | MEDIUM | MEDIUM | XSS impact mitigation gone. | -| `X-Frame-Options` | LOW | LOW | Clickjacking. (CSP `frame-ancestors` is the modern replacement.) | -| `X-Content-Type-Options` | LOW | LOW | MIME-sniff XSS. | -| `Referrer-Policy` | INFO | INFO | Outbound link leakage. | -| `Permissions-Policy` | INFO | INFO | Feature-policy hardening. | - -### 16.5 Always-on HTTP checks — 15 paths - -Run these against every alive webapp regardless of Nuclei availability. Cheap; high signal. - -| Path | Finding | Severity | Match logic | -|---|---|---|---| -| `/.git/config` | Exposed `.git` repo | **CRITICAL** | Body contains `[core]`, `[remote`, `repositoryformatversion` | -| `/.git/HEAD` | Exposed `.git/HEAD` | HIGH | Body matches `^ref:\s` | -| `/.env` | Exposed `.env` | **CRITICAL** | Multiline regex `^\s*[A-Z_][A-Z0-9_]*\s*=` | -| `/server-status` | Apache server-status | MEDIUM | Body contains `Apache Server Status` or matching title | -| `/server-info` | Apache mod_info | MEDIUM | Body contains `Apache Server Information` | -| `/.DS_Store` | Exposed `.DS_Store` | LOW | Byte signature `\x00\x00\x00\x01Bud1` | -| `/phpinfo.php` | phpinfo() leak | HIGH | Body contains `phpinfo()`, `PHP Version`, or matching title | -| `/info.php` | phpinfo() (alt path) | HIGH | Same as above | -| `/actuator/env` | Spring Boot `/actuator/env` | **CRITICAL** | Body contains `"propertySources"`, `systemProperties`, `systemEnvironment` | -| `/actuator/heapdump` | Spring Boot heapdump | **CRITICAL** | HPROF magic bytes / large binary download | -| `/_cat/indices` | Elasticsearch open | HIGH | Returns index list | -| `/console` | Jenkins script console | HIGH | Body contains `Jenkins`/`Script Console` | -| `/manager/html` | Tomcat Manager | HIGH | Body contains `Tomcat Web Application Manager` | -| `/wp-admin/install.php` | Orphaned WP install | LOW | Body contains `WordPress Installation` | -| `/.well-known/security.txt` | Disclosure policy info | INFO | Parse contact + policy fields | - -Plus parse `/robots.txt` for `Disallow:` paths — those become the next-tier wordlist for that target. - -### 16.6 SAML metadata — 5 paths - -``` -/saml/metadata -/FederationMetadata/2007-06/FederationMetadata.xml -/federationmetadata/2007-06/federationmetadata.xml -/simplesaml/saml2/idp/metadata.php -/auth/saml2/metadata -``` - -Reachable SAML metadata XML reveals: `EntityID`, signing certs (often pinned → cert-reuse pivot), `SingleSignOnService` URL, `NameIDFormat`. Mark as `MISCONFIG` (LOW severity unless metadata leaks internal hostnames or non-public certs, then MEDIUM). - -### 16.7 SSO subdomain prefixes — 8 prefixes - -Probe each against root domain + every sibling brand domain: -``` -auth.{domain} -login.{domain} -sso.{domain} -idp.{domain} -iam.{domain} -identity.{domain} -accounts.{domain} -oauth.{domain} -``` - -Plus probe `/.well-known/openid-configuration` on every alive subdomain (regardless of prefix). - -### 16.8 Cloud bucket permutation arsenal - -**6 prefixes:** -``` -"" # bare candidate -backup- -assets- -static- -dev- -prod- -``` - -**15 suffixes:** -``` -"" # bare candidate --backup --assets --static --media --data --uploads --dev --prod --staging --logs --private --public --dump --archive -``` - -**47 generic stems** (filter unless combined with target-identifying token): -``` -www, mail, email, app, apps, web, webmail, ftp, cdn, static, assets, media, img, images, -videos, download, downloads, upload, uploads, data, files, docs, support, help, kb, -blog, news, dev, test, staging, stg, qa, uat, sandbox, preprod, preview, vpn, -mx, smtp, imap, pop, dns, ns, ns1, ns2, mx1, mx2 -``` - -**Provider URL templates:** - -S3: -``` -https://{candidate}.s3.amazonaws.com/ -https://{candidate}.s3-{region}.amazonaws.com/ # try us-east-1, us-west-2, eu-west-1, ap-southeast-1 first -https://s3.{region}.amazonaws.com/{candidate}/ -``` - -GCS: -``` -https://{candidate}.storage.googleapis.com/ -https://storage.googleapis.com/{candidate}/ -``` - -Azure Blob: -``` -https://{candidate}.blob.core.windows.net/ -``` - -**Probe technique:** HEAD first → 200/301 = exists, 403 = exists private, 404 = skip. On exists, GET root → if XML/JSON object listing returns, **CRITICAL** `PUBLIC_CLOUD_BUCKET`. Direct-URL object reads but not listable → **HIGH** `PUBLIC_CLOUD_BUCKET_OBJECT_READ`. - -### 16.9 JS guess-paths for endpoint discovery - -Probe these paths on every alive webapp (in addition to scraped `