Baseline Bonanza: Ten Baseline Hunts You Should Do (and How to Do Them)
Because attackers hide in plain sight... until you know what's normal.
Every threat hunting program needs a solid foundation. That’s why I decided to sneak into Sydney’s baseline blog post series and make part 2!
Whether you're building a new hunting capability or validating an existing one, baseline hunts help you understand what "normal" looks like in your environment. Make the abnormal stand out like a beacon. 🚨
Today, we are sharing ten essential baseline hunts that every organization should run regularly. These aren't 0day hunts, but they're the bread and butter that catch real adversaries. These are tool-agnostic approaches with enough detail to get you started, regardless of your tech stack.
Why Baseline Hunts Matter
Before diving into the hunts, let's be clear: baseline hunts aren't about finding sophisticated nation-state actors (though you might). As Sydney discussed in her article, they're about:
Establishing what's normal in YOUR environment
Catching the low-hanging fruit that often indicates bigger problems
Building repeatable processes that junior analysts can execute
Creating institutional knowledge about your environment's quirks
Ten Essential Baseline Hunts
Queries may need tweaking based on your environment, logging structure, and indexes. Edit as needed.
Scheduled Task/Cron Job Baseline
What You're Hunting:
Persistence and execution through scheduled tasks that deviate from your environment's norm
Key Data Sources:
Windows: Event IDs 4698 (task created), 4700 (task enabled), 4702 (task updated)
Windows: Schedule service operational logs (Microsoft-Windows-TaskScheduler/Operational)
Linux: /var/log/cron, ausearch for crontab changes, systemd timer logs
Cloud: CloudWatch Events, Azure Logic Apps, GCP Cloud Scheduler
The Hunt:
Start by establishing your scheduled task baseline:
- WHO typically creates tasks? (IT automation accounts, deployment tools, specific admins)
- WHEN are tasks created? (maintenance windows, deployment schedules)
- WHERE do task actions point? (legitimate software paths, admin script directories)
- WHAT are normal task names? (vendor patterns, naming conventions)
Specific Things to Hunt:
Suspicious Task Characteristics
# Tasks running from suspicious paths
event_type="windows_task_created" OR EventID=4698
| eval suspicious_path = case(
match(TaskPath, "(?i)\\\\Temp\\\\"), 1,
match(TaskPath, "(?i)\\\\AppData\\\\"), 1,
match(TaskPath, "(?i)\\\\Public\\\\"), 1,
match(TaskPath, "(?i)\\\\ProgramData\\\\"), 1,
match(TaskPath, "(?i)\\\\Users\\\\.*\\\\Desktop\\\\"), 1,
1=1, 0)
| where suspicious_path=1
| table _time, TaskName, TaskPath, TaskAuthor, TaskCommand
# Detect base64 encoded or obfuscated commands
EventID=4698 OR event_type="scheduled_task"
| regex TaskContent="(-enc|-encodedcommand|FromBase64String|ToCharArray|cmd.*/[cC]|powershell.*-[eE])"
| stats count by TaskName, TaskCommand, ComputerName
# Tasks with high-frequency triggers (beaconing)
EventID=4698 OR event_type="windows_task_created"
| rex field=TaskContent "(?i)<Interval>PT(?<interval>\d+)M</Interval>"
| where isnotnull(interval) AND interval <= 5
| table TaskName, interval, TaskCommand, TaskAuthor
# Hidden tasks (names with special characters or GUID-like)
EventID=4698
| regex TaskName="^(\.|~|\$|{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}})"
OR regex TaskName="^[a-zA-Z]{6,10}$"
| stats values(TaskCommand) as commands by TaskName, ComputerName
# SYSTEM tasks with user-writable targets
EventID=4698 TaskUser="SYSTEM"
| regex TaskCommand="(?i)(\\\\Users\\\\|\\\\Temp\\\\|\\\\AppData\\\\|%USERPROFILE%)"
| table _time, TaskName, TaskCommand, TaskPath
| eval risk_score = "CRITICAL - SYSTEM executing from user-writable location"
Anomalous Creation Patterns
EventID = 4698 AND (
TaskName contains random_looking_string OR
TaskPath contains "\\\\Users\\\\" OR
Command contains "powershell -enc" OR
Command contains "cmd /c" followed by multiple commands OR
CreationTime outside business_hours AND
Creator not in approved_automation_accounts
)
Linux Cron Indicators
# Monitor cron file modifications
event_type="file_modification"
(file_path="/etc/crontab" OR
file_path="/etc/cron.*" OR
file_path="/var/spool/cron/*" OR
file_path="/etc/cron.d/*")
| stats count by user, file_path, action
| where user!="root" OR action="created"
# Detect new cron entries with suspicious commands
event_type="auditd" (type="SYSCALL" AND exe="/usr/bin/crontab")
OR (type="PATH" AND (name contains "/etc/cron" OR name contains "/var/spool/cron"))
| transaction pid maxspan=5s
| search success="yes"
| stats values(comm) as commands, values(name) as modified_files by user, hostname
# Hunt for wget/curl in cron jobs (common backdoor pattern)
(event_type="process_creation" parent_process="cron" OR parent_process="crond")
AND (process_name IN ("wget", "curl", "nc", "netcat", "bash", "sh", "python*", "perl"))
| regex commandline="(http|https|ftp|tcp|udp)://|([0-9]{1,3}\.){3}[0-9]{1,3}"
| stats count by process_name, commandline, user
# Systemd timer creation/modification
event_type="file_creation" OR event_type="file_modification"
(file_path="/etc/systemd/system/*.timer" OR
file_path="/lib/systemd/system/*.timer" OR
file_path="/usr/lib/systemd/system/*.timer")
| join file_path [
search event_type="file_*" file_path="*.service" earliest=-1m
]
| table _time, user, file_path, service_file, action
# Detect @reboot persistence
event_type="file_content" source="/var/log/cron*" OR source="auditd"
| regex content="@reboot|@startup"
| where user!="root" AND user!="systemd*"
| stats count by user, content
Cloud Scheduled Executions
# AWS - Lambda functions with suspicious schedules
event_source="cloudtrail" eventName="PutRule" OR eventName="PutTargets"
| regex requestParameters.scheduleExpression="(rate\(\d+\s*minute|cron\(\*/[1-5]|\*/10)"
| join requestParameters.targets{}.arn [
search event_source="cloudtrail" eventName="CreateFunction" OR eventName="UpdateFunctionCode"
| rename requestParameters.functionName as function_arn
]
| table _time, userName, requestParameters.name, scheduleExpression, targets{}.arn
# AWS - CloudWatch Events with encoded/suspicious targets
event_source="cloudtrail" (eventName="PutRule" OR eventName="PutTargets")
| regex requestParameters.targets{}.input="(base64|eval|exec|powershell|/bin/bash|curl|wget)"
| stats values(requestParameters.description) as description,
values(requestParameters.scheduleExpression) as schedule
by userName, requestParameters.name, requestParameters.targets{}.arn
# Azure - Logic Apps/Automation with external calls
event_source="azure_activity" operationName="Microsoft.Logic/workflows/write"
OR operationName="Microsoft.Automation/automationAccounts/runbooks/write"
| regex properties.definition="(http|https)://(?![a-z0-9\-]+\.(azure|microsoft|windows)\.)"
| where properties.definition contains "recurrence"
| eval trigger_frequency = if(match(properties.definition, "\"frequency\":\s*\"Minute\""), "HIGH_RISK", "MEDIUM_RISK")
| table _time, caller, resourceName, trigger_frequency
# GCP - Cloud Scheduler jobs targeting unusual resources
event_source="gcp_audit" protoPayload.methodName="google.cloud.scheduler.v1.CloudScheduler.CreateJob"
OR protoPayload.methodName="google.cloud.scheduler.v1.CloudScheduler.UpdateJob"
| regex protoPayload.request.job.httpTarget.uri="^(?!https://[a-z0-9\-]+\.(googleapis|google|googleusercontent)\.com)"
| stats values(protoPayload.request.job.schedule) as schedule,
values(protoPayload.request.job.httpTarget.uri) as target_url
by protoPayload.authenticationInfo.principalEmail, protoPayload.request.job.name
# Cross-cloud - Rapid creation of scheduled tasks
(event_source="cloudtrail" eventName="PutRule") OR
(event_source="azure_activity" operationName contains "schedule") OR
(event_source="gcp_audit" methodName contains "scheduler")
| bucket _time span=10m
| stats count by user_identity, _time
| where count > 5
| eval alert="Multiple scheduled tasks created in short window"
# Lambda/Function with admin role attachments
event_source="cloudtrail" eventName="AttachRolePolicy" OR eventName="PutRolePolicy"
| where requestParameters.policyArn="arn:aws:iam::aws:policy/AdministratorAccess"
OR requestParameters.policyDocument contains "\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\""
| join requestParameters.roleName [
search event_source="cloudtrail" eventName="CreateFunction"
| rename requestParameters.role as roleName
]
| table _time, userName, roleName, functionName, policyArn
Quick Win Detection Logic:
# Windows baseline
Get all scheduled tasks for 30 days, track:
- Task names and paths
- Creating accounts
- Action executables
- Trigger patterns
# Detection query
New_Scheduled_Task WHERE (
ActionPath not in baseline_paths OR
ActionPath contains_any ["temp", "appdata", "public", "programdata"] OR
TaskName matches "^[a-zA-Z]{6,10}$" OR # Random-looking names
Trigger = "At startup" AND Creator != "SYSTEM" OR
Multiple_Triggers_Per_Hour = true
)
Building Your Baseline:
# Build your baseline
baseline_tasks = {
'legitimate_paths': [
'C:\\\\\\\\Program Files\\\\\\\\*',
'C:\\\\\\\\Windows\\\\\\\\System32\\\\\\\\*',
'C:\\\\\\\\ProgramData\\\\\\\\Microsoft\\\\\\\\*'
],
'known_task_names': collect_30_days(),
'automation_accounts': ['SYSTEM', 'svc_deployment', 'IT_Automation'],
'normal_creation_hours': (6, 20), # 6 AM to 8 PM
}
# Hunt logic
for new_task in today:
if new_task.name not in baseline_tasks['known_task_names']:
if new_task.action_path not in baseline_tasks['legitimate_paths']:
flag_suspicious()
if random_pattern.match(new_task.name):
flag_suspicious()
if new_task.creator not in baseline_tasks['automation_accounts']:
flag_for_review()
Tuning Tips:
Document all legitimate automation tools and their task naming patterns
Create separate baselines for servers (more automation) vs workstations
Alert immediately on tasks with download/execute patterns
Track tasks that are created and deleted quickly (anti-forensics)
Abnormal Process Spawning Velocity
What You're Hunting:
Rapid process creation that indicates malware unpacking, fork bombs, or automated exploitation
Key Data Sources:
Process creation events (Sysmon Event ID 1, EDR telemetry)
System performance logs showing process creation rates
Parent-child process relationship data
Linux: auditd process tracking, /proc monitoring
The Hunt:
Build velocity baselines for process creation:
- Normal installer behavior: 10-20 processes over 2-5 minutes
- Developer tools: May spawn 50+ processes but with specific patterns
- System startup: Known burst of ~100 processes at boot
- Baseline the "spawn rate" per process name per time window
Specific Things to Hunt:
High-Velocity Process Creation (Single Parent)
event_type="process_creation"
| stats count by parent_process_id, parent_process_name
span=1m as children_per_minute
| where children_per_minute > 30
AND parent_process_name NOT IN ("msiexec.exe", "setup.exe",
"devenv.exe", "java.exe", "chrome.exe", "firefox.exe")
Recursive Self-Spawning Detection
event_type="process_creation"
| where process_name = parent_process_name
| stats count by process_name, parent_process_id span=30s as recursive_spawns
| where recursive_spawns >= 5
| fields process_name, parent_process_id, recursive_spawns, commandline
Process Tree Explosion - Grandchildren Created Too Fast
event_type="process_creation" earliest=-5m
| eval process_chain = parent_process_name + " -> " + process_name
| stats dc(process_id) as unique_children,
dc(grandparent_id) as unique_grandchildren,
earliest(_time) as first_spawn,
latest(_time) as last_spawn,
values(process_chain) as chains
by grandparent_process_name, grandparent_id
| eval duration_seconds = (last_spawn - first_spawn)
| eval spawn_rate = unique_children / duration_seconds
| where (unique_children > 50 AND duration_seconds < 60)
OR spawn_rate > 2
Short-Lived Process Bursts
event_type="process_creation" OR event_type="process_termination"
| stats earliest(_time) as start_time,
latest(_time) as end_time,
dc(child_process_id) as children_spawned
by process_id, process_name, parent_process_name
| eval lifetime_seconds = (end_time - start_time)
| where lifetime_seconds < 1 AND children_spawned > 0
| stats count as burst_count,
avg(children_spawned) as avg_children,
values(parent_process_name) as parents
by process_name
| where burst_count > 10
Temp Directory Execution Chains
event_type="process_creation"
| where (process_path contains "\\\\\\\\Temp\\\\\\\\"
OR process_path contains "\\\\\\\\AppData\\\\\\\\Local\\\\\\\\Temp\\\\\\\\"
OR process_path contains "/tmp/"
OR process_path contains "/var/tmp/")
| join parent_process_id [
search event_type="process_creation" earliest=-1m
| rename process_id as parent_process_id
]
| where parent_process_path NOT contains "\\\\\\\\Temp\\\\\\\\"
| stats count as temp_executions,
values(process_name) as temp_processes,
values(parent_process_name) as droppers
by parent_process_id span=30s
| where temp_executions > 3
Command/Script Interpreter Chains
event_type="process_creation"
process_name IN ("cmd.exe", "powershell.exe", "bash", "sh", "wscript.exe", "cscript.exe")
| join parent_process_id [
search event_type="process_creation"
process_name IN ("cmd.exe", "powershell.exe", "bash", "sh", "wscript.exe", "cscript.exe")
| rename process_id as parent_process_id
]
| stats count as interpreter_chain_depth,
values(commandline) as commands
by root_parent_id span=1m
| where interpreter_chain_depth > 5
Unknown Binary High Spawn Rate
event_type="process_creation"
| lookup known_processes.csv process_hash OUTPUT known
| where isnull(known) OR known="false"
| stats count as children_spawned,
dc(process_name) as unique_children,
values(process_path) as child_paths
by parent_process_name, parent_process_hash span=2m
| where children_spawned > 20
| sort -children_spawned
Building Your Baseline:
# Collect spawn rates for all processes
event_type="process_creation" earliest=-7d
| bucket _time span=1m
| stats count by parent_process_name, _time
| stats avg(count) as avg_per_minute,
max(count) as max_per_minute,
stdev(count) as stdev_per_minute
by parent_process_name
| outputlookup process_spawn_baseline.csv
# Detection using baseline
event_type="process_creation"
| bucket _time span=1m
| stats count by parent_process_name, _time
| lookup process_spawn_baseline.csv parent_process_name
OUTPUT avg_per_minute, max_per_minute, stdev_per_minute
| eval threshold = avg_per_minute + (3 * stdev_per_minute)
| where count > threshold OR count > max_per_minute * 2
| fields parent_process_name, count, avg_per_minute, max_per_minute
Critical Detection Patterns:
# Immediate high-confidence alerts
- Any process spawning >100 children in 60 seconds (except allowlisted)
- Same binary name spawning itself >10 times in 30 seconds
- Process trees exceeding 10 levels deep
- Unknown binaries spawning >5 processes per second
- cmd.exe or powershell.exe chains >5 deep
Tuning Tips:
Create separate baselines for different host types (servers, workstations, build machines)
Allowlist CI/CD agents and build servers that naturally have high spawn rates
Combine with file creation events - high spawn rate + temp file drops = high confidence
Track time-of-day patterns (builds happen during business hours)
Service/Daemon Creation and Modification
What You're Hunting:
Persistence through service manipulation
Key Data Sources:
Windows: Event IDs 4697, 7045, 7040; Registry changes to Services keys
Linux: Systemd logs, changes to /etc/systemd/, cron modifications
Cloud: Lambda/Function creation, scheduled task creation
The Hunt:
Establish your organization's service baseline:
- WHO typically creates services? (installers, IT management tools, specific admins)
- WHEN are services created? (maintenance windows, software deployments)
- WHERE do service binaries live? (System32, Program Files, known vendor paths)
- WHAT are your standard service naming conventions? (vendor patterns, internal standards)
Specific Things to Hunt:
Windows Focus
New Service Indicators:
- Event ID 7045: New service installed
- Registry: HKLM\\\\System\\\\CurrentControlSet\\\\Services\\\\[new_key]
- Unusual service paths (not in System32 or Program Files)
- Services with suspicious names (short, random, mimicking legitimate)
Linux Focus
- New files in: /etc/systemd/system/, /etc/init.d/
- Modified crontabs: /var/spool/cron/, /etc/cron.*/
- New .service files with unusual ExecStart paths
- Systemctl commands from non-admin users
Cloud Focus
- New Lambda functions with admin roles
- CloudWatch Events rules with suspicious targets
- Azure Automation runbooks with broad permissions
- GCP Cloud Functions with unusual triggers
Quick Win Detection Logic:
(EventID = 7045 OR EventID = 4697) AND
(
ServicePath not starts with "C:\Windows\" OR
ServicePath contains "Users\" OR
ServicePath contains "%TEMP%" OR
ServiceName length < 4
)
Tuning Tips:
Document all legitimate software deployment tools and their service naming patterns (SCCM, Tanium, CrowdStrike, etc.)
Create separate baselines for different server roles (DC, Exchange, SQL have different service patterns)
Allowlist known managed service providers' remote management tools
Alert immediately on services with both suspicious path AND running as SYSTEM/LocalService
Track service creation alongside software installation events to reduce false positives
Monitor for services created and deleted within 24 hours (temporary persistence)
Open Source Package Manager Activity Baseline
What You're Hunting:
Unusual package installations, dependency confusion attacks, and supply chain compromises through package managers
Key Data Sources:
Proxy/firewall logs to package repositories
Process creation events (pip, npm, gem, cargo, maven, etc.)
CI/CD pipeline logs
Container build logs
The Hunt:
Map your package manager landscape and establish baselines:
- WHO uses which package managers? (developers vs CI/CD vs production)
- WHERE do packages come from? (pypi.org, npmjs.org, internal registries)
- WHEN are packages installed? (build times, deployment windows)
- WHAT packages are normal? (your top 100 most common)

Specific Hunting Patterns:
Dependency Confusion Indicators
- Package names similar to internal packages (typosquatting)
- Version pinning bypasses: "@latest" in production
- Packages from unexpected registries
- Direct installs from GitHub/GitLab instead of official registry
Suspicious Installation Commands
process_name in ("pip", "npm", "gem", "cargo", "maven") AND (
command_line contains "http://" OR # Non-HTTPS
command_line contains "--extra-index-url" OR # Additional registries
command_line contains "git+" OR # Direct git installs
command_line matches IP_address_pattern OR # IP instead of domain
command_line contains "--ignore-scripts" # Hiding post-install hooks
)
Supply Chain Attack Patterns
Package install followed immediately by:
- Outbound connection to rare domain
- Creation of scheduled task/cron job
- Modification of .bashrc/.profile
- Spawning of encoded PowerShell/bash
Quick Win Detection Logic:
# Never-before-seen package with similar name
if package_name not in 30_day_baseline AND
edit_distance(package_name, internal_packages) < 3:
ALERT: Possible dependency confusion
# Production system pulling unpinned versions
if environment == "production" AND
(command contains "@latest" OR not using lock_file):
ALERT: Insecure package management
Building Your Baseline:
# Build frequency analysis
baseline = {
'common_packages': count_by_frequency(last_30_days),
'allowed_registries': ['pypi.org', 'npmjs.org', 'internal-registry.com'],
'suspicious_flags': ['--trusted-host', '--ignore-scripts', '--force'],
'production_patterns': packages_in_lock_files_only
}
# Hunt logic
for package_event in today:
if package_event.name not in baseline['common_packages']:
if similar_to_internal_package(package_event.name):
flag_critical()
elif package_event.registry not in baseline['allowed_registries']:
flag_suspicious()
Tuning Tips:
Separate baselines for dev/staging/prod
Alert on any direct URL installs in production
Monitor package manager config file changes (.npmrc, pip.conf)
Track packages yanked from public registries (supply chain indicator)
Data Staging Detection
What You're Hunting:
Collection and preparation of data for exfiltration
Key Data Sources:
File system activity logs
Process creation with compression/archive tools
Cloud storage bucket access logs
The Hunt:
Build baselines for legitimate archive operations:
- WHO normally creates archives? (backup software, IT tools, developers)
- WHEN do archives get created? (backup windows, end of month reports)
- WHERE are archives normally stored? (backup drives, designated folders)
- WHAT types get compressed? (logs, databases, code repositories)
Specific Hunting Patterns:
Suspicious Archive Locations
Archived folders being written to or called from:
- Desktop, Downloads, Temp, Public folders
- User profile directories for service accounts
- Web server directories (inetpub, www, html)
- Root of C:\ or /tmp directories
Anti-Forensic Indicators
- Password-protected archives (-p, --password flags)
- Split/volume archives (-v flag, .001/.002 files)
- Archives deleted immediately after creation
- Encryption tools run before compression
Mass Collection Behaviors
- 100+ files accessed within 5 minutes
- Recursive copying to staging directories
- Database dumps outside maintenance windows
- Git/SVN repository cloning to unusual locations
- Robocopy or rsync with /E or -r flags
Staging Directory Patterns
Common staging directory names:
- temp, tmp, cache, staging
- collection, loot, data, export
- backup (when not in approved backup locations)
- Single letter directories (a, b, x, z)
Quick Win Detection Logic:
# Archive creation in suspicious locations
process_name IN ("rar.exe", "7z.exe", "zip.exe", "tar")
AND (path contains "\\Desktop\\" OR path contains "\\Temp\\" OR path contains "/tmp/")
# Password-protected archives
process_name IN ("rar", "7z", "zip")
AND commandline contains ("-p" OR "-password" OR "--encrypt" OR "-v")
# Mass data collection
file_access_count > 100 in 5_minute_window
AND process NOT IN (backup_software, antivirus, indexing_service)
# Database exports to unusual paths
(commandline contains "mysqldump" OR "pg_dump" OR "mongodump" OR "INTO OUTFILE")
AND destination NOT contains ("backup", "archive", "dump")
Building Your Baseline:
# Create allowlist
normal_archive_activity = {
'backup_windows': [(2, 4), (14, 16)], # 2-4 AM, 2-4 PM
'backup_accounts': ['svc_backup', 'admin_backup'],
'valid_paths': ['/backup/', 'D:\\Backups\\', '/mnt/backup/'],
'expected_tools': ['backup_software.exe', 'rsync', 'robocopy']
}
# Detection threshold
if (archive_creation NOT IN normal_archive_activity AND
archive_size > 100MB AND
location IN suspicious_paths):
alert("Potential data staging")
Tuning Tips:
Allowlist scheduled backup operations by time window and source
Create different thresholds for different data types (source code vs documents)
Alert on archives created shortly before outbound network connections
Monitor for staged data that gets deleted after network transfer
Track archive creation by users who don't normally create archives
Lateral Movement Patterns
What You're Hunting:
Adversary or normal movement between systems
Key Data Sources:
Windows authentication logs (4624, 4625, 4648)
Network connections between internal hosts
RDP/SSH/SMB/WinRM/RPC logs
The Hunt:
Build a map of normal administrative movement:
- WHICH accounts typically access multiple systems?
- WHAT is the normal pattern? (admin → servers, not user → user)
- WHAT protocols are used? (RDP for admins, SMB for file shares)
- WHEN does lateral movement occur? (maintenance windows, business hours)
Specific Hunting Patterns:
Workstation-to-Workstation Movement
- Type 3 (network) logons between user workstations
- RDP connections from non-IT workstations
- Multiple failed auth followed by success (password spraying)
- User accounts accessing 5+ workstations in an hour
Pass-the-Hash/Pass-the-Ticket Indicators
EventID=4624 AND LogonType=3 AND LogonProcess="NtLmSsp"
AND (
WorkstationName != "-" OR
SourceNetworkAddress != expected_jump_host OR
AccountName ends with "$" accessing workstations OR
LogonGuid = "00000000-0000-0000-0000-000000000000"
)
Suspicious Tool Usage
Process creation on remote systems:
- psexec.exe, psexesvc.exe
- wmic.exe with /node parameter
- winrm.exe, Enter-PSSession
- Impacket tools (wmiexec.py, smbexec.py)
- mstsc.exe from unusual sources
Unusual Authentication Chains
Patterns indicating compromise:
- User → Jump Host → Server A → Server B → Server C
- Service account authenticating interactively
- Admin account accessing systems outside their normal scope
- East-West movement at unusual hours (2-5 AM)
Quick Win Detection Logic:
# Workstation-to-workstation lateral movement
EventID=4624 LogonType=3
| stats count by SourceAddress, TargetHost, AccountName span=1h
| where SourceAddress=workstation AND TargetHost=workstation
| where AccountName NOT IN (IT_admin_accounts)
# Multiple systems accessed rapidly
EventID=4624
| stats dc(ComputerName) as unique_systems by AccountName span=1h
| where unique_systems > 5
| where AccountName NOT IN (scanner_accounts, admin_accounts)
# Remote execution tools
process_name IN ("psexec.exe", "wmic.exe", "winrs.exe")
| where parent_process != "approved_automation.exe"
| table _time, user, process_name, commandline, target_host
Building Your Baseline:
# Map normal lateral movement patterns
EventID=4624 earliest=-7d
| stats dc(ComputerName) as systems_accessed,
values(ComputerName) as target_systems,
values(LogonType) as logon_types
by AccountName, SourceNetworkAddress
| where systems_accessed > 3
| outputlookup lateral_movement_baseline.csv
# Document normal patterns
normal_lateral_movement = {
'admin_jump_hosts': ['admin01.domain', 'admin02.domain'],
'service_accounts': ['svc_backup', 'svc_monitoring'],
'expected_paths': 'workstation → server, server → server',
'unexpected_paths': 'workstation → workstation',
'maintenance_windows': '2-4 AM, 2-4 PM'
}
# Detection using baseline
EventID=4624 LogonType=3
| lookup lateral_movement_baseline.csv AccountName SourceNetworkAddress
OUTPUT target_systems as normal_targets
| where ComputerName NOT IN (normal_targets)
| eval risk_score = case(
SourceType="workstation" AND DestType="workstation", "HIGH",
AccountName contains "$", "MEDIUM",
1=1, "LOW"
)
Tuning Tips:
Document legitimate remote administration tools and their usage patterns
Create separate baselines for IT staff vs regular users
Alert on any lateral movement from high-value targets (Domain Controllers, databases)
Monitor for lateral movement followed immediately by data collection
Track service accounts that suddenly start moving laterally
Registry Persistence Locations
What You're Hunting:
Malware persistence through registry modifications
Key Data Sources:
Registry modification events (Sysmon Event ID 12, 13, 14)
EDR registry telemetry
Registry audit logs
The Hunt:
Build a baseline of legitimate registry persistence in your environment:
- WHO normally modifies registry Run keys? (installers, IT tools, specific software)
- WHEN do registry modifications occur? (software deployment, login, maintenance)
- WHERE are legitimate autostart programs located? (Program Files, System32)
- WHAT are your standard registry persistence entries? (AV, EDR, legitimate tools)
Specific Hunting Patterns:
Run/RunOnce Key Modifications
Common malicious patterns:
- Entries pointing to %TEMP%, %APPDATA%, %PUBLIC%
- Entries with random 6-10 character names
- PowerShell or cmd.exe with encoded/obfuscated arguments
- Entries pointing to files created in last 24 hours
- DLL entries without corresponding EXE
Winlogon Hijacking
Common used registry key locations:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify
Suspicious values:
- Shell != "explorer.exe"
- Userinit with comma-separated additional entries
- Any Notify subkey additions
Service and Driver Persistence
EventID=13
TargetObject contains "\\Services\\" AND (
Details contains "\\Users\\" OR
Details contains "%TEMP%" OR
Details matches "^[a-z]{6,10}$" OR # Random service names
EventType="CreateKey" with non-signed binary
)
Lesser-Known Persistence Locations
Monitor for modifications to:
- AppInit_DLLs (deprecated but still used)
- Image File Execution Options\*\Debugger
- SilentProcessExit\*\MonitorProcess
- Terminal Server\Wds\rdpwd\StartupPrograms
- Environment\UserInitMprLogonScript
- Explorer\Browser Helper Objects
- Print Monitors
Quick Win Detection Logic:
# Run key with suspicious paths
EventID=13 TargetObject="*\\CurrentVersion\\Run*"
| where Details contains ("\\Temp\\" OR "\\AppData\\" OR "\\Public\\" OR "powershell" OR "cmd.exe /c")
| where Image NOT IN ("msiexec.exe", "trusted_installer.exe")
# Winlogon tampering detection
EventID=13
| regex TargetObject=".*\\Winlogon\\(Shell|Userinit|Notify)"
| where Details != "explorer.exe"
AND Details != "C:\\Windows\\system32\\userinit.exe,"
| eval severity="CRITICAL"
# New service DLL or ImagePath modification
EventID=13
| regex TargetObject=".*\\Services\\.*\\(ServiceDll|ImagePath)"
| stats earliest(_time) as first_seen by Details, TargetObject
| where first_seen > relative_time(now(), "-24h")
Building Your Baseline:
# Document all existing autostart entries
EventID=13 TargetObject="*\\Run*" OR TargetObject="*\\RunOnce*" earliest=-7d
| stats values(Details) as known_entries,
dc(Computer) as prevalence
by TargetObject
| where prevalence > 1
| outputlookup registry_persistence_baseline.csv
# Create allowlist of legitimate entries
legitimate_persistence = {
'run_keys': [
'SecurityHealth', 'OneDriveSetup', 'TeamsMachineInstaller',
'WindowsDefender', 'CcmExec', 'Chrome', 'Edge'
],
'services': [
'Windows*', 'Defender*', 'CrowdStrike*', 'Carbon*'
],
'paths': [
'C:\\Program Files\\*', 'C:\\Program Files (x86)\\*',
'C:\\Windows\\System32\\*', 'C:\\Windows\\SysWOW64\\*'
]
}
# Detection using baseline
EventID=13 TargetObject="*\\Run*"
| lookup registry_persistence_baseline.csv TargetObject OUTPUT known_entries
| where NOT Details IN (known_entries)
| rex field=Details "(?<file_path>.*\.exe)"
| eval created_recently = if(file_creation_time > relative_time(now(), "-24h"), 1, 0)
| where created_recently=1 OR file_path contains "\\Temp\\"
Tuning Tips:
Whitelist software deployment tools that legitimately modify Run keys
Alert immediately on any Winlogon, Notify, or Debugger modifications
Correlate registry persistence with process execution from same paths
Monitor for registry entries that are created and deleted quickly (execution then cleanup)
Track prevalence - unique persistence across multiple machines is suspicious
Create separate baselines for servers vs workstations
Privilege Escalation Indicators
What You're Hunting:
Attempts to gain higher privileges
Key Data Sources:
Security logs (4672, 4673, 4674 - privilege events)
Process creation with integrity level changes
Sudo logs (Linux)
Cloud IAM changes
The Hunt:
Track privilege changes and unusual elevation patterns:
- WHO normally uses elevated privileges? (admins, service accounts, automation)
- WHEN does privilege escalation occur? (patching, deployment, maintenance)
- WHERE do privileged processes run from? (System32, admin tools directories)
- WHAT are normal privilege elevation patterns? (help desk → local admin, not user → SYSTEM)
Specific Hunting Patterns:
Windows Privilege Escalation
- Token manipulation (SeDebugPrivilege, SeImpersonatePrivilege abuse)
- UAC bypasses (eventvwr, fodhelper, computerdefaults)
- SYSTEM process creation from user context
- DLL hijacking in privileged processes
- Process integrity level > parent integrity level
Linux Privilege Escalation
- Sudo usage by non-sudoers group members
- SUID/SGID binaries executing from /tmp or /home
- Capability abuse (CAP_SYS_ADMIN, CAP_DAC_OVERRIDE)
- Kernel exploit indicators (dirty cow patterns)
- Modified sudoers file or sudo configuration
Cloud Privilege Escalation
- AssumeRole to admin or poweruser roles
- Policy attachments with "*:*" permissions
- Service account key creation by non-admins
- Cross-account role assumptions
- AddUserToGroup for admin groups
Quick Win Detection Logic:
# Windows: Process integrity level changes
process_creation_event
| where integrity_level > parent_integrity_level
| where parent_process NOT IN ("services.exe", "lsass.exe", "winlogon.exe")
# Linux: Suspicious sudo usage
event_type="sudo_log"
| where user NOT IN (sudoers_group)
OR (command contains "/tmp/" OR command contains "chmod +s")
# Cloud: Privilege escalation via role assumption
event_source="cloudtrail" eventName="AssumeRole"
| where requestParameters.roleArn contains "admin" OR contains "poweruser"
| stats count by userIdentity.arn, requestParameters.roleArn span=1h
| where count > 5
Building Your Baseline:
# Map normal privilege patterns
Track for 7 days:
- Which accounts normally use elevated privileges
- What processes typically run as SYSTEM
- Standard sudo commands per user
- Normal IAM role assumptions
privilege_baseline = {
'elevated_users': ['admin_*', 'svc_deploy'],
'system_processes': ['services.exe', 'lsass.exe'],
'sudo_patterns': {'devops': ['systemctl', 'docker'], 'dba': ['mysql']},
'role_assumptions': {'dev_role': ['prod_readonly'], 'admin_role': ['audit_role']}
}
Tuning Tips:
Allowlist known admin tools that require elevation
Alert on any SUID binary creation in user-writable directories
Monitor for multiple failed privilege escalation attempts
Track privilege changes followed by data access or lateral movement
Network Share Enumeration
What You're Hunting:
Discovery activity mapping your network shares
Key Data Sources:
SMB/NetBIOS logs
Windows Event 5140 (network share access)
File server audit logs
The Hunt:
Build a baseline of normal share access patterns:
- WHO typically accesses network shares? (users, backup services, scanners)
- WHEN are shares accessed? (business hours, backup windows)
- WHERE are shares accessed from? (workstations, servers, admin jump boxes)
- WHAT shares are commonly accessed? (home drives, department shares, public)
Specific Hunting Patterns:
Rapid Share Enumeration
- 10+ different shares accessed in 5 minutes
- Sequential scanning of share names
- Access attempts to non-existent shares
- Pattern matching (share1, share2, share3...)
Administrative Share Access
- C$, ADMIN$, IPC$ from user workstations
- Non-admin accounts accessing admin shares
- Service accounts accessing admin shares
- After-hours access to sensitive shares
Enumeration Tool Usage
Commands and tools to detect:
- net view \\server /all
- net share
- Get-SmbShare (PowerShell)
- enum4linux, smbclient, nbtscan
- SharpShares, SoftPerfect scanner
Failed Access Patterns
- Multiple STATUS_ACCESS_DENIED in short timeframe
- Failed attempts across alphabetically ordered shares
- Access attempts to honeypot/decoy shares
- Same user failing across multiple servers
Quick Win Detection Logic:
# Rapid share enumeration
EventID=5140
| bucket _time span=5m
| stats dc(ShareName) as unique_shares,
values(ShareName) as shares_accessed
by IpAddress, AccountName, _time
| where unique_shares > 10
OR shares_accessed contains "$"
# Admin share access from non-admins
EventID=5140 ShareName IN ("C$", "ADMIN$", "IPC$")
| where AccountName NOT IN (admin_accounts)
AND IpAddress NOT IN (admin_workstations)
# Net commands for enumeration
process_creation commandline="net view*" OR commandline="net share"
| where parent_process != "legitimate_scanner.exe"
Building Your Baseline:
# Map normal share access patterns
EventID=5140 earliest=-7d
| stats dc(ShareName) as shares_per_user,
values(ShareName) as normal_shares
by AccountName
| outputlookup share_access_baseline.csv
# Detection threshold
EventID=5140
| lookup share_access_baseline.csv AccountName OUTPUT normal_shares
| where ShareName NOT IN (normal_shares)
| stats count by AccountName, ShareName, IpAddress
| where count > threshold
Tuning Tips:
Whitelist vulnerability scanners and asset inventory tools
Create different baselines for IT staff vs regular users
Alert on any access to honeypot shares
Monitor enumeration followed by large file transfers
Track share access outside business hours
Cloud Resource Anomalies
What You're Hunting:
Unusual cloud resource usage or creation
Key Data Sources:
CloudTrail (AWS), Activity Logs (Azure), Audit Logs (GCP)
Cloud billing anomalies
Resource inventory changes
The Hunt:
Establish baselines for normal cloud resource usage:
- WHO creates cloud resources? (DevOps, automation tools, specific admins)
- WHEN are resources created? (deployment windows, business hours)
- WHERE are resources deployed? (approved regions, production accounts)
- WHAT resource types are standard? (EC2, RDS, Lambda, storage buckets)
Specific Hunting Patterns:
Geographic Anomalies
- Instances in regions you never operate in
- Resources in expensive regions (data transfer costs)
- Cross-region snapshot copies
- Resources in non-compliant jurisdictions
Security Control Weakening
eventName IN ("AuthorizeSecurityGroupIngress", "ModifyDBInstance", "PutBucketPolicy")
AND (
requestParameters contains "0.0.0.0/0" OR
requestParameters contains "PubliclyAccessible:true" OR
requestParameters contains "BlockPublicAccess:false" OR
requestParameters contains "Encryption:false"
)
Data Exfiltration Indicators
Suspicious backup/snapshot activity:
- CreateSnapshot outside maintenance windows
- CopySnapshot to external accounts
- ExportImage to personal regions
- Large S3 GetObject operations (>1GB in 5 minutes)
Quick Win Detection Logic:
# Resources in unauthorized regions
eventName="RunInstances"
| where awsRegion NOT IN ("us-east-1", "us-west-2")
# Logging disruption
eventName IN ("DeleteTrail", "StopLogging", "DeleteFlowLogs")
| alert priority=CRITICAL
# Mass snapshot creation
eventName="CreateSnapshot"
| bucket _time span=1h
| stats count by userIdentity.arn
| where count > 10
Building Your Baseline:
# Document normal cloud operations
event_source="cloudtrail" earliest=-7d
| stats dc(awsRegion) as regions_used,
values(awsRegion) as active_regions,
count by eventName, userIdentity.arn
| where count > 10
| outputlookup cloud_baseline.csv
# Define normal patterns
cloud_baseline = {
'approved_regions': ['us-east-1', 'us-west-2'],
'backup_schedule': 'daily_0200_UTC',
'service_accounts': ['backup-svc', 'deployment-automation'],
'snapshot_frequency': '1-5 per day per database',
'normal_api_volume': '1000-5000 calls per hour'
}
# Detection using baseline
event_source="cloudtrail"
| lookup cloud_baseline.csv eventName userIdentity.arn OUTPUT normal_regions
| where awsRegion NOT IN (normal_regions)
| eval risk_score = case(
eventName="RunInstances" AND awsRegion NOT IN ('us-east-1','us-west-2'), "CRITICAL",
eventName contains "Delete" AND eventName contains "Log", "HIGH",
eventName="CreateSnapshot" AND hour NOT IN (2,3,4), "MEDIUM",
1=1, "LOW"
)
Tuning Tips:
Maintain strict approved regions list
Whitelist backup service accounts
Alert immediately on CloudTrail/logging changes
Monitor for resources created then quickly deleted
Track unusual data transfer volumes
Key Takeaways
Baselines are living documents - Update them on a regular basis, monthly, quarterly, or annually may be appropriate depending on what you are looking at
Context is king - What's normal for a dev environment isn't normal for accounting, your SOC will thank you for finding this
Start with the basics - These ten hunts catch more than exotic threats, look for the basic understanding to make surfacing bad easier
Automate gradually - Manual hunts teach you what to automate, be ruthless but incremental
Share your findings - Build institutional knowledge, document, share, repeat
Remember: The best threat hunters know their environment so well that anomalies practically jump off the screen. These baseline hunts build that intuition systematically.
Keep an eye out for part 3, which will walk you through implementing baseline hunts in 4 weeks, from zero to production-ready hunts.
Happy hunting!
What baseline hunts have been most valuable in your environment? What would you add to this list? Drop a comment below or reach out on THOR Collective socials!