pentest · git:20260423.c064e23 · 2026-04-23 · sha256 57c7bee9efe0ebc5
pentest git:20260423.c064e23B
Immutable. This exact content is served forever at /api/v1/blob/57c7bee9efe0ebc5.
---
name: pentest
description: Security testing and OSINT toolkit wrapping 183+ tools from Z4nzu/hackingtool (nmap, sherlock, amass, subfinder, nuclei, httpx, holehe, maigret, trufflehog, sqlmap, etc.). 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. Claude Code runs tools locally — native, WSL, or Docker — and only hands off on real errors (hardware missing, stdin prompts, unavailable backend).
---
# Pentest / OSINT Skill
You are Claude Code running locally on the user's machine. You have real Bash, real filesystem, real process execution. **Try tools first; handoff only on actual failure.**
## The try-first rule
The old version of this skill pre-blocked tools based on capability flags (`requires_sudo`, `requires_gui`, `requires_hardware`). That was wrong for Claude Code — you run locally. Docker containers run as root. GUI apps open on the user's desktop. Hardware attached to the host is reachable through the backend.
**Always attempt a run before handing off.** `ht_run.py` now:
- Runs the command on the preferred backend
- Retries with `sudo -n` if it gets permission_denied on native/wsl
- Classifies real errors (`no_device`, `not_installed`, `stdin_needed`) and only hands off when they can't be worked around
- Only pre-blocks `interactive` tools (stdin mid-run — stdin piping can't answer prompts)
## 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.
| Script | Purpose |
|---|---|
| `ht_search.py` | Query the tool index. Filter by `--q`, `--category`, `--tag`, `--capability runnable_by_claude`, `--os linux`. |
| `ht_env.py` | Report host OS, WSL distros, Docker availability, and `preferred_backend`. |
| `ht_run.py` | Tries a tool. `--command "..."` for full-command override (use when RUN_COMMANDS is empty, e.g. nmap). `--args "..."` for append-args. `--network-host` for LAN scans via docker. `--privileged` for raw sockets / hardware. `--force` to ignore the interactive pre-block. |
## Golden path
1. **Read the ask.** Map it to a workflow if possible — see `reference/workflows.md`.
2. **Check the environment once.** `python ${CLAUDE_PLUGIN_ROOT}/scripts/ht_env.py` — tells you which backend will run. If Windows + no WSL + Docker stopped, ask the user to start Docker before any Linux-only work.
3. **Find candidate tools.** `ht_search.py --q "<keyword>"` to discover tool ids. Don't assume ids.
4. **Try running them.**
- If the tool's RUN_COMMANDS slot is populated → `ht_run.py <tool_id> --args "..."`
- If RUN_COMMANDS is empty (common — many hackingtool classes have `runnable=False`) → use `--command "<full command>"`. The plugin still picks the right backend and docker image for you.
- For LAN / gateway scans via Docker → add `--network-host` so the container can see your LAN.
- For raw sockets / hardware → add `--privileged`.
5. **Parse the JSON result:**
- `status: ok` → summarize the interesting parts of stdout for the user; don't paste the whole output if it's long
- `status: error` with `returncode != 0` and no handoff → tool ran but failed. Report stderr to the user and decide whether to retry with different args.
- `status: handoff` → the plugin exhausted what it can do. Emit the handoff block (see below).
- `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.
## Docker image overrides
`ht_run.py` maps common tools to purpose-built images that are faster and lighter than the fallback `kalilinux/kali-rolling`:
| Tool | Image |
|---|---|
| NMAP | `instrumentisto/nmap` |
| Nuclei | `projectdiscovery/nuclei` |
| Subfinder / Httpx / Katana | `projectdiscovery/*` |
| Amass | `caffix/amass` |
| TheHarvester | `secsi/theharvester` |
| Holehe / Maigret | `megadose/holehe`, `soxoj/maigret` |
| TruffleHog / Gitleaks | official images |
| Sqlmap | `paoloo/sqlmap` |
| Impacket / NetExec | `rflathers/impacket`, `byt3bl33d3r/netexec` |
These images have the binary as ENTRYPOINT, so `ht_run.py` passes args directly. If you need a tool not in the map, it falls back to `kalilinux/kali-rolling bash -lc <cmd>` — slower first pull, but works for anything Kali can `apt install`.
Override with `--docker-image my/image` for one-off swaps.
## Handoff (only when you can't do it)
When `ht_run.py` returns `status: handoff`, surface the command to the user in this shape:
```
──── Handoff: <tool title> ─────────────────────
Reason: <reason>
Diagnostic: <one-line summary of what failed>
Run this yourself:
<command>
Paste the output back and I'll continue.
─────────────────────────────────────────────────
```
Handoff reasons and what they mean:
| Reason | What happened | User action |
|---|---|---|
| `interactive` | Tool reads stdin mid-run (index flag) | Run manually, or use `--force` with `--command` to supply non-interactive args |
| `no_backend` | No Linux runtime — no WSL, Docker stopped | Start Docker Desktop or `wsl --install -d Ubuntu` |
| `no_device` | Tool reported no wifi adapter / no monitor mode / no USB | Hardware not visible to backend (common on Docker for wifi — needs USB passthrough) |
| `not_installed` | Binary missing in the backend | Try `ht_run.py <id> --install` or `--docker-image <image-with-tool>` |
| `sudo_password_needed` | `sudo -n` failed — needs a password prompt | Run manually, or configure passwordless sudo for that command |
| `interactive_detected` | Ran, but the tool blocked on stdin | Index should be updated; in the meantime, handoff |
| `no_command` | Index has no RUN_COMMANDS and no `--command` was given | Pass `--command "<full cmd>"` yourself |
## Ad-hoc commands (no wrapper)
For one-off invocations that don't map to a hackingtool tool id — e.g. `curl -I https://target` or `arp -a` — just use Bash directly. You don't need to route everything through `ht_run.py`. Use the wrapper when:
- The tool is in the hackingtool index and you want the image/backend selection
- You want the error classification / sudo retry
- You're chaining many tools and want uniform JSON output
## References
- `${CLAUDE_PLUGIN_ROOT}/skills/pentest/reference/workflows.md` — named playbooks
- `${CLAUDE_PLUGIN_ROOT}/skills/pentest/reference/handoff-patterns.md` — templates for each handoff reason