- _prompt() clamps non-positive tuning values to default with operator warning
(HIGH from AUDIT_SEC_989: prevents garbage display when vars file has
masscan_rate: -100 or nuclei_concurrency: 0)
- Remove empty 'if scan_mode in (...): pass' placeholder left from refactor
(P3 from AUDIT_CODE_989)
- Add AUDIT_CODE/SEC/ENV_989.md and FIXES_989.md with skip justifications
(path traversal not applicable to single-operator threat model;
Tor wording already adequate; estimate disclosure interactive-only)
Estimator rewrite:
- Phase-decomposed estimate_scan_hours: masscan, nmap-only, nmap-fingerprint,
probe, nuclei phases each timed separately
- Accepts 6 tuning params (nmap_timing, nmap_workers, nuclei_rate,
nuclei_concurrency, hit_rate, use_tor) — was previously ignoring all of them
- nmap-only mode fixed: was underestimated ~100x (treated nmap like packet
scanning); now uses NMAP_TIME_BY_TIMING table per timing template
- masscan+nuclei mode now properly modeled (was falling through to
masscan-only formula, missing the nuclei phase entirely)
- Tor multiplier scoped per-phase: applied to nmap/nuclei/probe only.
Masscan uses raw sockets and Tor cannot proxy raw sockets — was
previously applying Tor multiplier to masscan rate (incorrect)
- 5min/node provisioning overhead added (matters vs Linode 1h billing min)
- build_estimate_table accepts tuning dict, threads through to engine
Operator-facing changes (deploy_webrunner.py):
- Tuning section moved BEFORE estimate display so numbers reflect actual
operator choices, not defaults
- Estimate header now shows active tuning values when set
- Caveats block printed below estimate explaining empirical limits
(hit rate range, nuclei template variance, nmap timing tradeoffs,
provisioning, billing minimums)
- CRITICAL OPSEC banner displayed when Tor enabled with masscan modes:
loud red warning that masscan SYN packets bypass Tor and reveal cloud
node IP. Includes confirmation gate — operator must affirm they
understand before proceeding. Suggests nmap-only mode for full Tor
coverage if attribution risk is unacceptable
Why: operator was getting estimates off by 100x for nmap-only and 0%
accurate for masscan+nuclei. Tuning controls were invisible to the
estimator. Tor + masscan was silently broken-by-design (raw sockets
bypass proxychains) without operator awareness.
- New scan mode: masscan+nuclei — masscan finds open ip:port pairs,
nuclei runs operator-supplied CVE template against discovered hosts
- Per-country vulnerable host count in merge output via CIDR→country lookup
- Tuning args on every scan: --nmap-timing, --nmap-timeout, --nmap-workers,
--nuclei-rate, --nuclei-concurrency, --nuclei-timeout, --masscan-rate
- Vars file support: operator provides scan_vars.yaml to pre-fill all
tuning settings without interactive prompts
- Templates copied to nodes at provision time — no live fetches (OPSEC)
- run_scan.yml passes all tuning args with Ansible defaults as fallback
- configure_node.yml installs nuclei binary + uploads template on demand
- Updated deployment summary shows mode-specific tuning params
- countries-format.md and targets-format.md updated for new capabilities
- scan_vars.yaml.example documents all configurable settings with comments
estimate_scan_hours() was pure masscan math — zero nmap time modeled.
For masscan+nmap and geo-scout modes with 10K masscan hits, actual nmap
time dominates but was completely invisible in the estimate.
Added nmap phase calculation using empirical constants:
- NMAP_TIME_PER_HOST_SEC = 10 (nmap -sV -T4 per host)
- MASSCAN_HIT_RATE = 0.01 (fraction of IPs with open ports)
- _NMAP_WORKERS = 10 (matches WEBRUNNER_NMAP_WORKERS default)
Also fixed build_estimate_table() partial-chunk overestimation: last
chunk was billed at full preset size regardless of actual IP count,
causing 1.5-2x cost inflation for non-aligned scan ranges.
scan_masscan_nmap() and scan_geo_scout() were running nmap serially —
one host at a time with a 60s timeout, turning a 1h estimate into 24h+
for large scans.
Both modes now use ThreadPoolExecutor with 10 workers (configurable via
WEBRUNNER_NMAP_WORKERS env var). geo-scout uses an inner scan_one()
helper to batch nmap + probe_service() per host within the same future.
Also fixed probe_service() bytes format antipattern and removed dead
targets_arg variable from scan_nmap_only().
run_scan.yml converted from command: > folded scalar to command: argv:
list form to prevent argument splitting on space-containing values.
Each node chunk now carries its own region field, assigned round-robin
across the full provider region list unless the user pinned a specific
region. AWS uses the same async/poll:0 pattern as Linode but with a
serial per-unique-region setup phase (key import + SG) before firing all
creates in parallel. Teardown uses hostvars ec2_region per node so
multi-region AWS teardowns hit the right endpoint. Infisical replaces
interactive credential prompts for both providers. Linode vars.yaml
instance type corrected to g6-nanode-1.
All Linode creates fire simultaneously via async/poll:0 loop instead of
serial include_tasks. One shared root pass + SSH key generated before
the loop eliminates per-node credential races. async_status collects
all create confirmations before add_host. Play 2 runs with strategy:free
and -f 50 so all nodes configure, scan, and collect results concurrently.
Per-node serial provision file retired — logic is now inline.
Show deployment option only when at least one provider has credentials
configured. Checks LINODE_TOKEN/FLOKINET_API_KEY via Infisical and
AWS creds via env. Displays setup instructions when nothing is found
rather than letting the user launch a deployment that will immediately
fail at provisioning.
create_extra_vars callers pass {**config} copies so linode_token never
landed in the original config dict. Pull provider credentials from the
environment (already set by set_provider_environment) so the temp-file
secret path works regardless of how config was constructed.
Passes linode_token and other secrets to Ansible through a 0o600 temp
file (@tmpfile) rather than the main --extra-vars JSON blob. Fixes
undefined variable errors for provision tasks that reference linode_token
as an Ansible variable, while keeping secrets out of process listings.
Temp file is removed in finally block regardless of outcome.
Also fixes absolute path for node_chunks_file and other logs-relative
paths so Ansible lookup('file', ...) resolves correctly from any
playbook directory.
Ansible does not support 'when' at the play level — moved
teardown_after_scan condition onto each individual teardown task.
Teardown Linode task now reads token from LINODE_TOKEN env var via
lookup('env') instead of the linode_token extra-var (which is now
filtered out of extra-vars by _SECRET_KEYS). AWS teardown removes
hardcoded aws_access_key/secret_key params — those are already set
as environment vars by set_provider_environment().
deployment_engine.py fallback for missing linode_token now calls
creds get LINODE_TOKEN homelab at runtime instead of loading from
providers/Linode/vars.yaml. Token has been moved to Infisical.
- providers/webrunner.yml: wrap loop_var under loop_control in all three
provider provision tasks — Ansible was rejecting loop_var as a conflicting
action statement alongside include_tasks
- deployment_engine.py: strip _SECRET_KEYS (tokens, passwords, api keys)
from create_extra_vars() — credentials are already set as env vars by
set_provider_environment(), no need to pass them through --extra-vars
where they end up in process listings and logs
- deployment_engine.py: remove debug log line that dumped the full
ansible-playbook command including all extra-vars JSON with live creds
- deployment_engine.py: create deployment log files with 0o600 permissions
instead of world-readable 0o644
- deploy_webrunner.py: remove load_vars_file() call that was pulling
phishing module creds (SMTP password, gophish port, domain) into the
webrunner config dict — all provider creds already collected by
gather_provider_config()
- deploy_webrunner.py: remove unused select_provider import
- .gitignore: narrow 'deployment*' glob to specific artifact patterns so
deployment_engine.py is no longer excluded from tracking
- Tor choice now appears before the estimate table so the displayed time/cost is accurate
- build_estimate_table accepts use_tor; applies 0.2x rate multiplier for nmap/probe modes
- masscan-only excluded from multiplier (masscan bypasses proxychains via raw sockets)
- Added per-mode warnings explaining which traffic actually routes through Tor
- Replace get_deployment_name_with_options() call with auto-derived wr-{deployment_id}; user already entered engagement name, second naming menu was noise
- Split run_scan.yml single command with conditional Jinja prefix into two when-gated tasks; empty-string prefix from use_tor=false caused Ansible to fail with return code 4
- deploy_webrunner.py: full rewrite following attack_box pattern exactly — auto-generate
engagement name when blank, select_provider+gather_provider_config per provider (handles
region selection), separate teardown_after_scan (default=yes) from enhanced_opsec,
use_tor prompt, bundled YAML defaults, setup_logging+confirm_action+show_naming_relationship
- utils/cidr_resolver.py: self-contained RIR delegation file downloader/parser — downloads
APNIC/ARIN/RIPE/LACNIC/AFRINIC, caches 24h in ~/.cache/c2itall/cidr/, no geo-scout dep
- utils/chunk_utils.py: remove geo-scout _try_load_geo_scout() dependency, use cidr_resolver
- modules/webrunner/inputs/countries.yaml: bundled default country list (no external dep)
- modules/webrunner/inputs/targets.yaml: bundled default scan targets and ports
- deploy.py: move WEBRUNNER from main menu (item 14) to tools submenu (item 10)
- configure_node.yml: install tor+proxychains4, start tor service, configure proxychains,
wait for circuit establishment when use_tor=true
- run_scan.yml: prefix command with proxychains4 when use_tor=true
- providers/webrunner.yml: add Play 4 teardown — Linode DELETE + AWS terminate-instances
using hostvars instance IDs stored during provisioning, conditional on teardown_after_scan
Multi-provider (Linode/AWS/FlokiNET) scan orchestration:
- Flatten all country CIDRs, chunk by IP count (sprint/balanced/economy presets)
- Assign chunks round-robin across providers
- Cost+time estimate table before deploy
- Ansible provisions N nodes in parallel, runs masscan/nmap/probe per node
- Results fetched back and merged into unified JSON report
- Scanner IPs logged per engagement for client IR reporting
- Operator IP whitelisting, enhanced OPSEC mode, YAML targets.yaml support
- deploy_phishing.py: undefined extra_vars on orchestration playbook
- freebird/main_menu.py: set_variable() called as function, is a method; fix to config.prompt_set_variable()
- freebird/tool_manager.py: shell=True with f-string paths; replace with direct pip binary call
- certipy-enum.py: password exposed in process list and logs; pass via env var
- cleanup_engine.py: load_vars_file() called with path string, expects provider name
- ioc-u.py: scapy import not guarded; tool crashed if scapy missing
- um_ops.py + um-vault.py: SQL table names f-stringed directly; quote identifiers
- ops-logger.sh: 63-line duplicate function block; removed second copy; quote config_script var
- recon_tools.py: ir-sandbox + link-sandbox not in TOOLS dict; forensics menu option 5 hidden from user
- um-crack.py: missing --scope flag inconsistent with all other Umbra tools
- heartbeat_ingest.py: openssl subprocess not catching FileNotFoundError; wrap with clear error
systemd-tmpfiles cleans /tmp on a schedule, wiping the tmux socket
and breaking existing sessions. Set TMUX_TMPDIR to ~/.local/share/tmux
so the socket survives cleanup on both full and quick recon boxes.
New module modules/tools/deploy_claude_bot.py with claude_bot_menu()
entry point. Supports local deploy/uninstall, remote SSH deploy with
auto-generated config.yaml (credentials passed via env vars, not written
to disk), and local/remote systemctl status checks. Wired into deploy.py
tools_menu() as option 8.
- Added ghost_protocol-public as git submodule so it's pulled with
git clone --recursive
- deploy_phantom() now checks submodule path first, then standalone
~/tools/ghost_protocol/, and auto-clones from GitHub as last resort
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gophish_ip:"{{ '192.168.1.10' if 'gophish' in deployment_components else '' }}"
mta_ip:"{{ '192.168.1.11' if 'mta_front' in deployment_components else '' }}"
redirector_ip:"{{ '192.168.1.12' if 'redirector' in deployment_components else '' }}"
webserver_ip:"{{ '192.168.1.13' if 'webserver' in deployment_components else '' }}"
gophish_ip:"{{ '10.0.0.10' if 'gophish' in deployment_components else '' }}"
mta_ip:"{{ '10.0.0.11' if 'mta_front' in deployment_components else '' }}"
redirector_ip:"{{ '10.0.0.12' if 'redirector' in deployment_components else '' }}"
webserver_ip:"{{ '10.0.0.13' if 'webserver' in deployment_components else '' }}"
deployment_id:"{{ deployment_id }}"
domain:"{{ phishing_domain | default(domain) }}"
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.