splunk-rule-conversion
SKILL.md
Splunk Rule Conversion
Overview
Convert Splunk SPL detection rules to Microsoft Sentinel KQL, Google SecOps YARA-L 2.0, and CrowdStrike CQL. Includes field mappings, syntax patterns, and detection quality improvements.
Quick Reference
| Source | Target | Key Differences |
|---|---|---|
| Splunk SPL | Microsoft KQL | Pipe vs chained operators, different aggregation syntax |
| Splunk SPL | Google YARA-L | Declarative rules vs procedural queries, UDM schema |
| Splunk SPL | CrowdStrike CQL | Similar pipe syntax, different field names |
SPL to Microsoft Sentinel KQL
Syntax Mapping
| SPL | KQL |
|---|---|
index=windows |
SecurityEvent table |
sourcetype=WinEventLog:Security |
SecurityEvent |
search field=value |
where field == "value" |
stats count by user |
summarize count() by user |
eval field=value |
extend field = value |
table field1, field2 |
project field1, field2 |
rename old AS new |
project-rename new = old |
dedup field |
summarize take_any(*) by field |
sort -count |
order by count desc |
rex field=x "(?<name>pattern)" |
parse x with * "pattern" name or extract() |
mvexpand field |
mv-expand field |
earliest=-24h |
TimeGenerated > ago(24h) |
| where count > 5 |
| where count_ > 5 |
transaction |
summarize makeset() + correlation logic |
Data Table Mapping
| Splunk Source | Sentinel Table |
|---|---|
| Windows Security Events | SecurityEvent |
| Syslog | Syslog |
| Network/Firewall | CommonSecurityLog |
| DNS | DnsEvents |
| Azure AD Sign-ins | SigninLogs |
| Azure AD Audit | AuditLogs |
| M365 Activity | OfficeActivity |
| Defender for Endpoint | DeviceEvents, DeviceProcessEvents |
| Azure Activity | AzureActivity |
Windows EventID Reference
| EventID | Description | KQL Filter |
|---|---|---|
| 4624 | Successful logon | EventID == 4624 |
| 4625 | Failed logon | EventID == 4625 |
| 4648 | Explicit credential logon | EventID == 4648 |
| 4672 | Special privileges assigned | EventID == 4672 |
| 4688 | Process creation | EventID == 4688 |
| 4698 | Scheduled task created | EventID == 4698 |
| 4720 | User account created | EventID == 4720 |
| 4732 | Member added to local group | EventID == 4732 |
KQL Detection Quality Guidelines
When converting, enhance detections with:
-
Avoid Low-and-Slow Blindness
// Create complementary rules with different thresholds // High threshold for immediate alert // Low threshold for hunting/investigation -
Address Distributed Attacks
// Aggregate by user across ALL devices, not per-device SecurityEvent | where EventID == 4625 | summarize FailedLogons = count() by TargetUserName, bin(TimeGenerated, 1h) | where FailedLogons > 10 -
Correlate Failures with Success
// Failed attempts followed by success = potential compromise let failures = SecurityEvent | where EventID == 4625; let successes = SecurityEvent | where EventID == 4624; failures | join kind=inner (successes) on TargetUserName | where TimeGenerated1 between (TimeGenerated .. TimeGenerated + 1h) -
Cover Cloud Identity
// Include non-interactive sign-ins (often missed) union SigninLogs, AADNonInteractiveUserSignInLogs | where ResultType != 0 -
Device Management Context
// CRITICAL: Use LEFT OUTER JOIN for device tables (not all devices managed) SecurityEvent | join kind=leftouter (DeviceInfo) on DeviceId -
Geographic Anomaly Detection
SigninLogs | extend Country = tostring(LocationDetails.countryOrRegion) | summarize Countries = make_set(Country) by UserPrincipalName, bin(TimeGenerated, 1h) | where array_length(Countries) > 1
Example Conversion: Brute Force Detection
Original SPL:
index=windows sourcetype=WinEventLog:Security EventCode=4625
| stats count as failed_attempts by Account_Name, src_ip
| where failed_attempts > 5
Converted KQL:
let timeframe = 1h;
let threshold = 5;
SecurityEvent
| where TimeGenerated > ago(timeframe)
| where EventID == 4625
| extend AccountName = TargetUserName
| extend SourceIP = IpAddress
| summarize FailedAttempts = count() by AccountName, SourceIP, bin(TimeGenerated, 5m)
| where FailedAttempts > threshold
| project TimeGenerated, AccountName, SourceIP, FailedAttempts
SPL to Google SecOps YARA-L 2.0
YARA-L 2.0 Rule Structure
rule rule_name {
meta:
author = "Security Team"
description = "Rule description"
severity = "HIGH" // INFO, LOW, MEDIUM, HIGH, CRITICAL
mitre_attack_tactic = "Tactic Name"
mitre_attack_technique = "TXXXX"
events:
// Event matching conditions
$event.metadata.event_type = "PROCESS_LAUNCH"
$event.principal.process.file.full_path = /pattern/i
match:
// Grouping for aggregation (optional)
$event.principal.user.userid over 5m
outcome:
// Calculated fields (optional)
$count = count($event)
condition:
// Rule trigger conditions
$event and $count > 5
}
CRITICAL YARA-L Requirements
-
Risk Score MUST be static integer
// CORRECT risk_score = 85 // WRONG - will fail risk_score = $severity * 10 -
No if() in aggregation functions
// WRONG $count = count_distinct(if($e.severity > 5, $e.user)) // CORRECT - use separate rules or filter in events section -
Match section indentation: exactly 2 spaces
match: $user over 5m // 2 spaces, not tab -
Placeholder variables use $ prefix
$e.metadata.event_type // event variable $user over 5m // match variable
UDM Field Mapping
| Splunk Field | UDM Path |
|---|---|
| user / Account | principal.user.userid |
| src_ip | principal.ip |
| dest_ip | target.ip |
| src_port | principal.port |
| dest_port | target.port |
| process | principal.process.file.full_path |
| command_line | principal.process.command_line |
| parent_process | principal.process.parent_process.file.full_path |
| host / hostname | principal.hostname |
| EventCode | metadata.product_event_type |
| action | security_result.action |
| severity | security_result.severity |
| file_hash | target.file.sha256 |
| url | target.url |
| domain | network.dns.questions.name |
UDM Event Types
| Type | Description |
|---|---|
PROCESS_LAUNCH |
Process execution |
PROCESS_TERMINATE |
Process termination |
NETWORK_CONNECTION |
Network connection |
NETWORK_DNS |
DNS query |
FILE_CREATION |
File created |
FILE_MODIFICATION |
File modified |
FILE_DELETION |
File deleted |
USER_LOGIN |
Authentication |
USER_LOGOUT |
Session end |
REGISTRY_CREATION |
Registry key created |
REGISTRY_MODIFICATION |
Registry value changed |
Example Conversion: Suspicious PowerShell
Original SPL:
index=windows sourcetype=WinEventLog:Security EventCode=4688
| search New_Process_Name="*powershell.exe"
| search (Process_Command_Line="*-enc*" OR Process_Command_Line="*-encodedcommand*")
| stats count by Account_Name, Workstation_Name
Converted YARA-L:
rule suspicious_encoded_powershell {
meta:
author = "Security Team"
description = "Detects encoded PowerShell execution"
severity = "HIGH"
mitre_attack_tactic = "Execution"
mitre_attack_technique = "T1059.001"
events:
$process.metadata.event_type = "PROCESS_LAUNCH"
$process.metadata.product_event_type = "4688"
$process.principal.process.file.full_path = /powershell\.exe$/i
$process.principal.process.command_line = /\-[eE]nc|\-[eE]ncodedCommand/
$user = $process.principal.user.userid
$host = $process.principal.hostname
match:
$user, $host over 5m
outcome:
$execution_count = count($process)
$risk_score = 75
condition:
$process
}
SPL to CrowdStrike CQL
CQL Syntax Mapping
| SPL | CQL |
|---|---|
index=main |
Select event source/repository |
sourcetype= |
event_simpleName= |
search field=value |
field=value |
stats count by field |
| groupBy([field]) |
eval newfield=expr |
| eval(newfield=expr) |
table fields |
| select([fields]) |
sort -field |
| sort(field, order=desc) |
dedup field |
| groupBy([field]) | head(1) |
rex |
| regex() |
where count > 5 |
| test(count > 5) |
CrowdStrike Event Types
| event_simpleName | Description |
|---|---|
ProcessRollup2 |
Process execution with full context |
SyntheticProcessRollup2 |
Synthetic process events |
NetworkConnectIP4 |
IPv4 network connection |
NetworkConnectIP6 |
IPv6 network connection |
DnsRequest |
DNS query |
FileWritten |
File creation/modification |
UserLogon |
Authentication event |
AsepValueUpdate |
Persistence mechanism |
Platform Filtering
// Windows only
event_platform=Win
// macOS only
event_platform=Mac
// Linux only
event_platform=Lin
CQL Field Reference
| Splunk Equivalent | CQL Field |
|---|---|
| hostname | ComputerName |
| user | UserName |
| process | FileName |
| command_line | CommandLine |
| parent_process | ParentBaseFileName |
| src_ip | LocalAddressIP4 |
| dest_ip | RemoteAddressIP4 |
| dest_port | RemotePort |
| file_hash | SHA256HashData |
| pid | TargetProcessId |
| parent_pid | ParentProcessId |
Example Conversion: Network Beaconing
Original SPL:
index=firewall sourcetype=pan:traffic
| stats count by src_ip, dest_ip, dest_port
| where count > 100
| sort -count
Converted CQL:
event_simpleName=NetworkConnectIP4
| groupBy([LocalAddressIP4, RemoteAddressIP4, RemotePort])
| count(aid, as=connection_count)
| test(connection_count > 100)
| sort(connection_count, order=desc)
| select([LocalAddressIP4, RemoteAddressIP4, RemotePort, connection_count])
Conversion Workflow
Step 1: Analyze Source Rule
- Identify data sources (index, sourcetype)
- Map fields to target schema
- Understand aggregation logic
- Note time windows and thresholds
Step 2: Translate Core Logic
- Convert search conditions
- Map aggregation functions
- Adapt field names
- Adjust time syntax
Step 3: Enhance Detection Quality
- Add complementary low-threshold variants
- Include distributed attack patterns
- Add success correlation for auth failures
- Cover cloud identity sources
- Add geographic anomaly detection
Step 4: Validate and Test
- Syntax validation
- Test with sample data
- Tune thresholds
- Document false positive patterns
MITRE ATT&CK Mapping Reference
Include MITRE mapping in converted rules:
| Tactic | Common Techniques |
|---|---|
| Initial Access | T1566 (Phishing), T1190 (Exploit Public-Facing) |
| Execution | T1059 (Command/Scripting), T1204 (User Execution) |
| Persistence | T1547 (Boot/Logon Autostart), T1053 (Scheduled Task) |
| Privilege Escalation | T1548 (Abuse Elevation), T1134 (Access Token) |
| Defense Evasion | T1070 (Indicator Removal), T1036 (Masquerading) |
| Credential Access | T1003 (OS Credential Dumping), T1110 (Brute Force) |
| Discovery | T1082 (System Info), T1083 (File/Directory Discovery) |
| Lateral Movement | T1021 (Remote Services), T1570 (Lateral Tool Transfer) |
| Collection | T1005 (Data from Local System), T1114 (Email Collection) |
| Exfiltration | T1041 (Exfil Over C2), T1048 (Exfil Over Alt Protocol) |
| Impact | T1486 (Data Encrypted), T1489 (Service Stop) |
Common Conversion Patterns
Authentication Monitoring
SPL:
index=windows EventCode=4625 | stats count by Account_Name | where count > 10
KQL:
SecurityEvent
| where EventID == 4625
| summarize FailedCount = count() by TargetUserName
| where FailedCount > 10
YARA-L:
rule brute_force_detection {
meta:
severity = "MEDIUM"
events:
$fail.metadata.event_type = "USER_LOGIN"
$fail.security_result.action = "BLOCK"
$user = $fail.target.user.userid
match:
$user over 10m
outcome:
$fail_count = count($fail)
condition:
$fail and $fail_count > 10
}
CQL:
event_simpleName=UserLogon LogonType=10
| groupBy([UserName])
| count(aid, as=logon_attempts)
| test(logon_attempts > 10)
Confidence Scoring
When converting rules, assess confidence:
| Score | Meaning |
|---|---|
| 0.90-1.00 | Perfect conversion, all fields mapped |
| 0.75-0.89 | Good conversion, minor assumptions made |
| 0.60-0.74 | Moderate conversion, some logic simplified |
| 0.40-0.59 | Partial conversion, manual review needed |
| <0.40 | Significant gaps, major rework required |
Factors affecting confidence:
- Field availability in target platform
- Aggregation complexity
- Multi-event correlation
- Custom field extractions
- Platform-specific features