MCP server for authorized security assessments
Built in Go with zero external dependencies. Multi-transport (stdio/SSE/WebSocket), tool chaining,
structured parsing, report generation, plugin system, rate limiting, caching, and audit logging.
go install github.com/yasindce1998/redhands/cmd/redhands@latest
curl -fsSL https://raw.githubusercontent.com/yasindce1998/redhands/main/scripts/install.sh | bash
This verifies a release archive against its published SHA256SUMS and prints MCP
configuration instructions. Older releases without checksums must be built from source.
To install security tools, use a reviewed checkout containing the dependency lock:
git clone https://github.com/yasindce1998/redhands.git && cd redhands
sudo ./scripts/install-tools.sh --profile web
git clone https://github.com/yasindce1998/redhands.git && cd redhands
make build
sudo make install
sudo make install-tools PROFILE=minimal # nmap, nuclei, httpx, subfinder
sudo make install-tools PROFILE=web # web assessment tools
sudo make install-tools PROFILE=network # network/infrastructure tools
sudo make install-tools PROFILE=ad # Active Directory tools
sudo make install-tools PROFILE=k8s # Kubernetes offensive tools
sudo make install-tools PROFILE=uefi # UEFI bootkit simulation
sudo make install-tools PROFILE=telecom # StaticZero telecom research (Linux only)
sudo make install-tools PROFILE=aegis # Aegis-Shadow eBPF research (Linux only)
sudo make install-tools PROFILE=sbom # SBOM + vulnerability scanning (syft, grype)
sudo make install-tools PROFILE=recon # deep reconnaissance tools
sudo make install-tools PROFILE=all # everything
make check-deps
git clone https://github.com/yasindce1998/redhands.git; cd redhands
go build -o bin\redhands.exe .\cmd\redhands
.\scripts\install-tools.ps1 -Profile web
.\scripts\install-tools.ps1 -Check # show what's installed/missing
Add to your MCP client config:
Claude Code (~/.claude/claude_desktop_config.json), Cursor (.cursor/mcp.json), VS Code (.vscode/mcp.json):
{
"mcpServers": {
"redhands": {
"command": "redhands"
}
}
}
redhands
REDHANDS_TRANSPORT=sse redhands # loopback only; see secure remote setup below
REDHANDS_TRANSPORT=ws redhands # loopback only
docker compose run --rm -i redhands
| Profile | Tools Included | Use Case |
|---|---|---|
minimal |
nmap, subfinder, httpx, nuclei | Quick recon and scanning |
web |
nmap, recon set (subfinder, amass, dig, waybackurls, gau, arjun), web set (httpx, katana, nikto, whatweb, testssl.sh), ffuf, gobuster, feroxbuster, masscan, rustscan, nuclei, sqlmap | Web application testing |
network |
nmap, masscan, rustscan, tshark, chisel, ligolo | Network/infrastructure |
ad |
nmap, impacket, certipy, crackmapexec, hashcat, john | Active Directory attacks |
k8s |
nmap, kubedagger-client, kubedagger-operator, kubedagger-server | Kubernetes offensive (eBPF) |
uefi |
barzakh-adversary, barzakh-scanner, barzakh-server | UEFI bootkit adversary simulation |
telecom |
staticzero-defense, staticzero-offense, modem-firmware, sdr-bridge | Telecom security research (eBPF, Linux only) |
aegis |
aegis-shadow-defense, aegis-shadow-offense, aegis-c2 | Linux eBPF security research (Aegis-Shadow) |
sbom |
syft, grype | SBOM generation and vulnerability scanning |
recon |
recon set (subfinder, amass, dig, waybackurls, gau, arjun) + web set (httpx, katana, nikto, whatweb, testssl.sh) | Deep reconnaissance |
all |
Everything | Full offensive toolkit |
Profiles describe desired dependencies, not a guarantee every dependency is available on every OS. RustScan, testssl.sh, and CrackMapExec currently need manual installation; KubeDagger client is Linux-only. Installers report missing tools rather than down unverified replacements. See dependency maintenance.
make build produces redhands, redhands-cli, and redhands-creds in bin/.
The CLI uses the same authenticated SSE transport as MCP clients:
REDHANDS_TRANSPORT=sse ./bin/redhands
./bin/redhands-cli tools
./bin/redhands-cli targets add 192.0.2.1 lab-host
./bin/redhands-cli status
For HTTPS, set REDHANDS_URL=https://your-server:8443, REDHANDS_API_KEY, and
REDHANDS_CLIENT_CA=/path/to/ca.pem. For mTLS, also set REDHANDS_CLIENT_CERT
and REDHANDS_CLIENT_KEY. The CLI rejects untrusted certificates, redirects, and
non-loopback plain HTTP. REDHANDS_CLIENT_TIMEOUT sets its total deadline (default 6m).
Tool failures return a nonzero exit status; calls are never retried automatically.
- Go 1.26.11+ (for building from source; if using Go 1.27, use 1.27.4+)
- Security tools on PATH (installed via scripts above, Docker, or manually)
294 tools are registered across 36 toolsets. A default startup serves 287 of
them: the two credstore tools additionally require REDHANDS_CREDSTORE_KEY,
and the five GoPhish phishing tools additionally require REDHANDS_GOPHISH_*
environment variables.
| Tool | Description |
|---|---|
nmap_port_scan |
TCP/UDP port scanning (SYN, connect, UDP) |
nmap_service_detect |
Service and version detection (-sV) |
nmap_os_detect |
OS fingerprinting (-O) |
nmap_vuln_scan |
NSE vulnerability scripts |
| Tool | Description |
|---|---|
subfinder_enum |
Subdomain enumeration from passive sources |
amass_enum |
Network mapping and ASN discovery (OWASP Amass) |
dns_lookup |
DNS record queries via dig (A, AAAA, MX, NS, TXT, etc.) |
waybackurls |
Fetch archived URLs from the Wayback Machine |
gau_urls |
Fetch URLs from AlienVault OTX, Wayback, CommonCrawl, URLScan |
arjun_discover |
HTTP parameter discovery (hidden GET/POST params) |
| Tool | Description |
|---|---|
httpx_probe |
HTTP service probing with tech detection |
katana_crawl |
Next-gen web crawler (JS crawling, headless) |
nikto_scan |
Web server vulnerability scanner |
whatweb_fingerprint |
Web technology fingerprinting |
testssl_scan |
TLS/SSL encryption testing |
| Tool | Description |
|---|---|
ffuf_fuzz |
Web fuzzing (dirs, params, vhosts) |
gobuster_dir |
Directory/file brute-forcing |
feroxbuster_scan |
Recursive content discovery (forced browsing) |
| Tool | Description |
|---|---|
masscan_scan |
Internet-scale port scanning (10M pps) |
rustscan_scan |
Modern fast port scanner (all 65535 ports in seconds) |
| Tool | Description |
|---|---|
sqlmap_scan |
Automatic SQL injection detection and exploitation |
| Tool | Description |
|---|---|
nuclei_scan |
Template-based vulnerability scanning |
Kerberos interaction toolkit — for Windows engagement hosts running RedHands
with an operator-compiled Rubeus.exe allowlisted as rubeus (GhostPack
ships no compiled releases by design; Linux execution is not possible — the
tool P/Invokes Windows LSA APIs).
| Tool | Description |
|---|---|
rubeus_kerberoast |
|
rubeus_asktgt |
|
rubeus_asreproast |
|
rubeus_harvest |
|
rubeus_dump |
|
rubeus_describe |
Decode and describe a base64 ticket |
| Tool | Description |
|---|---|
impacket_secretsdump |
Dump SAM/LSA/NTDS.dit secrets |
impacket_psexec |
PsExec-style remote execution |
impacket_wmiexec |
WMI-based remote execution |
impacket_smbclient |
SMB client operations |
impacket_dcomexec |
DCOM remote execution |
impacket_get_tgt |
Request Kerberos TGT |
impacket_get_st |
Request service ticket (Kerberoast/delegation) |
impacket_ntlmrelay |
NTLM relay attack |
| Tool | Description |
|---|---|
sliver_generate |
Generate implant (mTLS, HTTP/S, DNS, WireGuard) |
sliver_listeners |
Manage C2 listeners |
sliver_sessions |
List/interact with sessions |
sliver_beacons |
List/interact with beacons |
sliver_execute |
Execute commands on implant |
sliver_upload |
Upload files to implant |
sliver_download |
Download files from implant |
sliver_pivot |
Pivot setup and management |
sliver_portfwd |
Port forwarding through implant |
| Tool | Description |
|---|---|
chisel_server |
Start Chisel reverse tunnel endpoint |
chisel_client |
Chisel client (forward/reverse/SOCKS) |
ligolo_start |
Start Ligolo-ng proxy server |
ligolo_route |
Add routes through Ligolo tunnel |
ligolo_listener |
Manage Ligolo listeners on agent |
| Tool | Description |
|---|---|
hashcat_crack |
Crack hashes (dictionary, rules, masks) |
hashcat_benchmark |
Benchmark hash types |
hashcat_show |
Show cracked from potfile |
john_crack |
Crack passwords with John the Ripper |
john_show |
Show cracked passwords |
john_formats |
List supported hash formats (graceful degradation on core john builds) |
hydra_attack |
| Tool | Description |
|---|---|
cme_smb |
SMB enumeration/exec/pass-the-hash |
cme_winrm |
WinRM execution |
cme_ldap |
LDAP enumeration |
cme_mssql |
MSSQL queries/execution |
cme_ssh |
SSH credential spray/exec |
| Tool | Description |
|---|---|
certipy_find |
Find AD CS templates and CAs |
certipy_req |
Request certificates |
certipy_auth |
Authenticate via PKINIT |
certipy_shadow |
Shadow credentials attack |
certipy_forge |
Golden certificate forgery |
certipy_relay |
NTLM relay to AD CS |
| Tool | Description |
|---|---|
tshark_capture |
Capture packets (interface, filter, duration) |
tshark_read |
Read pcap with display filter |
tshark_stats |
Protocol statistics |
tshark_extract |
Extract fields from captures |
tshark_follow |
Follow TCP/UDP/HTTP streams |
| Tool | Description |
|---|---|
| 69 tools | eBPF-powered Kubernetes offensive toolkit, including managed C2-server lifecycle ( kubedagger_server_* ) — seetools/kubedagger/ |
| Tool | Description |
|---|---|
barzakh_list |
List available UEFI bootkit payload types (134 payloads) |
barzakh_generate |
Generate a UEFI bootkit payload for adversary simulation (x86-64, aarch64, riscv64, power64, armv7) |
barzakh_corpus |
Generate full test corpus of malicious/clean firmware pairs |
barzakh_scan |
Scan firmware images with 138 specialized bootkit detectors (exit 1 = detection, reported as a finding) |
barzakh_validate |
Validate detector coverage (TPR/FPR) against a corpus |
barzakh_scenario |
List/show/run multi-stage ATT&CK-mapped attack chains (6 bundled) |
barzakh_robustness |
Mutation-based detector evasion benchmark |
barzakh_detectors |
List/describe the 138 detectors |
barzakh_baseline |
Create/show firmware baselines |
barzakh_intel |
Query IOC feeds, MISP, ATT&CK, STIX intel |
barzakh_server_start |
Start the Barzakh REST scanner server as a managed background daemon (X-API-Key auth, SQLite history) |
barzakh_server_stop |
Stop a managed Barzakh server |
barzakh_server_status |
Status of managed Barzakh servers |
barzakh_inject |
|
barzakh_esp_build |
|
barzakh_qemu |
Boot firmware in an emulated QEMU/OVMF environment (run config / memory dump) |
barzakh_range |
Boot QEMU/OVMF/swTPM cyber-range suites with assert-on-detection (exit 1 = failed assertions, surfaced as a result) |
barzakh_fuzz |
Protocol-aware firmware fuzzing (dhcpv6/bmp/capsule/httpboot/tcg/hii) |
barzakh_history |
Query the SQLite scan history (list/show/diff/trends) |
barzakh_kb |
Manage the firmware knowledge base (add/list/search/export/import) |
barzakh_ml |
Train/evaluate the ML anomaly models |
barzakh_manifest_validate |
Validate detectors against a labeled corpus manifest with train/validation/test splits |
barzakh_info |
Scanner build/version, detector, architecture, and format metadata |
barzakh_report |
Generate report files (json/html/markdown/sarif/csv/ecs/cef/leef) from scan results |
Wraps StaticZero, an eBPF telecom security research framework (2G–5G, satellite, baseband firmware). Authorized lab use only.
Warning
telecom_offense_start launches the **telecom interception research **
(features F89–F172: IMSI targeting, GTP/SS7/Diameter interception, RF and
satellite domains). Intercepting telecommunications is illegal without
authorization in most jurisdictions — including in labs in some. Use only
in an isolated lab environment with equipment you own, and only where you
hold explicit permission. The tool is approval-gated and requires root.
| Tool | Description |
|---|---|
telecom_doctor |
Read-only StaticZero defense engine preflight (platform/kernel/BPF checks) |
telecom_firmware_analyze |
Static vulnerability analysis of modem/baseband firmware images |
telecom_firmware_extract |
Extract firmware components with manifest |
telecom_firmware_diff |
Diff two firmware images |
telecom_firmware_nv_dump |
Extract NV items (carrier/band/security config) |
telecom_firmware_patch |
|
telecom_firmware_monitor |
Monitor a modem device protocol stream (diag/at/qmi/mbim; bounded by the execution timeout) |
telecom_sdr_scan |
Receive-only LTE cell scan via SDR |
telecom_sdr_capture |
Receive-only IQ capture via SDR |
telecom_sdr_inject |
|
telecom_sdr_relay |
|
telecom_sdr_selftest |
|
telecom_threats |
Correlated threats from a running defense engine |
telecom_alerts |
Raw alerts from a running defense engine |
telecom_stats |
Engine runtime statistics |
telecom_defense_start |
Start the StaticZero defense engine as a managed background daemon (JSON alerts stream to a file) |
telecom_defense_stop |
Stop a managed defense engine daemon |
telecom_defense_status |
Status of managed defense engine daemons |
telecom_offense_start |
|
telecom_offense_stop |
Stop a managed offense daemon |
telecom_offense_status |
Status of managed offense daemons |
telecom_correlator_start |
Start the protocol correlator as a managed daemon (JSON-lines event ingest on loopback) |
telecom_correlator_stop |
Stop a managed correlator daemon |
telecom_correlator_status |
Status of managed correlator daemons |
StaticZero binaries ship as checksum-verified release assets (v0.5.0, Linux
only, eBPF embedded) and install via the telecom profile:
sudo make install-tools PROFILE=telecom.
Wraps Aegis-Shadow, a Linux eBPF security research platform (educational, VM-only).
The analysis tools never execute binaries; aegis_offense_plan renders the manual
lab command with risk notes without executing it. The defense engine and the
offense rootkit can each be started as a managed background daemon
(approval-gated).
[!DANGER]
aegis_offense_start launches the **eBPF rootkit ** — process hiding,
credential harvesting, log tampering, anti-forensics, and covert C2, running
at kernel level with root. Two capabilities are destructive by design:
deadman_switch (scorched-earth wipe triggered on tamper or timeout — the
VM may become unrecoverable) and wipe_bytecode (erases the loaded eBPF
bytecode from memory, defeating live forensics). Both are opt-in, emit loud
warnings in every result, and expect a disposable, snapshotted lab VM.
Data loss from these flags is expected behavior, not a bug.
| Tool | Description |
|---|---|
aegis_alerts_parse |
Parse/filter NDJSON alert files from the defense engine |
aegis_alerts_tail |
Tail the last N alerts from an alert file |
aegis_modules_list |
Reference catalog of defense modules and offense features |
aegis_defense_config_read |
Read/validate a defense engine hot-reload config |
aegis_defense_config_write |
Write a defense engine config (threshold, window) |
aegis_offense_plan |
Dry-run planner: validate offense feature flags and render the exact manual command line with risk notes |
aegis_defense_start |
Start the Aegis-Shadow defense engine as a managed background daemon (NDJSON alerts stream to a file; requires root) |
aegis_defense_stop |
Stop a managed defense engine daemon |
aegis_defense_status |
Status of managed defense engine daemons |
aegis_offense_start |
☠️ Launch the Aegis-Shadow eBPF rootkit as a managed daemon (destructive deadman_switch /wipe_bytecode are opt-in with warnings; disposable VM only) |
aegis_offense_stop |
Stop a managed offense daemon |
aegis_offense_status |
Status of managed offense daemons |
aegis_c2_send |
☠️ Send a command to a running rootkit over its encrypted C2 channel (hide/unhide pid, obfuscate file, exfil, kill-switch; fire-and-forget; requires the aegis-c2 CLI from Aegis-Shadow ≥ v0.2.0) |
ExploitDB search and CVE enrichment — the connective tissue from scanner findings
to Metasploit: nuclei finding → CVE → public exploits → msf_module_run.
Requires the searchsploit binary + on-disk database (distro exploitdb
package — manual install, not checksum-pinnable).
| Tool | Description |
|---|---|
searchsploit_search |
Search ExploitDB by keyword (JSON results) |
searchsploit_cve |
Map a CVE ID to public exploits (EDB-IDs, paths) |
searchsploit_edb |
Look up an EDB-ID (exploit file path + copy hint) |
cve_enrich |
Extract CVEs from scanner output text, map to exploits, and suggest Metasploit modules (EDB .rb paths →msf_module_run candidates) |
Generic embedded firmware analysis with binwalk v2 — completes the firmware
story alongside UEFI (Barzakh) and baseband (modem-firmware). Requires
binwalk (v2, Python — distro/pip manual install; do not use the v3 Rust
rewrite, different CLI).
| Tool | Description |
|---|---|
firmware_scan |
Signature scan with a structured JSON summary (signatures → record rules) |
firmware_extract |
|
firmware_entropy |
Entropy analysis (encrypted/compressed region detection) |
SBOM generation and vulnerability scanning with Anchore syft + grype —
checksum-pinned releases, installed by the sbom profile.
| Tool | Description |
|---|---|
sbom_generate |
Generate an SBOM (syft-json, CycloneDX, SPDX) from an image, directory, or file |
vuln_scan_image |
Vulnerability-scan an image or SBOM with grype (exit-2-on-findings surfaced as a result with a severity summary) |
vuln_scan_sbom |
Scan an existing SBOM file with grype |
Wraps Metasploit Framework (msfvenom + msfconsole). Console tools generate
validated .rc resource files from structured parameters — never
msfconsole -x — so console-command injection is structurally impossible.
Metasploit is not checksum-pinnable (no stable release binaries); install it
manually (apt install metasploit-framework on Kali/Parrot) and the allowlist
discovers msfconsole/ msfvenom on PATH. msfconsole startup takes 10–60s;
runs are bounded by REDHANDS_TIMEOUT.
| Tool | Description |
|---|---|
msf_payload_generate |
|
msf_payload_list |
List payloads, encoders, formats, platforms, or architectures |
msf_module_info |
Show module documentation (exploit/auxiliary/post/payload/encoder/nop) |
msf_module_run |
|
msf_resource_run |
.rc resource file (arbitrary console commands; approval-gated) |
| Tool | Description |
|---|---|
redhands_health |
Server health check and binary dependency status |
RedHands supports four transport modes:
| Transport | Use Case | Config |
|---|---|---|
| stdio (default) | Local use with Claude Code, Cursor, VS Code | REDHANDS_TRANSPORT=stdio |
| HTTP (Streamable) | Modern MCP 2026-07-28 clients: stateless single-endpoint JSON-RPC |
REDHANDS_TRANSPORT=http (REDHANDS_HTTP_ADDR , default127.0.0.1:8082 ;REDHANDS_HTTP_PATH , default/mcp ) |
| SSE | Legacy HTTP clients (deprecated-era transport) | REDHANDS_TRANSPORT=sse (loopback by default) |
| WebSocket | Real-time bidirectional (custom transport) | REDHANDS_TRANSPORT=ws (loopback by default) |
The HTTP transport implements the modern spec: POST-only /mcp endpoint,
required MCP-Protocol-Version/ Mcp-Method (and Mcp-Name for tools/call)
headers validated against the body, 405 for GET/DELETE, 404+-32601 for
unknown methods, 202 for notifications, and per-request _meta protocol
version validation (-32022 with the supported list). Legacy clients that
open with initialize are served by the dual-era handshake on every
transport. Non-loopback listeners require authentication and TLS.
| Mode | Config | Description |
|---|---|---|
| none (default) | REDHANDS_AUTH=none |
No authentication |
| API key | REDHANDS_AUTH=apikey +REDHANDS_API_KEY=<secret> |
X-API-Key header validation |
| mTLS | REDHANDS_AUTH=mtls + cert/key/CA env vars |
Client certificate verification |
Chain tools sequentially with variable substitution:
{
"method": "workflow/run",
"params": {
"steps": [
{"tool": "nmap_port_scan", "params": {"target": "10.0.0.1"}},
{"tool": "nuclei_scan", "params": {"target": "http://10.0.0.1"}}
]
}
}
Variables: $prev.text (previous step output), $step[N].text (Nth step output).
Substitutions contain the full formatted text, not extracted targets; only pass them
to parameters that accept that format. All steps honor dry-run, rate limits and scope.
Campaigns extend workflows with conditions, parallel fan-out, failure
recovery (goto:), checkpointing, resume, cancellation, and
playbook templates. Every step executes through the same middleware chain
as a direct tools/call (audit, auth, rate limits, scope, dry-run, approval,
concurrency limits).
{
"method": "campaign/run",
"params": {
"id": "web-engage-1",
"name": "Web assessment",
"steps": [
{"id": "portscan", "tool": "nmap_port_scan", "params": {"target": "10.0.0.1"}},
{"id": "webscan", "tool": "nuclei_scan", "params": {"target": "http://10.0.0.1"}},
{"parallel": [
{"id": "nikto", "tool": "nikto_scan", "params": {"target": "http://10.0.0.1"}},
{"id": "tls", "tool": "testssl_scan", "params": {"target": "10.0.0.1:443"}}
], "on_fail": "continue"},
{"id": "report", "tool": "loot_add", "params": {"description": "done"}}
]
}
}
| Field | Description |
|---|---|
id |
Unique step identifier used by conditions and ${id...} references |
tool /params |
Tool name and JSON params (exactly like tools/call ) |
parallel |
Array of sub-steps executed concurrently (≤16, no nesting) |
condition |
Skip the step unless the condition holds |
on_fail |
stop (default),continue , orgoto:<step_id> recovery jump |
{"condition": {"step": "portscan", "op": "json_gt", "path": "hosts.0.ports.0", "value": "0"}}
Operators: success, failure, contains, not_contains, equals, not_equals,
plus JSON-aware json_exists, json_not_exists, json_equals, json_not_equals,
json_contains, json_gt, json_lt evaluated against a previous step's structured
output (step may be a step ID, "$prev", or a zero-based index).
Placeholders can extract fields from prior results, not just whole text. Tools emitting JSON (raw or in a markdown ``` json fence) are queryable by dot-path with numeric array indices:
$prev.text full text of the previous step
$prev.json.findings.0.severity field extraction
$step[2].json.hosts whole array from step 2 (native type preserved)
${portscan.json.hosts.0.ip} reference by step ID
A placeholder that makes up an entire string value is substituted natively (numbers stay numbers, arrays stay arrays); placeholders embedded in a larger string are substituted as text. Unresolvable placeholders pass through untouched.
record rules map JSON arrays in a step's output to target database entries,
so campaigns build the host/service/credential/finding picture automatically:
{
"id": "discover",
"tool": "kubedagger_k8s_discover",
"params": {"namespace": "default"},
"mitre": ["T1613"],
"record": [
{"type": "host", "path": "nodes", "fields": {"ip": "ip", "hostname": "name", "os": "os"}},
{"type": "host", "path": "pods", "fields": {"ip": "pod_ip", "hostname": "name"}}
]
}
Entry types: host, service, credential, finding. Recorded entries are
tagged campaign:<workflow_id> and become queryable via the targetdb_* tools
and the attack graph. Recording failures warn in the step result without
failing the campaign.
Steps accept mitre: ["T1613", "T1552.001"] technique tags. Campaign results
aggregate the unique techniques of executed steps as mitre_techniques, which
can be passed to kubedagger_detection (action: "generate", techniques as
a comma-separated list) to produce matching Falco/Sigma/audit detection rules.
The shipped k8s-recon-chain playbook demonstrates recording and tagging end
to end, including a final detection-rule generation step.
| Method | Params | Description |
|---|---|---|
campaign/run |
steps or template +vars |
Execute a campaign synchronously; checkpoints every step |
campaign/start |
same as campaign/run |
Async : with the Tasks extension declared, returns aCreateTaskResult (taskId, ttlMs, pollIntervalMs) and runs detached from the request; without it, identical tocampaign/run . Idempotent: re-starting a running ID returns the live task |
campaign/status |
workflow_id |
Load persisted state |
campaign/list |
— | List all saved campaigns |
campaign/resume |
workflow_id |
Continue a d/cancelled campaign |
campaign/cancel |
workflow_id |
Cancel a running campaign (works cross-replica: the engine re-reads persisted state at each checkpoint) |
campaign/templates |
— | List playbook templates |
campaign/template/get |
name |
Fetch one template |
campaign/template/save |
template JSON | Validate and persist a playbook |
tasks/get |
taskId |
Poll a task: working /input_required /completed /failed /cancelled ; terminal states carry the result or error |
tasks/cancel |
taskId |
Cooperatively cancel a task (acks immediately) |
tasks/update |
taskId ,inputResponses |
Submit approval responses for a d task ( approval for a sequential gate,approval-<sub_step_id> per gated sub-step inside a parallel group; unknown keys are acked and ignored) |
Campaigns interrupted mid-step (cancellation or timeout) checkpoint as
d and re-run the interrupted step on resume. Every campaign is
single-writer (an advisory flock per workflow ID): a duplicate start of a
running campaign returns its live task instead of corrupting the checkpoint.
Tools that require operator approval (high-impact tools not listed in
REDHANDS_APPROVED_TOOLS) no longer need to block. When the request declares
the Tasks extension, an approval-gated tools/call answers with
resultType: "input_required" and an elicitation/create approval request;
the client re-issues the same call with inputResponses: {"approval": {"approved": true}} and the single call executes (audited as an approval
bypass). Inside a task-tracked campaign, an approval-gated step s the
task (input_required via tasks/get), the response arrives through
tasks/update, and campaign/resume re-runs the step. Requests without the
capability fail closed with the classic message. Responses never ride in
tool arguments — only in the dedicated inputResponses field.
Parallel groups use the same machinery per sub-step instead of failing the
gated sub-step outright: when one or more sub-steps of a group trip approval
gates, the campaign s ONCE and tasks/get shows one inputRequest per
gated sub-step, keyed approval-<sub_step_id>. Answer each individually
(tasks/update with inputResponses: {"approval-<sub_step_id>": {"approved": true}}; unknown or stale keys are acked and ignored). On
campaign/resume the group replays partially: sub-steps that already
completed are not re-executed, sub-steps that failed for other reasons keep
their recorded failure, approved sub-steps re-run once with their own
one-call bypass, denied sub-steps fail with approval denied and flow
through the group's on_fail (stop/continue/goto), and unanswered gates
re-trip with fresh request ids. A goto that re-enters a group is a fresh
execution — the per-run completion set resets and every sub-step re-runs.
Capability-less requests keep the classic fail-closed behavior for group
sub-steps too.
Playbooks are reusable, versioned campaign templates stored as JSON in
REDHANDS_WORKFLOW_TEMPLATES_DIR (default ./playbooks/). Shipped examples:
recon-to-vuln, web-assessment, k8s-recon-chain, ad-kerberoast-chain,
firmware-bootkit-assessment, telecom-defense-monitoring,
firmware-purple-team. Point-and-shoot a playbook:
{
"method": "campaign/run",
"params": {
"template": "recon-to-vuln",
"vars": {"domain": "target.example", "severity": "critical,high"}
}
}
Campaigns can bind to an engagement with the engagement parameter. The named
engagement must exist and be the active engagement — the campaign fails
fast instead of silently running against the wrong scope, and every step's
targets are checked against that engagement's scope by the middleware:
{
"method": "campaign/run",
"params": {
"engagement": "lab-1",
"template": "web-assessment",
"vars": {"target": "https://target.example"}
}
}
Bindings are persisted with the campaign state and re-validated on resume, so switching engagements between runs rejects a stale binding.
Limits: 128 steps per campaign, 16 steps per parallel group, 64 goto recovery
jumps, 16 MiB cumulative output — mirroring workflow hard caps.
Built-in parsers for common tool output formats:
- Nmap XML — parses
-oXoutput into structured hosts/ports/services - Nuclei JSON — parses JSONL findings into severity-grouped results
- Masscan JSON — parses
-oJoutput into host/port pairs
Generate reports from workflow results:
{
"method": "report/generate",
"params": {
"format": "markdown",
"title": "Assessment report",
"sections": [{"title": "Summary", "tool": "nmap_port_scan", "content": "Reviewed scan results"}]
}
}
Formats: markdown (.md) and html (self-contained with inline CSS).
Define custom tools via JSON without writing Go:
{
"name": "custom_quick_scan",
"description": "Quick nmap top-100 scan",
"binary": "nmap",
"args_template": ["-sS", "--top-ports", "100", "{{.target}}"],
"input_schema": {
"type": "object",
"properties": {
"target": {"type": "string", "description": "Target IP or hostname"}
},
"required": ["target"]
}
}
Drop JSON files in REDHANDS_PLUGINS_DIR (default ./plugins/) and they appear in tools/list automatically.
All settings via environment variables:
| Variable | Default | Description |
|---|---|---|
REDHANDS_TOOLSETS |
(all) | Comma-separated toolsets to register (approval still applies) |
REDHANDS_TIMEOUT |
5m |
Execution timeout per tool call |
REDHANDS_MAX_OUTPUT |
10485760 |
Max output bytes per execution (10MB) |
REDHANDS_RATE_LIMIT |
10 |
Token bucket refill rate (requests/sec) |
REDHANDS_RATE_BURST |
20 |
Token bucket burst capacity |
REDHANDS_CACHE_TTL |
5m |
Result cache TTL |
REDHANDS_CACHE_SIZE |
100 |
Max pure-function results (oldest-expiry eviction) |
REDHANDS_AUDIT_FILE |
audit.jsonl |
Audit log file path |
REDHANDS_TRANSPORT |
stdio |
Transport mode: stdio ,sse , orws |
REDHANDS_SSE_ADDR |
127.0.0.1:8080 |
SSE HTTP listen address |
REDHANDS_WS_ADDR |
127.0.0.1:8081 |
WebSocket listen address |
REDHANDS_AUTH |
none |
Auth mode: none ,apikey , ormtls |
REDHANDS_API_KEY |
— | API key (required when auth=apikey) |
REDHANDS_TLS_CERT |
— | TLS certificate path |
REDHANDS_TLS_KEY |
— | TLS private key path |
REDHANDS_TLS_CA |
— | CA certificate for mTLS client verification |
REDHANDS_PLUGINS_DIR |
./plugins |
Directory for JSON plugin definitions |
REDHANDS_PLUGIN_POLL_INTERVAL |
5s |
Plugin directory polling interval |
REDHANDS_CREDSTORE_KEY |
— | Strong encryption key required for credential persistence |
REDHANDS_CREDSTORE_FILE |
credstore.enc |
Encrypted credential store file |
REDHANDS_TARGETDB_FILE |
targetdb.json |
Target database file |
REDHANDS_TARGETDB_AUTOSAVE |
true |
Persist target database changes immediately |
REDHANDS_ENGAGEMENT_DIR |
engagements |
Engagement metadata directory |
REDHANDS_DRY_RUN |
false |
Preview executions without running tools |
REDHANDS_OPSEC_PROFILE |
— | OPSEC profile name (parsed; proxy routing not yet wired) |
REDHANDS_IOC_FILE |
— | Indicator-of-compromise list file (parsed; matching not yet wired) |
REDHANDS_TCP_ADDR |
— | TCP transport listen address; mTLS only (API-key headers are not supported over TCP) |
REDHANDS_DASHBOARD_ADDR |
— | Dashboard HTTP listen address (parsed; dashboard not yet wired) |
REDHANDS_GOPHISH_URL |
— | GoPhish API base URL (enables the phishing toolset) |
REDHANDS_GOPHISH_API_KEY |
— | GoPhish API key |
REDHANDS_MAX_CONCURRENT |
4 |
Maximum active tool calls across clients and workflows |
REDHANDS_MAX_QUEUED |
16 |
Maximum queued calls; further requests fail without executing |
REDHANDS_APPROVED_TOOLS |
— | Exact comma-separated names approved by the operator for high-impact/unclassified tools |
REDHANDS_WORKFLOW_TIMEOUT |
30m |
Total deadline for a sequential workflow or campaign |
REDHANDS_WORKFLOW_STATE_DIR |
workflow_state |
Campaign checkpoint directory |
REDHANDS_WORKFLOW_TEMPLATES_DIR |
playbooks |
Playbook template directory |
REDHANDS_WORKFLOW_MAX_PARALLEL |
4 |
Max concurrent steps inside a campaign parallel group (≤32) |
REDHANDS_EVIDENCE_DIR |
evidence |
Artifact directory with persistent metadata index |
REDHANDS_LOOT_DIR |
loot |
Directory containing the atomic loot.json store |
Non-loopback listeners require authentication and TLS. API keys are accepted only
in the X-API-Key header. stdio uses local OS access controls, not API-key authentication.
Caching is limited to pure MITRE/risk-score lookups, never scans or state-changing tools.
High-impact, mixed-mode, and unclassified tools (including plugins) require explicit
operator approval via REDHANDS_APPROVED_TOOLS. Dry-run previews do not require this
approval and never execute tools. tools/list publishes policy metadata and MCP safety hints.
See security and deployment notes for HTTPS/mTLS setup,
scope limitations, credential migration, and remaining release work.
Evidence metadata persists in REDHANDS_EVIDENCE_DIR/index.json. Artifacts use random
IDs rather than host/tool text in filenames, and are checked against recorded size and
SHA-256 values when the store opens. Old files with no index remain untouched and are
not automatically listed. Corrupt metadata or missing/changed indexed artifacts stop
startup instead of silently losing their history.
Loot writes are atomic, and malformed loot.json files fail startup. Both stores are
single-process stores: do not point multiple servers at the same directories. Artifacts
and loot are private files, not encrypted storage; protect their directories and backups.
evidence_screenshot requires operator approval and cutycapt installed on PATH at
startup. It uses the shared executor, private staging, HTTP(S)-only URLs, and PNG
validation. CutyCapt may require a display/headless environment; it is not bundled in
the current Docker image. Browser redirects and subresources still need network isolation.
See evidence and loot recovery before moving stores, repairing corrupt files, or handling legacy/unindexed artifacts.
Enable only specific toolsets:
export REDHANDS_TOOLSETS=nmap,impacket,crackmapexec,certipy,sliver,crack
export REDHANDS_TOOLSETS=nmap,recon,web,fuzz,scan,vuln
export REDHANDS_TOOLSETS=nmap,scan,tshark,tunnel
export REDHANDS_TOOLSETS=barzakh,telecom,aegis
export REDHANDS_TOOLSETS=
- Binary allowlist — only known security tools can be executed
- Argument validation — shell metacharacter rejection for external-tool arguments
- Target validation and scope — explicit network targets checked against active engagement scope; not a complete network sandbox
- Destructive-capability posture — tools that can destroy data or destroy
a machine (
aegis_offense_startwithdeadman_switchorwipe_bytecode) are opt-in, approval-gated, and emit loud warnings in every result; read the warnings in theAegis-Shadow andTelecom sections before using the offense tools - Output size cap — 10MB per execution
- Execution timeout — configurable per tool call
- Rate limiting — token bucket prevents abuse
- Authentication — API key or mTLS for remote transports
- Audit logging — direct calls and workflow steps logged as JSONL; logging failures reported to stderr
make build # Build binary to bin/
make test # Run tests with race detector
make lint # Run golangci-lint
make docker # Build Docker image
make install # Install binary to /usr/local/bin
make install-tools # Install security tools (PROFILE=minimal|web|network|ad|k8s|uefi|telecom|aegis|sbom|recon|all)
make check-deps # Check which tools are available
Additional regression checks:
go vet ./...
python3 -m unittest discover -s scripts -p 'test_*.py'
go run golang.org/x/vuln/cmd/govulncheck@v1.9.0 ./...
The wrapper unit tests build argv strings but verify them only against a
MockExecutor. When an upstream binary changes its CLI, nothing catches it —
the tools/livecheck suite does. It runs the real binaries and asserts
that the flags, subcommands and enum values the wrappers emit are still
accepted (help-text assertions plus safe dry-parses terminated by
--help/--echo; nothing scans a target or mutates state).
make test-live
Notes:
The suite is gated behind the livebin build tag, sogo test ./... and
the normal CI jobs are unaffected. #
Binaries are discovered via PATH ; a binary that is not installed is
reported asSKIP ... binary not on PATH , never as a failure. The suite is
safe to run on any machine. #
A check can also skip with a KNOWN CLI DRIFT /SKIPPED (distro quirk) message when the pinned or distro binary genuinely rejects the wrapper's
argv (e.g. Ubuntu ships core john without the jumbo-only--list , and a
broken whatweb package). Those skips are deliberate, printed in every run,
and must lead to a wrapper fix or a dependency bump — never to deleting the
check. #
In the Docker image (which ships ~25 pinned binaries but no Go toolchain), compile the suite on the host and run it inside the container, where the binaries live:
CGO_ENABLED=0 go test -tags=livebin -c -o livecheck.test ./tools/livecheck/
docker run --rm --network none -v "$PWD/livecheck.test:/usr/local/bin/livecheck.test:ro" \
--entrypoint /usr/local/bin/livecheck.test redhands
The live-binaries CI job instead installs a minimal binary set on
ubuntu-latest (apt plus the checksum-verified pinned releases viascripts/dependencies.py ) and runsgo test -tags=livebin directly. #
When you change a wrapper's argv, update the matching entry in tools/livecheck/table.go so drift detection stays meaningful.
Tier 1 (tools/livecheck) proves binaries still accept the wrappers' argv.
Tier 2 (testdata/ golden fixtures) proves output shapes. The
tools/e2e suite (Tier 3) proves real execution: it drives JSON-RPC
tools/call and campaign/run requests into a fully wired in-process
server — the same BinaryExecutor (allowlist from binaries.Discover()),
the same middleware chain (audit to a temp file, rate limits, engagement
scope, approval gates, concurrency limiter), the same registry with builtin
tool policies, and the same campaign recorder as cmd/redhands — and
asserts on what the real binaries actually did.
make test-e2e
Scenarios (all skip cleanly when their binary is missing):
- Barzakh purple-team loop (offline, deterministic) —
barzakh_corpusgenerates a corpus into a temp dir,barzakh_scanscans one malicious sample (scanner exit 1 = bootkit detected must surface as aresult with the severity summary rendered),barzakh_validatevalidates the corpus. - Campaign with real execution and record rules — a
campaign/runwhose steps execute the real corpus+scan tools through the middleware chain; the scan step's finding record rule persists the scanner's realfindingsarray into the target database, asserted via the store. - nmap against a test-owned listener — the suite opens a
127.0.0.1:0listener and runsnmap_port_scan(connect scan; SYN needs root) andnmap_service_detectagainst that exact port. - nuclei with a test-authored template — a minimal
http-protocol template (static word matcher on a body the local test server always serves, severityinfo) is written to a temp file and passed via thetemplatesparameter; nothing is downloaded. - telecom_doctor —
staticzero-defense --doctor --json: asserts the DoctorReport renders with a checks array and theebpf_objectcheck passes (the release binary embeds the eBPF object); privilege failures are expected and fine. - telecom_firmware_analyze — a synthetic modem image (documented
fixture generator with a fixed seed for the padding) analyzed with
modem-firmware analyze; asserts the vulnerability report, its findings (strcpy, weak ciphers, debug interfaces), and the written report file. - kubedagger_k8s_discover against no cluster — only asserts the call
returns a well-formed response without hanging (the wrapper's
empty-stdout handling is intentionally not pinned).
HOMEis redirected so the client cannot pick up a developer's real kubeconfig.
Safety rules enforced by the suite:
- The only network targets are
127.0.0.1/localhost listeners the test itself owns; there is no network egress beyond the local binaries' own startup. - Everything deterministic:
:0listeners, fixed seeds for synthetic inputs, temp dirs, bounded polling (no sleeps) for the async audit log. - Gated behind the
livee2ebuild tag, sogo test ./...and the normal CI jobs are unaffected. - Executing tools are pre-approved in the harness config exactly like a
reviewed
REDHANDS_APPROVED_TOOLSsetup, so the approval middleware itself is exercised (not bypassed).
The live-e2e CI job installs the scenario binaries (nmap via apt; nuclei
via the pinned go install; barzakh, StaticZero, and KubeDagger binaries
via the checksum-verified pinned releases in scripts/dependencies.json)
and runs go test -tags=livee2e ./tools/e2e/.... When you add a scenario
that needs a new binary, add it to that job's install steps.
docker build -t redhands .
docker run -i redhands
docker compose run --rm -i redhands
docker compose --profile secure up redhands-secure
The Docker image includes a subset of supported security tools (check dependencies before use; some tools require additional installation or privileges):
- apt/source : nmap, masscan, tshark, hashcat, john, nikto, whatweb
- pip : impacket, certipy-ad
- Go tools : subfinder, httpx, nuclei, ffuf, katana, gobuster, gau, waybackurls
- Releases : chisel, ligolo-ng, kubedagger (client, operator, server v0.5.0), barzakh (adversary, scanner, server v0.7.1), staticzero (defense, modem-firmware, sdr-bridge v0.5.0), aegis-shadow-defense (v0.2.0)
MIT
RedHands is provided for authorized security testing, education, and research purposes only. You are solely responsible for ensuring you have explicit, written permission before using this tool against any system, network, or application. Unauthorized access to computer systems is illegal and punishable under applicable law including the Computer Fraud and Abuse Act (CFAA), Computer Misuse Act, and equivalent legislation worldwide.
The authors and contributors of RedHands accept no liability for misuse or damage caused by this software. Use responsibly and ethically.