{"id":196,"date":"2025-10-28T09:00:00","date_gmt":"2025-10-28T09:00:00","guid":{"rendered":"http:\/\/justruss.tech\/index.php\/2023\/04\/17\/building-useful-splunk-dashboards-for-a-home-soc\/"},"modified":"2026-05-15T10:34:55","modified_gmt":"2026-05-15T10:34:55","slug":"building-useful-splunk-dashboards-for-a-home-soc","status":"publish","type":"post","link":"https:\/\/justruss.tech\/index.php\/2025\/10\/28\/building-useful-splunk-dashboards-for-a-home-soc\/","title":{"rendered":"Building Useful Splunk Dashboards for a Home SOC"},"content":{"rendered":"<p>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&#8217;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.<\/p>\n<h3>Data ingestion: add-ons before anything else<\/h3>\n<pre>\/\/ Without the correct technology add-ons, Splunk indexes raw event text\n\/\/ Searching raw text is slow, fragile, and makes query writing painful\n\/\/ Install these before building a single search:\n\n\/\/ From Splunkbase:\n\/\/ 1. Splunk Add-on for Microsoft Windows (TA-windows)\n\/\/    - Provides field extractions for Security, System, Application event logs\n\/\/    - Maps Windows event fields to the CIM (Common Information Model)\n\/\/ 2. Splunk Add-on for Sysmon (TA-sysmon or Splunk-Add-on-for-Sysmon-Modular)\n\/\/    - Provides correct field names for all Sysmon event IDs\n\/\/    - Without this, Sysmon data has wrong field names in every query\n\n$SPLUNK_HOME\/bin\/splunk install app TA-windows.tgz -auth admin:yourpassword\n$SPLUNK_HOME\/bin\/splunk install app TA-sysmon.tgz -auth admin:yourpassword\n$SPLUNK_HOME\/bin\/splunk restart\n\n\/\/ Verify extractions are working before building dashboards:\nindex=wineventlog EventCode=4624\n| table _time, host, EventCode, Account_Name, Logon_Type, src_ip\n| head 5\n\n\/\/ If Account_Name is empty, field extraction is not working\n\/\/ Check: Settings &gt; Source types &gt; look for WinEventLog:Security\n\/\/ The add-on should have added field transforms for this source type<\/pre>\n<h3>Panel 1: Authentication monitoring<\/h3>\n<pre>\/\/ Failed logon attempts - core lateral movement and brute force detection\nindex=wineventlog EventCode=4625 earliest=-24h\n| eval Logon_Type_Label=case(\n    Logon_Type==\"2\",  \"Interactive (console)\",\n    Logon_Type==\"3\",  \"Network (remote share\/SMB)\",\n    Logon_Type==\"4\",  \"Batch (scheduled task)\",\n    Logon_Type==\"5\",  \"Service logon\",\n    Logon_Type==\"7\",  \"Unlock workstation\",\n    Logon_Type==\"8\",  \"Network cleartext\",\n    Logon_Type==\"9\",  \"New credentials (runas)\",\n    Logon_Type==\"10\", \"Remote interactive (RDP)\",\n    Logon_Type==\"11\", \"Cached interactive\",\n    true(),           \"Unknown: \" . Logon_Type)\n| stats\n    count AS Failures,\n    dc(src_ip) AS Unique_IPs,\n    values(src_ip) AS Source_IPs,\n    values(Logon_Type_Label) AS Logon_Types\n    by Account_Name, ComputerName\n| where Failures &gt;= 5\n| sort -Failures\n| rename Account_Name AS \"Account\", ComputerName AS \"Target Host\"<\/pre>\n<pre>\/\/ Successful logons after multiple failures (potential successful brute force)\n\/\/ This is the high-priority alert: failed attempts followed by success\nindex=wineventlog (EventCode=4625 OR EventCode=4624) earliest=-2h\n| eval event_type=if(EventCode==4625, \"failure\", \"success\")\n| stats\n    count(eval(event_type=\"failure\")) AS failures,\n    count(eval(event_type=\"success\")) AS successes\n    by Account_Name, src_ip\n| where failures &gt;= 3 AND successes &gt;= 1\n| eval alert=\"Possible successful brute force\"\n| table Account_Name, src_ip, failures, successes, alert\n| sort -failures<\/pre>\n<h3>Panel 2: Process creation anomalies<\/h3>\n<pre>\/\/ Suspicious parent-child process relationships\n\/\/ Office applications should never spawn shells\nindex=sysmon EventCode=1 earliest=-24h\n| eval ParentName=lower(mvindex(split(ParentImage, \"\\\\\"), -1))\n| eval ChildName=lower(mvindex(split(Image, \"\\\\\"), -1))\n| eval combo=ParentName + \" -&gt; \" + ChildName\n| where\n    \/\/ Office apps spawning shells\n    (ParentName IN (\"winword.exe\",\"excel.exe\",\"powerpnt.exe\",\"outlook.exe\",\"onenote.exe\",\"mspub.exe\")\n     AND ChildName IN (\"cmd.exe\",\"powershell.exe\",\"wscript.exe\",\"cscript.exe\",\"mshta.exe\",\"certutil.exe\"))\n    OR\n    \/\/ System processes spawning unexpected children\n    (ParentName IN (\"services.exe\",\"lsass.exe\",\"smss.exe\")\n     AND ChildName NOT IN (\"svchost.exe\",\"lsm.exe\",\"taskhost.exe\",\"conhost.exe\"))\n| stats count by ComputerName, combo, CommandLine\n| sort -count<\/pre>\n<pre>\/\/ Living off the land binaries (LOLBins) used for download or execution\nindex=sysmon EventCode=1 earliest=-24h\n(Image=\"*\\\\certutil.exe\" CommandLine=\"*-decode*\" OR\n Image=\"*\\\\certutil.exe\" CommandLine=\"*urlcache*\" OR\n Image=\"*\\\\mshta.exe\" CommandLine=\"http*\" OR\n Image=\"*\\\\regsvr32.exe\" CommandLine=\"\/s*http*\" OR\n Image=\"*\\\\regsvr32.exe\" CommandLine=\"scrobj*\" OR\n Image=\"*\\\\wscript.exe\" CommandLine=\"*.vbs*\" OR\n Image=\"*\\\\cscript.exe\" CommandLine=\"*.vbs*\" OR\n Image=\"*\\\\rundll32.exe\" CommandLine=\"*javascript*\")\n| table _time, ComputerName, Account_Name, Image, CommandLine\n| sort -_time<\/pre>\n<h3>Panel 3: PowerShell detection<\/h3>\n<pre>\/\/ PowerShell encoded commands with decoded content\nindex=wineventlog EventCode=4688 earliest=-24h\n    (Process_Command_Line=\"*-EncodedCommand*\"\n     OR Process_Command_Line=\"*-enc *\"\n     OR Process_Command_Line=\"*-en *\")\n| rex field=Process_Command_Line\n    \"(?i)(?:-enc|-en|-encodedcommand)\\s+(?P[A-Za-z0-9+\/=]{20,})\"\n| eval decoded_bytes=base64decode(b64_raw)\n| eval decoded=replace(decoded_bytes, \"\\u0000\", \"\")\n| eval is_suspicious=if(\n    match(decoded, \"(?i)(DownloadString|WebClient|IEX|Invoke-Expression|FromBase64)\"),\n    \"HIGH\",\n    \"REVIEW\")\n| table _time, ComputerName, Account_Name, is_suspicious, decoded\n| sort -_time<\/pre>\n<pre>\/\/ Script block logging: all Warning-level PowerShell events\n\/\/ Event ID 4104 Level 3 = PowerShell itself flagged this as suspicious\nindex=wineventlog\n    source=\"Microsoft-Windows-PowerShell\/Operational\"\n    EventCode=4104\n    Level=3\n    earliest=-24h\n| rex field=_raw \"ScriptBlockText.*?(?P\\S.*?)(?=ScriptBlockId|\\Z)\"\n| eval clean_script=substr(script_content, 1, 500)\n| table _time, ComputerName, clean_script\n| sort -_time<\/pre>\n<h3>Panel 4: Persistence mechanism monitoring<\/h3>\n<pre>\/\/ New scheduled tasks (Event ID 4698) outside standard paths\nindex=wineventlog EventCode=4698 earliest=-48h\n| rex field=_raw \"Task Name:\\s+(?P[^\\r\\n]+)\"\n| rex field=_raw \"&lt;Command&gt;(?P[^&lt;]+)&lt;\/Command&gt;\"\n| rex field=_raw \"&lt;Arguments&gt;(?P[^&lt;]+)&lt;\/Arguments&gt;\"\n| where NOT match(task_cmd, \"(?i)^(C:\\\\\\\\Windows\\\\\\\\|C:\\\\\\\\Program Files)\")\n| table _time, ComputerName, Subject_Account_Name, task_name, task_cmd, task_args\n| sort -_time<\/pre>\n<pre>\/\/ New services installed (Event ID 7045)\nindex=wineventlog EventCode=7045 earliest=-48h\n| eval suspicious=if(\n    match(Service_File_Name, \"(?i)(temp|appdata|public|downloads|desktop)\"),\n    \"HIGH\",\n    if(match(Service_File_Name, \"(?i)(powershell|cmd|wscript|cscript|mshta)\"),\n    \"HIGH\", \"REVIEW\"))\n| table _time, ComputerName, Service_Name, Service_File_Name, Service_Account, suspicious\n| sort -_time<\/pre>\n<pre>\/\/ Registry run key modifications (Sysmon Event ID 13)\nindex=sysmon EventCode=13 earliest=-24h\n    TargetObject IN (\n        \"*\\\\CurrentVersion\\\\Run*\",\n        \"*\\\\CurrentVersion\\\\RunOnce*\",\n        \"*\\\\Windows\\\\CurrentVersion\\\\Policies\\\\Explorer\\\\Run*\"\n    )\n| where NOT match(Details, \"(?i)^(C:\\\\\\\\Windows\\\\\\\\|C:\\\\\\\\Program Files)\")\n| table _time, ComputerName, User, TargetObject, Details\n| sort -_time<\/pre>\n<h3>Panel 5: Network anomalies<\/h3>\n<pre>\/\/ Uncommon outbound ports from workstations\n\/\/ Workstations should mostly use 80, 443, 53, 88, 389, 636\nindex=sysmon EventCode=3 earliest=-24h\n    NOT DestinationPort IN (80, 443, 53, 88, 389, 636, 3268, 3269)\n    NOT DestinationIp IN (\"10.0.0.0\/8\", \"172.16.0.0\/12\", \"192.168.0.0\/16\")\n| stats count by DestinationIp, DestinationPort, Image, ComputerName\n| where count &gt; 1\n| sort -count<\/pre>\n<pre>\/\/ DNS queries to newly registered or suspicious domains\n\/\/ Requires Sysmon Event ID 22 (DNS query logging) enabled\nindex=sysmon EventCode=22 earliest=-24h\n    NOT QueryName IN (\"*.microsoft.com\",\"*.windows.com\",\"*.office.com\")\n    QueryResults=\"*\"\n| stats count by QueryName, Image, ComputerName\n| where count &lt; 3  \/\/ Rare queries are more interesting than common ones\n| sort count<\/pre>\n<h3>Building and maintaining the dashboards<\/h3>\n<p>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.<\/p>\n<p>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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most Splunk tutorials show dashboards with clean data. Real log<br \/>\ndata is messy. The searches that actually work.<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[],"class_list":["post-196","post","type-post","status-publish","format-standard","hentry","category-threat-hunting"],"_links":{"self":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/196","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/comments?post=196"}],"version-history":[{"count":4,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/196\/revisions"}],"predecessor-version":[{"id":285,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/196\/revisions\/285"}],"wp:attachment":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/media?parent=196"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/categories?post=196"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/tags?post=196"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}