Implement Kibana 8 custom threat hunting dashboards for security incident response

Advanced 90 min Sep 17, 2026 141 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Build production-grade Kibana 8 threat hunting dashboards with MITRE ATT&CK mapping, Lens visualizations, drilldowns, KQL detections and role-based access control for SOC teams. Includes saved objects API automation for version-controlled dashboard deployment.

Prerequisites

  • Running Elasticsearch 8 and Kibana 8 cluster with security logs already indexed
  • Elastic Stack security features enabled with a superuser account
  • Familiarity with KQL and basic Elasticsearch mappings
  • curl and jq installed for API testing

What this solves

SOC analysts need more than default Kibana dashboards to hunt threats efficiently. This tutorial builds custom Kibana 8 dashboards mapped to MITRE ATT&CK tactics, with drilldowns, saved KQL detections, TSVB anomaly panels, alerting rules and RBAC for security teams.

You will index security logs into Elasticsearch, build Lens visualizations for threat indicators, wire up cross-dashboard drilldowns for incident investigation, and automate deployment with the saved objects API so dashboards live in version control instead of clicking around the UI.

Prerequisite: This tutorial assumes you already have Elasticsearch and Kibana 8 running with security logs flowing in. If you need the ingestion pipeline first, see configure Logstash 8 security pipelines with threat intelligence enrichment and install and configure Filebeat 8.15 for log shipping to ELK.

Step-by-step configuration

Create dedicated indices for security data sources

Threat hunting dashboards perform best when firewall, EDR, auth and proxy logs land in clearly separated indices rather than one generic catch-all. Create index templates so field mappings stay consistent across data sources.

curl -X PUT "https://localhost:9200/_index_template/security-auth" \
  -u elastic:YourStrongPass123! \
  -H "Content-Type: application/json" \
  -d '{
    "index_patterns": ["security-auth-*"],
    "template": {
      "settings": { "number_of_shards": 1, "number_of_replicas": 1 },
      "mappings": {
        "properties": {
          "@timestamp": { "type": "date" },
          "source.ip": { "type": "ip" },
          "destination.ip": { "type": "ip" },
          "user.name": { "type": "keyword" },
          "event.outcome": { "type": "keyword" },
          "threat.tactic.name": { "type": "keyword" },
          "threat.technique.id": { "type": "keyword" },
          "host.name": { "type": "keyword" }
        }
      }
    }
  }'

Repeat this pattern for security-network-, security-endpoint- and security-proxy-. Consistent ECS-aligned field names like threat.technique.id are what let a single dashboard query across all of them later.

Build a unified data view

Kibana 8 data views replace legacy index patterns. Create one data view spanning all security indices so analysts can pivot between log sources without switching context.

curl -X POST "https://localhost:5601/api/data_views/data_view" \
  -u elastic:YourStrongPass123! \
  -H "kbn-xsrf: true" -H "Content-Type: application/json" \
  -d '{
    "data_view": {
      "title": "security-*",
      "name": "Security - All sources",
      "timeFieldName": "@timestamp"
    }
  }'

Also create narrower data views (security-auth-, security-endpoint-) for panels that only make sense against one log source, such as failed login trend charts.

Add runtime fields for MITRE ATT&CK enrichment

If your ingestion pipeline does not already tag events with ATT&CK technique IDs, add a runtime field in the data view so you can still group and filter by tactic in dashboards without reindexing.

curl -X POST "https://localhost:5601/api/data_views/data_view/security-view-id/runtime_field" \
  -u elastic:YourStrongPass123! \
  -H "kbn-xsrf: true" -H "Content-Type: application/json" \
  -d '{
    "name": "threat.tactic.derived",
    "runtimeField": {
      "type": "keyword",
      "script": {
        "source": "if (doc.containsKey('\''event.action'\'') && doc['\''event.action'\''].size() > 0 && doc['\''event.action'\''].value == '\''process_created'\'') { emit('\''execution'\''); } else { emit('\''unclassified'\''); }"
      }
    }
  }'

In production, prefer enriching at ingest time in Logstash rather than relying heavily on runtime fields, since runtime scripts add query-time overhead at scale.

Build Lens visualizations for threat indicators

Open Kibana, go to Visualize Library and create a new Lens visualization. Build a top-N bar chart of source.ip by count, filtered to event.outcome: failure, to surface brute-force sources.

curl -X POST "https://localhost:5601/api/saved_objects/lens/failed-auth-top-ips" \
  -u elastic:YourStrongPass123! \
  -H "kbn-xsrf: true" -H "Content-Type: application/json" \
  -d '{
    "attributes": {
      "title": "Failed auth - top source IPs",
      "visualizationType": "lnsXY",
      "state": {
        "datasourceStates": {},
        "visualization": {},
        "query": { "query": "event.outcome: failure", "language": "kuery" },
        "filters": []
      }
    },
    "references": []
  }'

Repeat for other indicators: unique destination ports per host (port scanning), rare process command lines (living-off-the-land), and DNS query entropy for beaconing. Build each as its own Lens panel so it can be reused across multiple dashboards.

Create MITRE ATT&CK mapped dashboards

Structure dashboards by tactic rather than by log source. Create one dashboard per tactic group (initial access, execution, persistence, lateral movement, exfiltration) with panels tagged to specific technique IDs.

curl -X POST "https://localhost:5601/api/saved_objects/dashboard/mitre-lateral-movement" \
  -u elastic:YourStrongPass123! \
  -H "kbn-xsrf: true" -H "Content-Type: application/json" \
  -d '{
    "attributes": {
      "title": "MITRE ATT&CK - Lateral movement (TA0008)",
      "description": "Panels for T1021 remote services, T1550 alt auth material, T1563 RDP hijack",
      "panelsJSON": "[]",
      "optionsJSON": "{\"useMargins\":true,\"hidePanelTitles\":false}",
      "timeRestore": true,
      "kibanaSavedObjectMeta": {
        "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"
      }
    },
    "references": []
  }'

Add tags per technique so analysts can filter the dashboard listing page by MITRE tactic. Use the Kibana tag manager under Stack Management to create tags like TA0008-lateral-movement and TA0001-initial-access.

Configure drilldowns for incident investigation

Drilldowns let an analyst click a suspicious IP in one panel and jump straight into a filtered view of related events. Open a panel's context menu, choose Create drilldown, and configure a dashboard-to-dashboard drilldown.

Note: Set the drilldown to carry over the clicked value as a filter (for example source.ip) and preserve the current time range so the investigation stays scoped to the incident window.

A typical drilldown chain: overview dashboard, click a flagged host, land on an endpoint-detail dashboard scoped to that host, then drill down again into a raw document table filtered by the same host and time range for full packet or process detail.

Save KQL queries for common attack patterns

Saved searches with KQL let analysts reuse detection logic across dashboards and Discover sessions. Create saved searches for patterns like impossible travel logins, privilege escalation attempts and suspicious PowerShell execution.

curl -X POST "https://localhost:5601/api/saved_objects/search/suspicious-powershell" \
  -u elastic:YourStrongPass123! \
  -H "kbn-xsrf: true" -H "Content-Type: application/json" \
  -d '{
    "attributes": {
      "title": "Suspicious PowerShell execution",
      "columns": ["@timestamp", "host.name", "user.name", "process.command_line"],
      "sort": [["@timestamp", "desc"]],
      "kibanaSavedObjectMeta": {
        "searchSourceJSON": "{\"query\":{\"query\":\"process.name: \\\"powershell.exe\\\" and process.command_line: (*-EncodedCommand* or *-nop* or *bypass* or *DownloadString*)\",\"language\":\"kuery\"},\"filter\":[]}"
      }
    },
    "references": [{"name": "kibanaSavedObjectMeta.searchSourceJSON.index", "type": "index-pattern", "id": "security-endpoint-view"}]
  }'

Other useful KQL patterns to save: destination.port: (4444 or 1337 or 31337) for known reverse-shell ports, dns.question.name: /.{30,}/ for long-domain DNS tunneling candidates, and event.action: \"user_login\" and geo.country_iso_code: paired with a Lens geo map for impossible travel checks.

Implement Kibana alerting rules for automated detection

Go to Stack Management, Rules and Connectors, and create an Elasticsearch query rule. This example alerts when more than 10 failed logins from a single IP occur within 5 minutes.

curl -X POST "https://localhost:5601/api/alerting/rule" \
  -u elastic:YourStrongPass123! \
  -H "kbn-xsrf: true" -H "Content-Type: application/json" \
  -d '{
    "name": "Brute force - repeated auth failures",
    "rule_type_id": ".es-query",
    "consumer": "alerts",
    "schedule": { "interval": "1m" },
    "params": {
      "index": ["security-auth-*"],
      "timeField": "@timestamp",
      "esQuery": "{\"query\":{\"bool\":{\"filter\":[{\"term\":{\"event.outcome\":\"failure\"}}]}}}",
      "size": 100,
      "thresholdComparator": ">",
      "threshold": [10],
      "timeWindowSize": 5,
      "timeWindowUnit": "m",
      "aggType": "count",
      "groupBy": "top",
      "termField": "source.ip",
      "termSize": 20
    },
    "actions": []
  }'

Attach a connector action (Slack, email or a webhook into your SOAR) so the rule pages the on-call analyst instead of silently logging a match. For high-volume environments, tune the check interval and grouping cardinality to avoid alert fatigue.

Add TSVB panels for anomaly and trend analysis

TSVB (Time Series Visual Builder) handles multi-metric time series better than Lens for baseline deviation charts. Create a TSVB panel showing a 7-day moving average of authentication failures against the current day's actual count.

curl -X POST "https://localhost:5601/api/saved_objects/visualization/auth-failure-anomaly-tsvb" \
  -u elastic:YourStrongPass123! \
  -H "kbn-xsrf: true" -H "Content-Type: application/json" \
  -d '{
    "attributes": {
      "title": "Auth failure trend vs 7-day baseline",
      "visState": "{\"title\":\"Auth failure trend vs 7-day baseline\",\"type\":\"metrics\",\"params\":{\"type\":\"timeseries\",\"index_pattern\":\"security-auth-*\",\"time_field\":\"@timestamp\",\"interval\":\"1h\"}}",
      "uiStateJSON": "{}",
      "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}" }
    },
    "references": []
  }'

Add a moving average and a static baseline series to the same panel so analysts can spot deviation visually rather than reading raw counts. TSVB annotations also work well for marking known maintenance windows so spikes during patching are not mistaken for attacks.

Secure dashboards with role-based access control

SOC dashboards often contain sensitive data like user names, IPs and process paths. Restrict access with Kibana feature privileges and space-level roles rather than granting all analysts the full admin role.

curl -X PUT "https://localhost:9200/_security/role/soc_analyst" \
  -u elastic:YourStrongPass123! \
  -H "Content-Type: application/json" \
  -d '{
    "indices": [
      { "names": ["security-*"], "privileges": ["read", "view_index_metadata"] }
    ],
    "applications": [
      {
        "application": "kibana-.kibana",
        "privileges": ["feature_dashboard.read", "feature_discover.all", "feature_visualize.read"],
        "resources": ["space:soc"]
      }
    ]
  }'

Create a separate soc_lead role with feature_dashboard.all and alerting management privileges so only leads can edit dashboards or acknowledge alert rules. Put SOC dashboards in a dedicated Kibana space so they never mix with unrelated business dashboards.

Never grant the superuser role for daily analyst work. Scope roles to the specific indices and Kibana features analysts actually need. Broad privileges turn a compromised analyst account into a full cluster compromise.

For deeper field-level restrictions, for example hiding raw credential hashes from junior analysts while still showing them to leads, see configure Kibana 8 advanced security with field-level restrictions and role-based access control.

Export dashboards with the saved objects API

Export the full set of security dashboards, visualizations and saved searches as a single NDJSON bundle for version control.

curl -X POST "https://localhost:5601/api/saved_objects/_export" \
  -u elastic:YourStrongPass123! \
  -H "kbn-xsrf: true" -H "Content-Type: application/json" \
  -d '{
    "type": ["dashboard", "lens", "visualization", "search", "index-pattern"],
    "includeReferencesDeep": true
  }' -o soc-dashboards-export.ndjson

Commit this file to a Git repository dedicated to Kibana configuration. Treat it exactly like infrastructure-as-code, with pull requests reviewed before changes reach production.

Automate deployment across environments

Import the NDJSON bundle into staging or production Kibana instances with the import API. Use overwrite=true in CI/CD pipelines so re-running the import updates existing objects instead of failing on conflicts.

curl -X POST "https://kibana-prod.internal:5601/api/saved_objects/_import?overwrite=true" \
  -u kibana_deploy:YourStrongPass123! \
  -H "kbn-xsrf: true" \
  --form file=@soc-dashboards-export.ndjson

Wrap this in a small deploy script triggered from your CI pipeline so a merged pull request against the dashboards repo automatically pushes to staging, and a tagged release pushes to production.

#!/bin/bash
set -euo pipefail

KIBANA_URL="$1"
KIBANA_USER="$2"
KIBANA_PASS="$3"

curl -sf -X POST "${KIBANA_URL}/api/saved_objects/_import?overwrite=true" \
  -u "${KIBANA_USER}:${KIBANA_PASS}" \
  -H "kbn-xsrf: true" \
  --form file=@soc-dashboards-export.ndjson

echo "Deployed SOC dashboards to ${KIBANA_URL}"
Note: Store the deploy credentials in a secrets manager rather than hardcoding them in the script. See configure Ansible Vault integration with HashiCorp Vault for secrets management for a pattern you can adapt to CI/CD pipeline variables.

Verify your setup

curl -s -u elastic:YourStrongPass123! "https://localhost:5601/api/saved_objects/_find?type=dashboard&search_fields=title&search=MITRE" | jq '.saved_objects[].attributes.title'
curl -s -u elastic:YourStrongPass123! "https://localhost:5601/api/alerting/rules/_find" | jq '.data[].name'
curl -s -u elastic:YourStrongPass123! "https://localhost:9200/_security/role/soc_analyst" | jq '.'

Log in to Kibana as a test user assigned only the soc_analyst role and confirm they can view dashboards but cannot edit them or access unrelated spaces.

Common issues

SymptomCauseFix
Dashboard import fails with reference errorsMissing data view or visualization referenced by the dashboard was not included in exportRe-run export with includeReferencesDeep: true and include all dependent object types
Alert rule never firesIndex pattern in rule params does not match actual index namesConfirm params.index matches live indices with GET _cat/indices/security-*
Analyst cannot see any dashboardsRole missing feature_dashboard.read privilege for the correct spaceUpdate the role's applications block with the correct space resource
TSVB panel shows no dataTime field mismatch between data view and index mappingConfirm timeFieldName in the data view matches the mapped @timestamp field type
Drilldown filter does not apply on target dashboardTarget dashboard's data view does not contain the filtered fieldUse a shared data view across source and target da

Nie chcesz zarządzać tym samodzielnie?

Zarządzamy infrastrukturą firm, które zależą od dostępności. W pełni zarządzana, z jednym stałym kontaktem, który zna Twoje środowisko.

Macie jednego stałego opiekuna, który zna Waszą konfigurację

Rotterdam 01:38 · dostępny w wiadomości, bez formularza zgłoszeń