Building Useful Splunk Dashboards for a Home SOC

28 October 2025 | 6 min read | justruss.tech

Every Splunk tutorial shows clean, pre-formatted data producing beautiful dashboards. Real log data is messy. Field names do not match what documentation says, queries that worked in someone else’s environment return nothing, and alert volume makes even useful detections functionally useless if they are not tuned. This covers what actually works for a home SOC setup, starting from raw data ingestion and building up to functioning detection panels.

Data ingestion: add-ons before anything else

// Without the correct technology add-ons, Splunk indexes raw event text
// Searching raw text is slow, fragile, and makes query writing painful
// Install these before building a single search:

// From Splunkbase:
// 1. Splunk Add-on for Microsoft Windows (TA-windows)
//    - Provides field extractions for Security, System, Application event logs
//    - Maps Windows event fields to the CIM (Common Information Model)
// 2. Splunk Add-on for Sysmon (TA-sysmon or Splunk-Add-on-for-Sysmon-Modular)
//    - Provides correct field names for all Sysmon event IDs
//    - Without this, Sysmon data has wrong field names in every query

$SPLUNK_HOME/bin/splunk install app TA-windows.tgz -auth admin:yourpassword
$SPLUNK_HOME/bin/splunk install app TA-sysmon.tgz -auth admin:yourpassword
$SPLUNK_HOME/bin/splunk restart

// Verify extractions are working before building dashboards:
index=wineventlog EventCode=4624
| table _time, host, EventCode, Account_Name, Logon_Type, src_ip
| head 5

// If Account_Name is empty, field extraction is not working
// Check: Settings > Source types > look for WinEventLog:Security
// The add-on should have added field transforms for this source type

Panel 1: Authentication monitoring

// Failed logon attempts - core lateral movement and brute force detection
index=wineventlog EventCode=4625 earliest=-24h
| eval Logon_Type_Label=case(
    Logon_Type=="2",  "Interactive (console)",
    Logon_Type=="3",  "Network (remote share/SMB)",
    Logon_Type=="4",  "Batch (scheduled task)",
    Logon_Type=="5",  "Service logon",
    Logon_Type=="7",  "Unlock workstation",
    Logon_Type=="8",  "Network cleartext",
    Logon_Type=="9",  "New credentials (runas)",
    Logon_Type=="10", "Remote interactive (RDP)",
    Logon_Type=="11", "Cached interactive",
    true(),           "Unknown: " . Logon_Type)
| stats
    count AS Failures,
    dc(src_ip) AS Unique_IPs,
    values(src_ip) AS Source_IPs,
    values(Logon_Type_Label) AS Logon_Types
    by Account_Name, ComputerName
| where Failures >= 5
| sort -Failures
| rename Account_Name AS "Account", ComputerName AS "Target Host"
// Successful logons after multiple failures (potential successful brute force)
// This is the high-priority alert: failed attempts followed by success
index=wineventlog (EventCode=4625 OR EventCode=4624) earliest=-2h
| eval event_type=if(EventCode==4625, "failure", "success")
| stats
    count(eval(event_type="failure")) AS failures,
    count(eval(event_type="success")) AS successes
    by Account_Name, src_ip
| where failures >= 3 AND successes >= 1
| eval alert="Possible successful brute force"
| table Account_Name, src_ip, failures, successes, alert
| sort -failures

Panel 2: Process creation anomalies

// Suspicious parent-child process relationships
// Office applications should never spawn shells
index=sysmon EventCode=1 earliest=-24h
| eval ParentName=lower(mvindex(split(ParentImage, "\\"), -1))
| eval ChildName=lower(mvindex(split(Image, "\\"), -1))
| eval combo=ParentName + " -> " + ChildName
| where
    // Office apps spawning shells
    (ParentName IN ("winword.exe","excel.exe","powerpnt.exe","outlook.exe","onenote.exe","mspub.exe")
     AND ChildName IN ("cmd.exe","powershell.exe","wscript.exe","cscript.exe","mshta.exe","certutil.exe"))
    OR
    // System processes spawning unexpected children
    (ParentName IN ("services.exe","lsass.exe","smss.exe")
     AND ChildName NOT IN ("svchost.exe","lsm.exe","taskhost.exe","conhost.exe"))
| stats count by ComputerName, combo, CommandLine
| sort -count
// Living off the land binaries (LOLBins) used for download or execution
index=sysmon EventCode=1 earliest=-24h
(Image="*\\certutil.exe" CommandLine="*-decode*" OR
 Image="*\\certutil.exe" CommandLine="*urlcache*" OR
 Image="*\\mshta.exe" CommandLine="http*" OR
 Image="*\\regsvr32.exe" CommandLine="/s*http*" OR
 Image="*\\regsvr32.exe" CommandLine="scrobj*" OR
 Image="*\\wscript.exe" CommandLine="*.vbs*" OR
 Image="*\\cscript.exe" CommandLine="*.vbs*" OR
 Image="*\\rundll32.exe" CommandLine="*javascript*")
| table _time, ComputerName, Account_Name, Image, CommandLine
| sort -_time

Panel 3: PowerShell detection

// PowerShell encoded commands with decoded content
index=wineventlog EventCode=4688 earliest=-24h
    (Process_Command_Line="*-EncodedCommand*"
     OR Process_Command_Line="*-enc *"
     OR Process_Command_Line="*-en *")
| rex field=Process_Command_Line
    "(?i)(?:-enc|-en|-encodedcommand)\s+(?P[A-Za-z0-9+/=]{20,})"
| eval decoded_bytes=base64decode(b64_raw)
| eval decoded=replace(decoded_bytes, "\u0000", "")
| eval is_suspicious=if(
    match(decoded, "(?i)(DownloadString|WebClient|IEX|Invoke-Expression|FromBase64)"),
    "HIGH",
    "REVIEW")
| table _time, ComputerName, Account_Name, is_suspicious, decoded
| sort -_time
// Script block logging: all Warning-level PowerShell events
// Event ID 4104 Level 3 = PowerShell itself flagged this as suspicious
index=wineventlog
    source="Microsoft-Windows-PowerShell/Operational"
    EventCode=4104
    Level=3
    earliest=-24h
| rex field=_raw "ScriptBlockText.*?(?P\S.*?)(?=ScriptBlockId|\Z)"
| eval clean_script=substr(script_content, 1, 500)
| table _time, ComputerName, clean_script
| sort -_time

Panel 4: Persistence mechanism monitoring

// New scheduled tasks (Event ID 4698) outside standard paths
index=wineventlog EventCode=4698 earliest=-48h
| rex field=_raw "Task Name:\s+(?P[^\r\n]+)"
| rex field=_raw "<Command>(?P[^<]+)</Command>"
| rex field=_raw "<Arguments>(?P[^<]+)</Arguments>"
| where NOT match(task_cmd, "(?i)^(C:\\\\Windows\\\\|C:\\\\Program Files)")
| table _time, ComputerName, Subject_Account_Name, task_name, task_cmd, task_args
| sort -_time
// New services installed (Event ID 7045)
index=wineventlog EventCode=7045 earliest=-48h
| eval suspicious=if(
    match(Service_File_Name, "(?i)(temp|appdata|public|downloads|desktop)"),
    "HIGH",
    if(match(Service_File_Name, "(?i)(powershell|cmd|wscript|cscript|mshta)"),
    "HIGH", "REVIEW"))
| table _time, ComputerName, Service_Name, Service_File_Name, Service_Account, suspicious
| sort -_time
// Registry run key modifications (Sysmon Event ID 13)
index=sysmon EventCode=13 earliest=-24h
    TargetObject IN (
        "*\\CurrentVersion\\Run*",
        "*\\CurrentVersion\\RunOnce*",
        "*\\Windows\\CurrentVersion\\Policies\\Explorer\\Run*"
    )
| where NOT match(Details, "(?i)^(C:\\\\Windows\\\\|C:\\\\Program Files)")
| table _time, ComputerName, User, TargetObject, Details
| sort -_time

Panel 5: Network anomalies

// Uncommon outbound ports from workstations
// Workstations should mostly use 80, 443, 53, 88, 389, 636
index=sysmon EventCode=3 earliest=-24h
    NOT DestinationPort IN (80, 443, 53, 88, 389, 636, 3268, 3269)
    NOT DestinationIp IN ("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16")
| stats count by DestinationIp, DestinationPort, Image, ComputerName
| where count > 1
| sort -count
// DNS queries to newly registered or suspicious domains
// Requires Sysmon Event ID 22 (DNS query logging) enabled
index=sysmon EventCode=22 earliest=-24h
    NOT QueryName IN ("*.microsoft.com","*.windows.com","*.office.com")
    QueryResults="*"
| stats count by QueryName, Image, ComputerName
| where count < 3  // Rare queries are more interesting than common ones
| sort count

Building and maintaining the dashboards

The rule that makes the most practical difference: build one panel at a time, run it against one full week of live data, review every single result manually, and document what is normal before treating anything as an alert. A panel that fires 200 times per day because you have legitimate admin scripts running is worse than no detection at all, because it trains you to ignore it.

For each panel, maintain an exclusion list in a CSV lookup file. Add legitimate findings to the exclusion list with a date and reason. Review the exclusion list every quarter because software changes and exclusions go stale.