pentest · diff

git:20260425.aafb10c to git:20260425.d61ce63

41 added, 72 removed. Audit B to A.

---
name: pentest
description: Security testing and OSINT toolkit wrapping 183+ tools from Z4nzu/hackingtool (nmap, sherlock, amass, subfinder, nuclei, httpx, holehe, maigret, trufflehog, sqlmap, impacket, netexec, and more). Use whenever the user asks to recon a target, scan a network, enumerate subdomains, investigate a username or email, test a web app, check for leaked secrets, or any other pentest / OSINT task. Runs locally on any OS via native Bash, WSL, or purpose-built Docker images — the skill picks the right backend automatically.
---
# Pentest / OSINT Skill
- Claude Code runs on the user's machine. You have real Bash, real filesystem, real process execution, and a fleet of pentest tools one wrapper call away. **Lean on the wrappers. Manual `curl` loops are a last resort, not a default.**
-
- ## Step 0 — MANDATORY PREFLIGHT (run on every first invocation)
+ You have real Bash, real filesystem, real process execution, and a fleet of pentest tools one wrapper call away. **Use the wrappers — manual `curl` loops are a fallback, not a default.**
- Before doing anything else — before recon, before tool selection, before even acknowledging the request — run:
+ ## Step 0 — Preflight (run first, every session)
```bash
python ${CLAUDE_PLUGIN_ROOT}/scripts/ht_preflight.py
```
- The output tells you:
- - `verdict`: `ready` | `partial` | `blocked`
- - `summary_for_user`: a pre-formatted message you should paraphrase to the user
- - `recommendations`: ordered list of `[priority] action → why` items the user must complete to unlock full coverage
-
- ### Acting on the verdict
-
- - **`ready`** (score ≥75): proceed normally. Mention the backend in one sentence ("Running via Docker — nuclei/subfinder/nmap all available.") and start work. Do not nag about info-priority recommendations.
- - **`partial`** (40–74): tell the user what's missing in plain English using the `recommendations`. Offer to proceed with reduced coverage, or wait for them to fix the gap. Do not silently degrade to manual probes.
- - **`blocked`** (<40): stop. Surface the recommendations verbatim and ask the user to take action before continuing. Do **not** fall back to manual `curl` / PowerShell probes as a substitute — that produces shallow results and misleads the user about coverage.
-
- The preflight is cheap (<1 second). Re-run it if the user says they've installed something or started Docker.
-
- ## Tool-first mindset (HARDENING)
-
- When you've decided what to do, the order of preference is **always**:
+ Read the `verdict` and act:
- 1. **Wrapper-mediated tool** via `ht_run.py` — picks the right Docker image, handles permissions, returns structured JSON.
- 2. **Direct call to a known binary** if it's already on PATH (e.g. `nmap` natively on Linux).
- 3. **Manual `curl` / `dig` / `nslookup`** only for spot-checks that have no real tool equivalent (e.g. fetching a single header for a sanity check).
+ - **`ready`** → state the backend in one sentence and start work.
+ - **`partial`** → tell the user what's missing (use `recommendations`), offer to proceed with reduced coverage.
+ - **`blocked`** → stop. Surface the recommendations and wait. **Do not** substitute manual `curl` / PowerShell probes for a missing tool — that misleads the user about coverage.
- **Anti-patterns to avoid:**
+ Re-run preflight if the user installs something or starts Docker.
- - ❌ Writing a `for` loop in bash that hits 50 paths with `curl` instead of running `ffuf` / `gobuster`.
- - ❌ Hand-crafting JSON payloads to test a dozen endpoints when `nuclei -dast` or a custom nuclei template would cover them.
- - ❌ Manually parsing TLS handshake output instead of running `nuclei -tags ssl` or `testssl.sh`.
- - ❌ Asking the user to paste WHOIS output when `whois` / `amass intel` / `theharvester` are one wrapper call away.
- - ❌ Skipping `subfinder` and inferring subdomains from CT logs by hand when the wrapper does both faster.
- - ❌ Saying "I'd run nuclei but I don't have it" when the preflight said `verdict=ready` — you do have it via Docker.
+ ## Tool-first defaults
- **Decision rules** (apply when scope is "audit" / "scan" / "find vulns"):
+ When the ask is "audit / scan / find vulns", reach for:
- | Ask | Default tool chain |
- |-----|-------------------|
+ | Ask | Default chain |
+ |-----|---------------|
| Subdomain enumeration | `subfinder -all` → `httpx -title -tech-detect` |
- | Web vuln scan | `nuclei -severity medium,high,critical` (then `info,low` separately) |
+ | Web vuln scan | `nuclei -severity medium,high,critical` |
| Port scan | `nmap -sV -sC` (or `naabu` for fast SYN sweep) |
| Directory / API discovery | `ffuf -w wordlist -u https://target/FUZZ` |
- | Secret hunting in repo / files | `trufflehog filesystem .` or `gitleaks detect` |
- | Username / email investigation | `holehe email@x` → `sherlock username` → `maigret username` |
+ | Secret hunting | `trufflehog filesystem .` or `gitleaks detect` |
+ | Username / email investigation | `holehe` → `sherlock` → `maigret` |
| TLS audit | `nuclei -tags ssl,tls` |
- | Domain OSINT | `amass intel -d target` → `theharvester -d target` |
- | Stealer-log / breach lookup | `trufflehog`, then domain-specific APIs |
+ | Domain OSINT | `amass intel` → `theharvester` |
- If a wrapper exists for the job, **start with the wrapper**. Manual probing is a refinement step after the tool's output has narrowed the surface, not a replacement for it.
+ Anti-patterns: `for`-looping curl across many paths instead of `ffuf` / `nuclei`; hand-parsing TLS output instead of `nuclei -tags ssl`; saying "I don't have nuclei" when preflight returned `ready` (you have it via Docker).
## The execution model
Every tool runs through `ht_run.py`, which:
1. Reads `ht_env.py` to pick a backend — **native** on Linux/macOS, **WSL** on Windows with a real distro, **Docker** anywhere with Docker Desktop.
- 2. Looks up a purpose-built Docker image for the tool if one exists (`instrumentisto/nmap`, `projectdiscovery/nuclei`, `caffix/amass`, 20+ more). Falls back to `kalilinux/kali-rolling` for anything not mapped.
- 3. Executes the command. If permission-denied on native/WSL, auto-retries with `sudo -n`. If an error is classifiable as recoverable, handles it.
- 4. Returns structured JSON with `status`, `stdout`, `stderr`, `returncode`, and the command that ran.
+ 2. Looks up a purpose-built Docker image for the tool if one exists (`instrumentisto/nmap`, `projectdiscovery/nuclei`, `caffix/amass`, 20+ more). Falls back to `kalilinux/kali-rolling`.
+ 3. Executes the command. Auto-retries with `sudo -n` on permission-denied (native/WSL).
+ 4. Returns JSON: `status`, `stdout`, `stderr`, `returncode`, `command`.
- Only one pre-block exists: tools flagged `interactive` (they read stdin mid-run). Bypass with `--force` + `--command` if you have non-interactive args.
+ Only one pre-block: tools flagged `interactive`. Bypass with `--force` + `--command` if you have non-interactive args.
## Bundled scripts
- All scripts live at `${CLAUDE_PLUGIN_ROOT}/scripts/`. Call them with `python ${CLAUDE_PLUGIN_ROOT}/scripts/<script>.py ...`. They emit JSON on stdout.
+ All at `${CLAUDE_PLUGIN_ROOT}/scripts/`. Emit JSON.
| Script | Purpose |
|---|---|
- | `ht_preflight.py` | **Run first.** Capability check + user-facing setup recommendations. Verdict drives whether to proceed. |
- | `ht_search.py` | Query the tool index. Filter by `--q`, `--category`, `--tag`, `--capability runnable_by_claude`, `--os linux`. |
- | `ht_env.py` | Low-level: report host OS, WSL distros, Docker availability, and `preferred_backend`. (Preflight wraps this.) |
- | `ht_run.py` | Execute a tool. `--command "..."` for a full-command override. `--args "..."` for append-args. `--network-host` for LAN scans via docker. `--privileged` for raw sockets / hardware. `--force` to bypass the interactive pre-block. |
+ | `ht_preflight.py` | **Run first.** Capability check + setup recommendations. |
+ | `ht_search.py` | Query the tool index (`--q`, `--category`, `--tag`, `--capability`, `--os`). |
+ | `ht_env.py` | Low-level env detect. (Preflight wraps this.) |
+ | `ht_run.py` | Execute. `--command "..."` to override, `--args "..."` to append, `--network-host` for LAN, `--privileged` for raw sockets, `--force` to bypass the interactive block. |
## Golden path
- 1. **Preflight.** `ht_preflight.py` — handle verdict per Step 0 above.
- 2. **Read the ask.** Map to a workflow if possible — see `reference/workflows.md`. Apply the decision rules table above for the default tool chain.
- 3. **Find candidate tools.** `ht_search.py --q "<keyword>"` to discover tool ids. Don't assume ids — they're snake-cased and namespaced (e.g. `web_attack.Nuclei`, not `nuclei`).
- 4. **Execute them.**
- - If RUN_COMMANDS is populated → `ht_run.py <tool_id> --args "..."`
- - If RUN_COMMANDS is empty (many upstream tools have `runnable=False`) → use `--command "<full command>"`. The wrapper still picks the backend and image for you.
- - For LAN / gateway scans via Docker → add `--network-host` so the container sees your LAN.
- - For raw sockets / hardware → add `--privileged`.
- 5. **Parse the JSON result:**
- - `status: ok` → summarize the interesting parts of stdout; don't paste the whole output if it's long.
- - `status: error` with `returncode != 0` → tool ran but failed. Report stderr and decide whether to retry with different args.
- - `status: fallback` → see `reference/runtime-fallbacks.md`. Pick the template matching the `reason` field.
- - `status: timeout` → raise `--timeout <N>` or break the scan into smaller chunks.
- 6. **Compose, don't just call once.** OSINT chains: `subfinder → httpx → nuclei`. `holehe → sherlock → maigret`. Feed outputs into the next step.
+ 1. **Preflight** — handle verdict per Step 0.
+ 2. **Read the ask** — map to a workflow (`reference/workflows.md`) and apply the defaults table.
+ 3. **Find tool ids** — `ht_search.py --q "<keyword>"`. Don't guess; ids are namespaced (e.g. `web_attack.Nuclei`).
+ 4. **Execute** — `ht_run.py <tool_id> --args "..."`, or `--command "<full>"` for tools where `runnable=False`. Add `--network-host` for LAN, `--privileged` for raw sockets.
+ 5. **Parse status:** `ok` → summarize highlights; `error` → report stderr, decide whether to retry; `fallback` → see `reference/runtime-fallbacks.md`; `timeout` → raise `--timeout` or chunk the scan.
+ 6. **Compose** — `subfinder → httpx → nuclei`, `holehe → sherlock → maigret`. Feed outputs forward.
## Docker image overrides
- `ht_run.py` maps common tools to purpose-built images. These pull faster than Kali and have proper ENTRYPOINTs, so you just pass args:
+ `ht_run.py` maps common tools to purpose-built images (faster pulls, proper ENTRYPOINTs):
| Tool | Image |
|---|---|
| NMAP | `instrumentisto/nmap` |
| Nuclei | `projectdiscovery/nuclei` |
| Subfinder / Httpx / Katana | `projectdiscovery/*` |
| Amass | `caffix/amass` |
| TheHarvester | `secsi/theharvester` |
| Holehe / Maigret / SpiderFoot | official images |
| TruffleHog / Gitleaks | official images |
| Sqlmap | `paoloo/sqlmap` |
| Impacket / NetExec | `rflathers/impacket`, `byt3bl33d3r/netexec` |
- Override with `--docker-image my/image` for one-off swaps.
+ Override with `--docker-image my/image` for one-offs.
- ## Ad-hoc commands (no wrapper) — narrow use only
+ ## Ad-hoc commands
- For one-off invocations that genuinely don't map to a hackingtool tool id — e.g. `curl -I https://target` for a single header sanity check, `arp -a`, `dig @8.8.8.8 example.com` — Bash is fine. Use `ht_run.py` whenever:
- - The tool is in the index and you want automatic image/backend selection
- - You want permission-error auto-retry
- - You're chaining multiple tools and want uniform JSON output
- - The task is enumeration / scanning / fuzzing at any scale (≥5 requests against a target)
+ Bash directly is fine for spot-checks: a single `curl -I`, `arp -a`, `dig @8.8.8.8 example.com`. Reach for `ht_run.py` whenever:
- If you find yourself writing a `for` loop with `curl` to test more than a handful of paths, **stop and reach for `ffuf` / `nuclei` / `gobuster` instead**.
+ - The tool is in the index and you want auto image/backend selection.
+ - You're chaining multiple tools and want uniform JSON output.
+ - The task is enumeration / scanning / fuzzing at any scale (**≥5 requests against a target**).
+ If you're about to write a `for` loop hitting many paths with `curl`, **stop — use `ffuf` / `nuclei` / `gobuster` instead.**
+
## References
- `${CLAUDE_PLUGIN_ROOT}/skills/pentest/reference/workflows.md` — named playbooks
- - `${CLAUDE_PLUGIN_ROOT}/skills/pentest/reference/runtime-fallbacks.md` — templates for each fallback reason
+ - `${CLAUDE_PLUGIN_ROOT}/skills/pentest/reference/runtime-fallbacks.md` — templates per fallback reason