-
Notifications
You must be signed in to change notification settings - Fork 166
EDR Integration
LitterBox can dispatch a payload to a separate EDR-instrumented Windows VM and pull the correlated detection alerts back into the results page. Two profile kinds are supported, both backed by the same Whiskers Rust agent:
| Kind | Where alerts come from | When to pick it |
|---|---|---|
kind: elastic |
LitterBox queries an operator-deployed Elastic stack for alerts indexed by Elastic Defend on the VM | You already run Elastic Defend or want a "real" commercial EDR in the loop |
kind: fibratus |
LitterBox polls Whiskers's /api/alerts/fibratus/since which wevtutil-queries the local Windows Application event log for Provider=Fibratus records |
You want an open-source ETW detection engine β no remote backend needed, runs entirely on the VM |
Both kinds share the same orchestrator structure, the same Phase-1/Phase-2 split, and the same UI. The alert-source plumbing differs, the rest is identical.
βββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββ
β LitterBox (Flask) β β EDR VM (Windows) β
β β β β
β upload page β β Whiskers.exe β
β /analyze/edr/<P>/<T> β βββββββΊ β /api/info β
β β β /api/lock/{acquire,release,...} β
β ElasticEdrAnalyzer OR β βββββββ β /api/execute/exec (XOR-encoded) β
β FibratusEdrAnalyzer β β /api/execute/kill β
β β β /api/logs/execution β
β βββββββββββββββββββ β β /api/alerts/fibratus/since β
β β Phase 1 (sync) β β β β
β β exec on agent β β β ββββββββββββββββββββββββββββββββ β
β βββββββββββββββββββ β β β EDR / detection engine β β
β βββββββββββββββββββ β β β β’ Elastic Defend agent β β
β β Phase 2 (async) β βββΌββββββββββΌβββ β’ OR Fibratus + ETW β β
β β alert poll loop β β β ββββββββββββββββββββββββββββββββ β
β βββββββββββββββββββ β β β β
βββββββββββββββββββββββββββ β Application event log β
β β (Provider=Fibratus) β
β ββββββββββββββββββββββββββββββββββββββ
β
β for kind=elastic only:
βΌ
ββββββββββββββββββββββββ
β Elastic stack β
β (.alerts-security. β
β alerts-default-*) β
ββββββββββββββββββββββββ
Both ElasticEdrAnalyzer and FibratusEdrAnalyzer expose run_exec() (sync) and run_correlation() (unlocked + threaded via registry.dispatch_split):
Phase 1 β exec, holds the agent lock.
-
GET /api/infoβ discover hostname (no manual config) -
lock_acquire(single-occupancy gate, auto-expires after 30 min if stranded) -
exec(payload, args)over multipart, XOR-encoded on the wire -
wait_for_exitorexec_timeout_seconds -
kill(idempotent) -
get_execution_logsβ stdout/stderr/exit_code lock_release
Returns (phase_1_dict, continuation) to the caller. The phase-1 dict has status: "polling_alerts" plus a populated execution.{pid, stdout, stderr, exit_code, exec_status, killed_by_edr}. AV-block runs use the same polling_alerts status β the orthogonal flag summary.blocked_by_av: true carries the AV-block state.
Phase 2 β alert correlation, no lock.
- Poll every 2s, early-return on first hit, then wait
SETTLE_SECONDS(8s) to capture burst-related alerts. - Bounded by
wait_seconds_for_alerts(90s for elastic, 30s for fibratus by default). - AV-block path uses
av_block_wait_seconds(60s / 30s) instead. - Saves the final findings dict via the route's
on_phase_2_donecallback.
The lock is intentionally NOT held during Phase 2 so back-to-back dispatches don't queue. Time-bounded queries keep concurrent runs from polluting each other's results.
polling_alerts β Phase 1 succeeded; Phase 2 in flight.
AV-block runs use this too β summary.blocked_by_av
carries the flag, status doesn't fork.
completed β Phase 2 done, alerts populated
blocked_by_av β Phase 2 done from AV-block path
partial β Phase 1 ok but backend query failed
agent_unreachable β couldn't reach Whiskers (transport-level)
busy β lock held by another run (409 from agent)
error β anything else
The frontend polls GET /api/results/edr/<profile>/<target> adaptively β 2s base, Γ1.5 backoff up to 15s on consecutive ticks where total_alerts didn't change, snap back to 2s on movement.
agent_unreachable, busy, and pre-execution error are not persisted to disk β they're transient transport failures, not real findings. The file-info hero only shows EDR-profile buttons that have actual saved data on disk per-sample.
Elastic. ElasticClient.fetch_alerts() filters by host.name (case-insensitive) + @timestamp window, plus a bool/should clause that requires the alert to mention the payload filename in any of:
-
file.name(term match) -
process.name(term match) -
file.path(wildcard*<filename>) -
process.executable(wildcard) -
process.command_line(wildcard β picks up rundll32-launched DLLs where the DLL name appears in the parent command line) -
process.args(wildcard)
The MD5-prefixed dropper filename is unique enough across uploads that concurrent dispatches don't taint each other's results.
Fibratus. FibratusEdrAnalyzer._poll_alerts calls AgentClient.get_fibratus_alerts(since, until) which hits Whiskers's /api/alerts/fibratus/since. Whiskers shells wevtutil qe Application "/q:..." /f:xml with the same XPath shape DetonatorAgent uses, extracts <TimeCreated> + <EventID> + <Data> per record, returns the raw JSON <Data> strings. LitterBox parses + filters by filename match against events[].proc.{name,exe,cmdline,parent_name,parent_cmdline}.
EDR alerts feed the run's own results page but do NOT contribute to the file-info detection score. EDR is its own analysis type with its own page; the file's score stays scoped to static + dynamic + PE info.
Within the EDR page, scoring is described in Detection Score Explained.
Pick the kind that matches your environment:
- Elastic Defend Setup β full walkthrough including the elastic-container-project deployment.
-
Fibratus Setup β install Fibratus on the VM, configure
alertsenders.eventlog: format: json, drop the profile YAML.
Both setups depend on Whiskers being installed on the VM first β see Whiskers Agent.
Profiles live under Config/edr_profiles/*.yml. Real files are gitignored; example files (*.yml.example) are tracked. Drop one file per profile; restart LitterBox to pick up changes.
Common fields (both kinds):
name: "elastic" # used in URLs (/analyze/edr/<name>/<target>)
display_name: "Elastic Defend" # shown on the upload page button
kind: "elastic" # or "fibratus" β defaults to "elastic" for back-compat
agent_url: "http://192.168.1.100:8080" # Whiskers
wait_seconds_for_alerts: 90 # Phase 2 max budget for the success path
av_block_wait_seconds: 60 # Phase 2 max budget for the AV-block path
exec_timeout_seconds: 60 # hard cap on payload runtime
drop_path: "C:\\Users\\Public\\sample.exe" # optional, defaults to <Whiskers-exe-dir>/samples/kind: elastic-only fields:
elastic_url: "https://192.168.1.50:9200"
elastic_apikey: "<base64-encoded-API-key>"
elastic_verify_tls: false # self-signed cert supportkind: fibratus adds nothing β the Whiskers agent does the alert-source work via wevtutil.
The agent self-reports its hostname via /api/info, so there is no hostname field β moving the agent to a different VM is transparent.
- Whiskers Agent β the agent on the VM
-
Elastic Defend Setup β
kind: elasticwalkthrough -
Fibratus Setup β
kind: fibratuswalkthrough -
All in One Pipeline β
/analyze/all/<target>runs Static + every reachable EDR profile in parallel - HTTP API Reference β every endpoint
- Detection Score Explained β what each scanner contributes
- π Home
- π§ Application Architecture
- π Dashboard
- π All in One Pipeline
- π― Detection Score Explained
- 𧬠Blender Scanner
- π FuzzyHash Scanner
- π‘οΈ HolyGrail BYOVD Scanner
- π YARA Rules Management