Enterprise Security Integration

Splunk-ES compatibility: threat-intel KV Store collections, adaptive response, CIM aliases, graph macros, and worked detection examples.

Updated May 2026Splunk

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:enrichment events 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:

CollectionMatches ES schemaPopulated by
whisper_ip_intelip_intelWhisper - Populate IP Threat Intel KV Store (disabled)
whisper_domain_inteldomain_intelWhisper - 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):

FieldTypeDescription
ipstringIP address (lookup key)
threat_collection_namestringES threat intelligence collection name this record belongs to (used by the ES threat framework to namespace indicators)
threat_collection_keystringES threat intelligence collection key (identifier within the collection)
descriptionstringThreat description
threat_keystringThreat classification
threat_groupstringwhisper_graph
weightnumberES threat weight: 1 (low), 2 (medium), 3 (high)
whisper_asnstringASN
whisper_asn_namestringASN organisation name
whisper_countrystringCountry code
whisper_prefixstringIP prefix (CIDR)
whisper_risk_scorenumberNormalised risk score (0-100)
whisper_risk_levelstringinformational / low / medium / high / critical
whisper_threat_scorenumberRaw threat score from CALL explain()
whisper_threat_levelstringThreat level from CALL explain()

whisper_domain_intel (ES domain_intel-compatible):

FieldTypeDescription
domainstringDomain name (lookup key)
threat_collection_namestringES threat intelligence collection name this record belongs to
threat_collection_keystringES threat intelligence collection key (identifier within the collection)
descriptionstringThreat description
threat_keystringThreat classification
threat_groupstringwhisper_graph
weightnumberES threat weight: 1 / 2 / 3
whisper_asn_namestringASN organisation name
whisper_countrystringCountry code
whisper_risk_scorenumberNormalised risk score (0-100)
whisper_risk_levelstringRisk level
whisper_threat_scorenumberRaw threat score
whisper_threat_levelstringThreat level

Enabling the populators

In Splunk:

  1. Go to Settings > Searches, Reports, and Alerts.
  2. Filter on app = TA-whisper-graph.
  3. 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).
  4. 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:

  1. Open the notable in Incident Review.
  2. Actions > Run Adaptive Response > Enrich with Whisper.
  3. The action pulls candidate indicators from src, dest, src_dns, and dest_dns on 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_ip
  • whisper_country -> dest_country
  • whisper_asn -> dest_asn
  • whisper_threat_score -> threat_score, whisper_threat_level -> threat_level
  • whisper_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, ...)
  • vendor and vendor_product are set via EVAL so 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:

TemplateWhat it does
Example - Whisper - Enrich DNS DomainsEnrich DNS query values with whisperlookup, write to <destination_index> as sourcetype=whisper:enriched_dns
Example - Whisper - Enrich Destination IPsEnrich dest_ip from network traffic, write as sourcetype=whisper:enriched_ip
Example - Whisper - Enrich Proxy HostnamesEnrich url_domain from proxy logs, write as sourcetype=whisper:enriched_proxy
Example - Whisper - Custom Graph Query EnrichmentRun 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:

MacroArgumentsPurpose
whisper_index--Index override used by every shipped dashboard/search (default index=whisper)
whisper_shared_nameservers(domain)domainDomains sharing nameservers with the given domain
whisper_asn_infrastructure(asn)asnPrefixes routed by an ASN
whisper_cname_chain(domain)domainCNAME chain resolution up to 5 hops
whisper_spf_chain(domain)domainSPF include chain up to 3 hops
whisper_bgp_peers(asn)asnBGP peers of an ASN with name + country
whisper_cohosted_domains(domain)domainDomains co-hosted on the same IP
whisper_full_investigation(indicator)indicatorhostname -> IP -> ASN with geo and reverse DNS
whisper_explain(indicator)indicatorThreat 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:

  1. Pick a source index -- network traffic, proxy, DNS, or whichever events contain the indicator you want to enrich.
  2. Clone one of the example templates as a starting point, or write a fresh stanza if none fits.
  3. Call whisperlookup (for enrichment) or one of the whisper_* macros (for graph traversal) to add Whisper context.
  4. Add your detection condition as a where clause on whisper_risk_score, whisper_threat_level, one of the whisper_is_* flags, or a graph-traversal result.
  5. Decide what happens on match -- write to index=risk for RBA, emit an index=notable event, or route back into your security workflow.
  6. 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:

FieldDescription
whisper_risk_scoreNormalised 0-100, aliased to CIM risk_score
whisper_risk_levelinformational / low / medium / high / critical
whisper_risk_factors_listComma-separated list of contributing factors
whisper_risk_componentsJSON 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_user is imported (via authorize.conf) into the ES roles your analysts use so they can run whisperlookup / whisperquery from the ES search bar.
  • Add TA-whisper-graph to your ES deployment-server bundle if shipping to a distributed environment; see the Deployment Architecture page.
  • 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.