Hunt
Proactive hunting with the hunt_entity and hunt_campaign prompts, search_sql, prevalence, and MITRE coverage
Hunt
Hunting is looking for attacker activity before a detection fires. nano-investigator provides two hunting prompts, two search tools, and a set of prevalence and MITRE tools to scope and confirm what you find. This page covers each, and how a hunt that finds a coverage gap becomes a detection.
The hunting prompts
Two prompts structure a hunt into phases so you assess before you go deeper.
hunt_entity takes entity and entity_type (required) and time_range (optional, default -24h). It runs in three phases:
- Context.
get_entity_context,lookup_ip,get_prevalence, and asearchrun in parallel (IOC matches surface on the search results'ioc_*columns). - Assessment. The findings are presented before the hunt continues, so you can redirect.
- Type-specific deep dive. A branch per entity type (
ip,user,host,hash,domain) with nPL templates tuned to that type.
hunt_campaign takes description (required) and time_range (optional, default -7d). It plans a hunt from a hypothesis:
- Plan.
get_mitre_techniqueplusget_source_types, then 3 to 5 candidate queries. - Build and run. Execute the queries with refinement between passes.
- Enrich hits.
get_prevalence, theioc_*columns, and related alerts and cases on whatever the queries surfaced. - Report. A hunt report. Rule creation is deferred to
nanodac; the prompt does not write detections.
> hunt for lateral movement in the last 7 days
Running hunt_campaign (description="lateral movement",
time_range="-7d")...
Plan phase:
get_mitre_technique(T1021): Remote Services — RDP, SMB, WinRM,
SSH sub-techniques.
get_source_types: windows_security, windows_sysmon, zeek_conn
available.
Drafted 4 queries:
1. Type-3 logons fanning out from one source host
2. SMB writes to ADMIN$ / C$ across hosts
3. WinRM (5985/5986) sessions between workstations
4. New service installs (7045) on multiple hosts, same name
Run phase (search_sql, window -7d):
Q1 returned 1 standout: WIN-FS01 → 14 distinct hosts via
type-3 logon, all in 40 minutes, service account svc-backup.
Q2–Q4: nothing notable.
Enrich phase:
get_prevalence not applicable (host fan-out, not an artifact).
Related alerts on WIN-FS01: the credential-dumping alert #1180.
Related cases: C-204 (already open on WIN-FS01).
Report: Probable lateral movement from WIN-FS01 using svc-backup,
14 hosts in 40 min, correlates with open case C-204. No detection
currently covers "single source, many type-3 logons in a short
window" — candidate for a new rule. Want this added to C-204?Searching: nPL and SQL
Two tools run queries. Both hit the search service.
search runs an nPL (piped, Splunk-style) query. It is good for quick | stats and | timechart work. Params: query and start_time (required); end_time, limit (default 100), source_type.
search_sql is the primary search tool. It runs a read-only ClickHouse SELECT. Params: sql (required); start_time, end_time, limit (optional, default window is the last 24h).
search_sql is SELECT-only. DROP, INSERT, and functions like SLEEP and HOSTNAME are blocked. Every query needs a timestamp filter so the backend can prune partitions, and the backend caps results at 100,000 rows (the tools default to a much smaller limit). Add a WHERE on the timestamp column and a sensible LIMIT.
> sql: count distinct destination hosts per source host for
type-3 logons in the last 7 days, top 10
Calling search_sql (sql="SELECT src_host,
count(DISTINCT dest_host) AS hosts
FROM events
WHERE timestamp >= now() - INTERVAL 7 DAY
AND event_type = 'logon' AND logon_type = 3
GROUP BY src_host ORDER BY hosts DESC LIMIT 10")...
src_host hosts
WIN-FS01 14
WIN-DC01 6
WIN-JUMP1 4
...Before you write SQL or nPL, two reference tools help: get_schema returns the UDM column inventory (call it once per session before SQL work), and explain_query compiles an nPL query to SQL without running it. get_field_values returns the top values for a field in a query scope, which is how you find the right filter value quickly.
Prevalence and rarity
Rarity is the core hunting signal: artifacts seen on few hosts, or seen for the first time recently, are worth a look.
get_prevalenceanswers how common one artifact is. Params:type(hashordomainonly; IP errors) andvalue. Returns host count, occurrences, first and last seen, andis_rare.get_rare_artifactslists artifacts seen on few hosts. Optional params:type(hash_md5/hash_sha256/domain/ip_address),window,limit(default 50),offset.get_new_artifactslists first-seen-recently artifacts. Optional params:type,since,limit(default 50),offset.
get_prevalence only accepts a single hash or domain value. The get_rare_artifacts and get_new_artifacts tools do accept ip_address, so reach for those when an IP is the artifact you want to profile.
> show me rare SHA256 hashes seen this week
Calling get_rare_artifacts (type=hash_sha256, window=7d)...
hash a3f1... 1 host first seen 2d ago
hash 9c20... 2 hosts first seen 5d ago
...
> how common is a3f1... ?
Calling get_prevalence (type=hash, value=a3f1...)...
host_count=1, occurrences=3, first_seen=2d ago, is_rare=trueMITRE coverage and sources
Two MITRE tools tie a hunt to ATT&CK, and one lists your log sources:
get_mitre_techniquereturns ATT&CK data, optionally filtered to one technique byid(for exampleT1071).get_mitre_coveragereturns the coverage and gaps of the detection library, which is how you find techniques with no detection behind them.get_source_typeslists the available log source types. Call it before using asource_typefilter so you use a real value.
A get_mitre_coverage gap is a strong reason to hunt: if a technique has no detection, an attacker using it produces no alert, and the hunt is your only way to see it.
From hunt to detection
A hunt that consistently surfaces real activity, or one that fills a get_mitre_coverage gap, should become a scheduled detection. nano-investigator is read-only for detections: it does not write rules. Rule authoring lives in nanodac.
Confirm the pattern is real and repeatable. Use get_prevalence, related alerts, and related cases to make sure the hunt query is finding attacker behavior and not a noisy baseline.
Capture the query. Save it with save_search so the exact nPL or SQL is not lost, and link it from the relevant case or notebook.
Hand it to nanodac. Take the working query into the detection-as-code workflow to draft, validate, and ship it as a scheduled rule. See Authoring detections.
> the WIN-FS01 fan-out query is a real gap. save it and tell me
how to turn it into a detection.
Calling save_search (name="lateral-movement-type3-fanout",
query="SELECT src_host, count(DISTINCT dest_host) ...",
query_mode="sql", visibility="group")...
→ saved
This is a detection gap (no rule covers single-source type-3
fan-out). nano-investigator does not author rules. Open the
saved query in nanodac and run the detection workflow:
draft → validate → test → PR → sync. See
/agents/detection-as-code/detections.What's next
- Detection-as-Code: turn a hunt into a scheduled detection
- Investigate alerts: the structured investigation workflow and notebooks
- Triage & cases: the case model and the start-of-shift briefing