Z-Sans uses ToolOrchestrator to uniformly orchestrate external tools and built-in implementations. All tool results are returned synchronously as Python data structures; external tools write temporary files that are read and parsed, then deleted after use.
Tool Probe Order¶
Each tool looks up an available implementation in this order:
- Configuration
external_tools.paths.<name> - Built-in directories (
tools/<name>/...,assets/<name>) - System PATH (
which/where)
When no external binary is found, a built-in implementation fallback is used, ensuring the scan is never interrupted.
Low-Privilege Environment Fallback
When /tmp is not writable, the temporary file directory automatically falls back to tools/.tmp; it can also be specified via the environment variable ZSANS_TEMP_DIR / $TMPDIR.
Tool Overview¶
| Tool method | What is invoked | Purpose | Timeout | Fallback if missing |
|---|---|---|---|---|
run_subfinder |
binary subfinder |
Subdomain discovery | 120s | Built-in DNS resolution |
run_naabu |
binary naabu |
Port scanning | 240s | Built-in TCP scanner |
run_jsfinder |
python assets/JSfinder.py |
JS/link extraction | jsfinder_timeout (default 30s) |
Built-in requests+BS4 |
run_free_subfinder |
python assets/free-subfinder.py |
Free passive subdomain aggregation | 120s | — |
run_ehole |
binary EHole |
Web fingerprinting | 60s | WhatWeb / title |
run_whatweb |
Ruby script whatweb |
Fingerprinting (richer) | 90s | EHole / title |
run_fingerprint |
Unified fingerprint entry | Select engine per configuration | — | — |
_run_internal_dns_resolver |
python assets/dnsxs.py |
Built-in DNS resolution | 60s | — |
_run_internal_port_scanner |
python assets/port.py |
Built-in port scanning | 180s | — |
_internal_jsfinder |
Pure Python implementation | Built-in JS/link extraction | http.timeout |
— |
Overriding Tools¶
Plugins can register or override tools via ToolOrchestrator.register_tool(name, path, version, extra_args); extra_args are appended to the end of the command line.
Fingerprinting¶
Fingerprinting is dispatched through the unified entry run_fingerprint:
external_tools:
fingerprint:
enabled: true # Enable fingerprinting
engine: auto # auto | ehole | whatweb | both | none
| engine | Behavior |
|---|---|
auto |
Auto-select by availability (prefers EHole) |
ehole |
EHole only |
whatweb |
WhatWeb only |
both |
Run both and merge the results |
none |
Skip fingerprinting |
EHole¶
- Built-in multi-architecture binaries:
tools/ehole/{darwin,linux,windows}/{arch}/EHole[.exe], each shipped withfinger.json+config.ini - Resolution priority: configured path → built-in multi-architecture directory → legacy single-directory → system PATH
- Command:
EHole finger -u <url> -o <tmp.json> - Output fields:
{url, fingerprints, cms, server, status_code, title}
WhatWeb¶
- Command:
whatweb --no-errors --log-json=<tmp> <url> - Filters out ~40 metainfo noise plugins (Title/HTTPServer/IP etc.) before counting fingerprints
- The first non-noise match is used as
cms;serveris taken from HTTPServer/WebServer/X-Powered-By
Built-in Scripts¶
| Script | Based on | Parses | Engine usage |
|---|---|---|---|
assets/JSfinder.py |
Threezh1 JSFinder | Extracts URLs and subdomains from pages + inline JS | -u input, -ou/-os write result files |
assets/dnsxs.py |
dnspython, connecting directly to 6 public DNS servers | A/AAAA/CNAME/MX/TXT/NS/SOA/PTR/ANY | -t A -q internal resolution fallback |
assets/free-subfinder.py |
5 free sources, concurrent aggregation | CRT.sh / AlienVault OTX / HackerTarget / DNSdumpster / Anubis | Extends internal DNS results |
assets/port.py |
Python socket concurrent connection | TCP connect_ex | -p <range> -q built-in port scanning |
Free Passive Sources (free-subfinder.py)¶
Aggregates the following public sources:
| Source | Type |
|---|---|
| CRT.sh | Certificate transparency logs |
| AlienVault OTX | OTX passive DNS |
| HackerTarget | Passive DNS API |
| DNSdumpster | Fetches a CSRF token first, then POSTs |
| Anubis (jonlu.ca) | Subdomain search engine |
Default timeout 15s, 5 threads, 2 retries with 1s backoff.
Built-in DNS (dnsxs.py)¶
Six public DNS servers, queried over direct UDP connections.
Built-in Port Scanner (port.py)¶
- Default 50 threads, 1.0s timeout per port
- Default port range
1-1024; also supports80,443,1000-2000format - Domains are resolved to IPs before scanning
The default port range used by the engine internally (asset_types.ip.tools.port_range):
Concurrency Control¶
- Global:
concurrency.max_tasks(default 20) - Each tool independently:
concurrency.tools.<name>(subfinder / naabu / jsfinder) ToolOrchestratorprovidesreduce_concurrency()/increase_concurrency()to dynamically adjust concurrency (cap 20)get_running_tasks()returns the currently running tasks (func/args/running_time)