Enterprise Security Integration
Splunk-ES compatibility: threat-intel KV Store collections, adaptive response, CIM aliases, graph macros, and worked detection examples.
Enterprise Security Integration Documentation
The Whisper Security Add-on is Splunk-ES-compatible but does not ship a prebuilt correlation-search pack. What you get out of the box is
- two ES Threat Intelligence KV Store collections
(
whisper_ip_intel,whisper_domain_intel) plus disabled-by-default populator searches, - an Enrich with Whisper adaptive response action usable from ES notable events,
- CIM-compliant field aliases on
whisper:enrichmentevents so notable events and RBA can correlate against Network Resolution and Threat Intelligence data models, - nine
whisper_*graph-query macros you can call from your own detections, plus four disabled-by-default enrichment pipeline templates you can clone into detections or risk generators.
This page documents what ships, and then walks through how to build your own detections and RBA rules using the pieces above.
What ships
Threat Intelligence framework integration
The ES Threat Intelligence framework consumes KV Store collections that
match the ES ip_intel and domain_intel schemas. The add-on ships
two:
| Collection | Matches ES schema | Populated by |
|---|---|---|
whisper_ip_intel | ip_intel | Whisper - Populate IP Threat Intel KV Store (disabled) |
whisper_domain_intel | domain_intel | Whisper - Populate Domain Threat Intel KV Store (disabled) |
Both populator searches live in savedsearches.conf and are disabled by
default (AppInspect requirement). Each search queries
MATCH ... LISTED_IN ... FEED_SOURCE against the WhisperGraph,
outputlookups to the collection, and tags each record with
threat_category = whisper so ES can surface them as a feed source.
The _time field on each record uses the indicator's latest lastSeen
timestamp from its threat feed sources when available, which reflects
when the indicator was most recently observed as active -- more useful
for ES correlation than collection time. When no source timestamps are
available (e.g. indicators not listed in any feed), _time falls back
to the collection time.
Records are written using KV Store batch_save (up to 1000 per batch)
for efficient bulk population. If a batch fails, the populator falls
back to per-record inserts to maximise coverage.
Collection schemas
whisper_ip_intel (ES ip_intel-compatible):
| Field | Type | Description |
|---|---|---|
ip | string | IP address (lookup key) |
threat_collection_name | string | ES threat intelligence collection name this record belongs to (used by the ES threat framework to namespace indicators) |
threat_collection_key | string | ES threat intelligence collection key (identifier within the collection) |
description | string | Threat description |
threat_key | string | Threat classification |
threat_group | string | whisper_graph |
weight | number | ES threat weight: 1 (low), 2 (medium), 3 (high) |
whisper_asn | string | ASN |
whisper_asn_name | string | ASN organisation name |
whisper_country | string | Country code |
whisper_prefix | string | IP prefix (CIDR) |
whisper_risk_score | number | Normalised risk score (0-100) |
whisper_risk_level | string | informational / low / medium / high / critical |
whisper_threat_score | number | Raw threat score from CALL explain() |
whisper_threat_level | string | Threat level from CALL explain() |
whisper_domain_intel (ES domain_intel-compatible):
| Field | Type | Description |
|---|---|---|
domain | string | Domain name (lookup key) |
threat_collection_name | string | ES threat intelligence collection name this record belongs to |
threat_collection_key | string | ES threat intelligence collection key (identifier within the collection) |
description | string | Threat description |
threat_key | string | Threat classification |
threat_group | string | whisper_graph |
weight | number | ES threat weight: 1 / 2 / 3 |
whisper_asn_name | string | ASN organisation name |
whisper_country | string | Country code |
whisper_risk_score | number | Normalised risk score (0-100) |
whisper_risk_level | string | Risk level |
whisper_threat_score | number | Raw threat score |
whisper_threat_level | string | Threat level |
Enabling the populators
In Splunk:
- Go to Settings > Searches, Reports, and Alerts.
- Filter on app = TA-whisper-graph.
- Edit Whisper - Populate IP Threat Intel KV Store and
Whisper - Populate Domain Threat Intel KV Store -- set
Enabled = true and pick a schedule (the default
0 */6 * * *runs every 6 hours). - In ES, confirm the feeds appear under Configure > Data Enrichment > Threat Intelligence Management.
Enrich with Whisper adaptive response action
alert_actions.conf ships the [whisper_enrich] custom alert action
backed by whisper_adaptive_response.py. This lets an ES analyst (or a
correlation search) enrich an indicator on demand and write the result
back onto the notable event.
From a notable event:
- Open the notable in Incident Review.
- Actions > Run Adaptive Response > Enrich with Whisper.
- The action pulls candidate indicators from
src,dest,src_dns, anddest_dnson the notable, calls the WhisperGraph, and adds the enrichment result as a comment on the notable.
From a correlation search, attach action.whisper_enrich = 1 to the
saved search to fire the enrichment automatically on each triggered
result.
CIM field aliases on enrichment events
props.conf aliases Whisper-prefixed fields on whisper:enrichment
events to CIM field names so notable events, RBA rules, and ES
dashboards can correlate without custom extractions. The full alias
table is in CIM Mapping, but at a
glance:
whisper_ip->dest_ipwhisper_country->dest_countrywhisper_asn->dest_asnwhisper_threat_score->threat_score,whisper_threat_level->threat_levelwhisper_risk_score->risk_score,whisper_risk_level->risk_level- all 13
whisper_is_*boolean threat flags alias to the CIM Threat Intelligence equivalents (is_c2,is_malware,is_phishing, ...) vendorandvendor_productare set viaEVALso CIM dashboards group Whisper data under a consistent vendor label.
The events are tagged network resolution dns, so they participate in
the Network Resolution and DNS CIM data models. whisper:threat_intel
and whisper:watchlist events are tagged threat report for Threat
Intelligence.
Example enrichment pipeline templates
savedsearches.conf ships four disabled enrichment pipeline
templates under the Example - Whisper - ... prefix. Each is a
starting point: clone it, swap the placeholders, pick a destination
index, and enable:
| Template | What it does |
|---|---|
Example - Whisper - Enrich DNS Domains | Enrich DNS query values with whisperlookup, write to <destination_index> as sourcetype=whisper:enriched_dns |
Example - Whisper - Enrich Destination IPs | Enrich dest_ip from network traffic, write as sourcetype=whisper:enriched_ip |
Example - Whisper - Enrich Proxy Hostnames | Enrich url_domain from proxy logs, write as sourcetype=whisper:enriched_proxy |
Example - Whisper - Custom Graph Query Enrichment | Run an arbitrary Cypher query per indicator via whisperquery, write as sourcetype=whisper:enriched_custom |
Each template's search field uses <source_index>,
<source_sourcetype>, <destination_index>, and <indicator_field>
placeholders that you must replace before enabling.
Graph-query macros
macros.conf ships nine macros you can reuse inside detections,
dashboards, or RBA rules to keep SPL short and consistent. They are
query helpers, not correlation-search thresholds:
| Macro | Arguments | Purpose |
|---|---|---|
whisper_index | -- | Index override used by every shipped dashboard/search (default index=whisper) |
whisper_shared_nameservers(domain) | domain | Domains sharing nameservers with the given domain |
whisper_asn_infrastructure(asn) | asn | Prefixes routed by an ASN |
whisper_cname_chain(domain) | domain | CNAME chain resolution up to 5 hops |
whisper_spf_chain(domain) | domain | SPF include chain up to 3 hops |
whisper_bgp_peers(asn) | asn | BGP peers of an ASN with name + country |
whisper_cohosted_domains(domain) | domain | Domains co-hosted on the same IP |
whisper_full_investigation(indicator) | indicator | hostname -> IP -> ASN with geo and reverse DNS |
whisper_explain(indicator) | indicator | Threat assessment via CALL explain() |
All macros are parameterised -- the $domain$ / $asn$ / $indicator$
substitutions flow into the params argument of whisperquery, so
there is no string interpolation on the way into the Cypher query.
Building your own detections
The recommended recipe for a Whisper-powered ES detection:
- Pick a source index -- network traffic, proxy, DNS, or whichever events contain the indicator you want to enrich.
- Clone one of the example templates as a starting point, or write a fresh stanza if none fits.
- Call
whisperlookup(for enrichment) or one of thewhisper_*macros (for graph traversal) to add Whisper context. - Add your detection condition as a
whereclause onwhisper_risk_score,whisper_threat_level, one of thewhisper_is_*flags, or a graph-traversal result. - Decide what happens on match -- write to
index=riskfor RBA, emit anindex=notableevent, or route back into your security workflow. - Schedule it disabled, tune it against historical data, then enable.
Worked example 1 -- suspicious CNAME chain
Goal: flag DNS queries where the CNAME chain is longer than 3 hops and the chain does not terminate at a known CDN.
index=<dns_index> sourcetype=<dns_sourcetype> earliest=-15m query=*
| rename query AS indicator
| dedup indicator
| `whisper_cname_chain(indicator)`
| where depth > 3
| lookup whisper_cdn_asns.csv asn AS whisper_asn OUTPUT asn AS cdn_asn
| where isnull(cdn_asn)
| eval risk_score=30
| eval risk_message="Whisper: suspicious CNAME chain depth=" . depth . " ending at " . cname_target
| eval risk_object=indicator, risk_object_type="other"
| collect index=risk sourcetype=whisper:risk
The depth threshold (3) lives in the SPL -- override it there, not
in a macro. whisper_cdn_asns.csv ships in lookups/ and lists known
CDN/SaaS ASNs for noise reduction.
Worked example 2 -- multi-feed threat IP communication
Goal: flag internal hosts talking to IPs appearing on multiple distinct threat feeds (high-confidence threat signal).
index=<firewall_index> sourcetype=<firewall_sourcetype> earliest=-15m action=allowed
| rename dest_ip AS indicator
| dedup src_ip indicator
| `whisper_explain(indicator)`
| spath threatFeeds{} output=feeds
| eval feed_count=mvcount(feeds)
| where feed_count >= 2
| eval risk_score=case(feed_count >= 4, 80, feed_count >= 2, 50, 1=1, 0)
| eval risk_message="Whisper: destination IP listed on " . feed_count . " threat feeds"
| eval risk_object=src_ip, risk_object_type="system"
| collect index=risk sourcetype=whisper:risk
The >= 2 minimum feed count is the detection threshold -- change it
inline.
Worked example 3 -- co-hosting density anomaly
Goal: flag outbound traffic to IPs with fewer than 5 co-hosted domains (dedicated infrastructure pattern often used by C2 or phishing), while excluding known CDN ASNs.
index=<firewall_index> sourcetype=<firewall_sourcetype> earliest=-30m action=allowed
| rename dest_ip AS indicator
| dedup src_ip indicator
| whisperlookup field=indicator type=ip
| where whisper_cohosting_count < 5
| lookup whisper_cdn_asns.csv asn AS whisper_asn OUTPUT asn AS cdn_asn
| where isnull(cdn_asn)
| eval risk_score=25
| eval risk_message="Whisper: low-density cohosting (" . whisper_cohosting_count . " domains on " . indicator . ")"
| eval risk_object=src_ip, risk_object_type="system"
| collect index=risk sourcetype=whisper:risk
Worked example 4 -- BGP prefix conflict
Goal: monitor an inventory of your organisation's ASNs for prefix conflicts (i.e. prefixes announced by unexpected ASNs -- a BGP hijack signal).
You will need a CSV of your ASNs (e.g. a hand-maintained
my_org_asns.csv):
| inputlookup my_org_asns.csv
| rename asn AS our_asn
| map search="| whisperquery query=\"MATCH (a:ASN {name: $our_asn$})-[:ROUTES]->(p:PREFIX)<-[:CONFLICTS_WITH]-(other_p:PREFIX)<-[:ROUTES]-(other:ASN) WHERE other.name <> $our_asn$ RETURN a.name AS our_asn, p.name AS prefix, other.name AS conflicting_asn LIMIT 50\" params=\"our_asn=$our_asn$\""
| eval risk_score=75
| eval risk_message="Whisper: prefix " . prefix . " announced by our ASN " . our_asn . " and unexpected ASN " . conflicting_asn
| eval risk_object=conflicting_asn, risk_object_type="other"
| collect index=risk sourcetype=whisper:risk
| map is used so each our_asn is passed through as a parameter
rather than interpolated into the Cypher string.
Inline risk scores
If you do not want to write a detection and only want to react to
Whisper-computed risk scores, every whisperlookup result already
includes four risk fields you can alert on:
| Field | Description |
|---|---|
whisper_risk_score | Normalised 0-100, aliased to CIM risk_score |
whisper_risk_level | informational / low / medium / high / critical |
whisper_risk_factors_list | Comma-separated list of contributing factors |
whisper_risk_components | JSON with per-factor score + detail |
Tune the factor weights in lookups/whisper_risk_factors.csv -- each
row specifies a factor name, point value, and description. Use
Settings > Lookups > Lookup table files to edit the CSV without
touching .conf files. See the
Enrichment page for the full factor list and weights.
Example RBA trigger based on inline risk scores:
index=<source_index> sourcetype=<source_sourcetype> earliest=-15m
| rename <indicator_field> AS indicator
| dedup indicator
| whisperlookup field=indicator
| where whisper_risk_score >= 60
| eval risk_score=whisper_risk_score
| eval risk_message="Whisper risk " . whisper_risk_level . " (" . whisper_risk_factors_list . ")"
| eval risk_object=indicator, risk_object_type=if(whisper_indicator_type=="ip", "system", "other")
| collect index=risk sourcetype=whisper:risk
Deployment notes for ES environments
- Install the add-on on the ES search head (or the ES search head cluster). The adaptive response action and threat intel populator searches run on the search head.
- If you operate a separate heavy forwarder for data collection, the modular inputs can also be configured there -- see the Modular Inputs page.
- Confirm
whisper_useris imported (viaauthorize.conf) into the ES roles your analysts use so they can runwhisperlookup/whisperqueryfrom the ES search bar. - Add
TA-whisper-graphto your ES deployment-server bundle if shipping to a distributed environment; see the Deployment Architecture page.
Related pages
- Saved Searches -- full list of shipping utility and example searches.
- Macros -- every
whisper_*graph macro with parameters and example output. - CIM Mapping -- full CIM alias table and data-model coverage.
- Enrichment -- risk factor list and scoring engine.
- Search Commands --
whisperlookup,whisperquery, and the rest of the command set.