This guide walks through setting up a detection validation lab using Echos, from initial build to verifying that your detection rules fire correctly.
The basic workflow is:
- Stand up a listener on a lab host (your "C2 server" stand-in)
- Run Echos from a separate lab host (your "victim" stand-in)
- Observe whether your detection stack alerts on the traffic
- Adjust detection rules and repeat
Everything stays within your lab network. No external traffic is required.
- Rust 1.70+ installed on the Echos host
- A packet capture or detection tool (Zeek, Suricata, Snort, your EDR/NDR, etc.) monitoring the lab segment
- A listener or simple HTTP server on the target host (e.g.
python3 -m http.server 8080)
git clone https://github.com/xdrew87/echos
cd echos
cargo build --releaseThe binary is at target/release/echos (or target\release\echos.exe on Windows).
./target/release/echos --listPick a profile that matches the traffic pattern you want to simulate. Each profile emulates a specific threat actor's beaconing style — headers, timing, jitter, and protocol.
Before running live, confirm the configuration looks correct:
./target/release/echos --profile Cobalt --target http://192.168.10.50:8080 --count 5 --dry-runOutput:
[DRY RUN] Would run profile "Cobalt" (HTTP → http://192.168.10.50:8080)
Jitter : 20% Uniform
Timeout : 10s
Insecure TLS : No
Count limit : 5
Duration : none
Start a listener on your target host:
# Simple HTTP listener (Python)
python3 -m http.server 8080Run Echos from the source host:
./target/release/echos --profile Cobalt --target http://192.168.10.50:8080 --count 5Check your detection stack for alerts matching Cobalt Strike HTTP patterns (User-Agent, Accept headers, etc.).
The APT28 profile generates DNS lookup traffic:
./target/release/echos --profile APT28 --count 10The default target is example.com. Your DNS monitoring (Zeek dns.log, Suricata, etc.) should log repeated A-record lookups from the same host within a short window. This tests your DNS beacon frequency detection.
Many lab environments use self-signed certificates. Use --insecure-tls to allow this:
./target/release/echos --profile Lazarus --target https://192.168.10.50:8443 --count 3 --insecure-tlsWithout --insecure-tls, Echos validates certificates normally. Use this to confirm your detection logic triggers on the correct TLS fingerprint regardless of cert validity.
Use --json to emit machine-readable output suitable for feeding into a validation script or CI pipeline:
./target/release/echos --profile Cobalt --count 5 --json --quiet --target http://192.168.10.50:8080The final summary prints as a single JSON object:
{
"profile": "Cobalt",
"protocol": "HTTP",
"target": "http://192.168.10.50:8080",
"attempts": 5,
"successes": 5,
"failures": 0,
"failure_rate_pct": 0.0,
"avg_delay_secs": 9.84,
"start": "2026-04-15 04:02:11",
"end": "2026-04-15 04:02:41",
"runtime_secs": 60.1,
"dry_run": false,
"insecure_tls": false
}Pipe this to jq or parse it in your automation scripts.
Define your own profile in a TOML file to match traffic you need to simulate:
# profiles/custom.toml
[[profiles]]
name = "Lab HTTPS Beacon"
protocol = "https"
target = "https://192.168.10.50:8443"
base_delay_secs = 30
jitter_percent = 10.0
jitter_algorithm = "gaussian"
[profiles.headers]
User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
X-Lab-Run = "detection-test-01"Load it at runtime:
./target/release/echos --config profiles/custom.toml --profile "Lab HTTPS Beacon" --count 5 --insecure-tls┌─────────────────────────────────────────────────────┐
│ │
│ 1. Write or update detection rule │
│ 2. Run Echos --dry-run to confirm setup │
│ 3. Run Echos with --count N │
│ 4. Check detection stack for expected alert │
│ 5. If alert fires → rule is validated ✓ │
│ 6. If no alert → tune rule, repeat │
│ │
└─────────────────────────────────────────────────────┘
- Start with
--count 1to confirm connectivity before running longer tests - Use
--verboseto see delay and timing details during a run - Use
--log-file run.jsonto archive every run for audit trails - The Emotet profile rotates across 4 targets by default — useful for testing target-rotation detection logic
- The APT29 profile uses sinusoidal jitter tied to time-of-day; delays will be longer at night and shorter during business hours
- Use
--timeoutto shorten per-connection timeouts in environments where listeners are not always up
Run Echos only on networks and systems you own or have explicit written permission to test. The default targets (127.0.0.1, example.com) are intentionally inert. Always point --target at infrastructure you control.