AgentSkillsCN

splunk-rule-conversion

将 Splunk SPL 检测规则转换为其他 SIEM 平台:Microsoft Sentinel(KQL)、Google SecOps/Chronicle(YARA-L 2.0),以及 CrowdStrike LogScale(CQL)。适用于在不同平台间迁移检测规则、对传统 Splunk 关联规则进行现代化改造,或构建多平台检测覆盖范围时使用。包含字段映射表、语法转换模式,以及检测质量优化方案。

SKILL.md
--- frontmatter
name: splunk-rule-conversion
description: "Convert Splunk SPL detection rules to other SIEM platforms: Microsoft Sentinel (KQL), Google SecOps/Chronicle (YARA-L 2.0), and CrowdStrike LogScale (CQL). Use when migrating detection rules between platforms, modernizing legacy Splunk correlations, or building multi-platform detection coverage. Includes field mapping tables, syntax conversion patterns, and detection quality enhancements."

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

SourceTargetKey Differences
Splunk SPLMicrosoft KQLPipe vs chained operators, different aggregation syntax
Splunk SPLGoogle YARA-LDeclarative rules vs procedural queries, UDM schema
Splunk SPLCrowdStrike CQLSimilar pipe syntax, different field names

SPL to Microsoft Sentinel KQL

Syntax Mapping

SPLKQL
index=windowsSecurityEvent table
sourcetype=WinEventLog:SecuritySecurityEvent
search field=valuewhere field == "value"
stats count by usersummarize count() by user
eval field=valueextend field = value
table field1, field2project field1, field2
rename old AS newproject-rename new = old
dedup fieldsummarize take_any(*) by field
sort -countorder by count desc
rex field=x "(?<name>pattern)"parse x with * "pattern" name or extract()
mvexpand fieldmv-expand field
earliest=-24hTimeGenerated > ago(24h)
| where count > 5| where count_ > 5
transactionsummarize makeset() + correlation logic

Data Table Mapping

Splunk SourceSentinel Table
Windows Security EventsSecurityEvent
SyslogSyslog
Network/FirewallCommonSecurityLog
DNSDnsEvents
Azure AD Sign-insSigninLogs
Azure AD AuditAuditLogs
M365 ActivityOfficeActivity
Defender for EndpointDeviceEvents, DeviceProcessEvents
Azure ActivityAzureActivity

Windows EventID Reference

EventIDDescriptionKQL Filter
4624Successful logonEventID == 4624
4625Failed logonEventID == 4625
4648Explicit credential logonEventID == 4648
4672Special privileges assignedEventID == 4672
4688Process creationEventID == 4688
4698Scheduled task createdEventID == 4698
4720User account createdEventID == 4720
4732Member added to local groupEventID == 4732

KQL Detection Quality Guidelines

When converting, enhance detections with:

  1. Avoid Low-and-Slow Blindness

    kql
    // Create complementary rules with different thresholds
    // High threshold for immediate alert
    // Low threshold for hunting/investigation
    
  2. Address Distributed Attacks

    kql
    // Aggregate by user across ALL devices, not per-device
    SecurityEvent
    | where EventID == 4625
    | summarize FailedLogons = count() by TargetUserName, bin(TimeGenerated, 1h)
    | where FailedLogons > 10
    
  3. Correlate Failures with Success

    kql
    // 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)
    
  4. Cover Cloud Identity

    kql
    // Include non-interactive sign-ins (often missed)
    union SigninLogs, AADNonInteractiveUserSignInLogs
    | where ResultType != 0
    
  5. Device Management Context

    kql
    // CRITICAL: Use LEFT OUTER JOIN for device tables (not all devices managed)
    SecurityEvent
    | join kind=leftouter (DeviceInfo) on DeviceId
    
  6. Geographic Anomaly Detection

    kql
    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:

spl
index=windows sourcetype=WinEventLog:Security EventCode=4625
| stats count as failed_attempts by Account_Name, src_ip
| where failed_attempts > 5

Converted KQL:

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

yaral
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

  1. Risk Score MUST be static integer

    yaral
    // CORRECT
    risk_score = 85
    
    // WRONG - will fail
    risk_score = $severity * 10
    
  2. No if() in aggregation functions

    yaral
    // WRONG
    $count = count_distinct(if($e.severity > 5, $e.user))
    
    // CORRECT - use separate rules or filter in events section
    
  3. Match section indentation: exactly 2 spaces

    yaral
    match:
      $user over 5m  // 2 spaces, not tab
    
  4. Placeholder variables use $ prefix

    yaral
    $e.metadata.event_type  // event variable
    $user over 5m           // match variable
    

UDM Field Mapping

Splunk FieldUDM Path
user / Accountprincipal.user.userid
src_ipprincipal.ip
dest_iptarget.ip
src_portprincipal.port
dest_porttarget.port
processprincipal.process.file.full_path
command_lineprincipal.process.command_line
parent_processprincipal.process.parent_process.file.full_path
host / hostnameprincipal.hostname
EventCodemetadata.product_event_type
actionsecurity_result.action
severitysecurity_result.severity
file_hashtarget.file.sha256
urltarget.url
domainnetwork.dns.questions.name

UDM Event Types

TypeDescription
PROCESS_LAUNCHProcess execution
PROCESS_TERMINATEProcess termination
NETWORK_CONNECTIONNetwork connection
NETWORK_DNSDNS query
FILE_CREATIONFile created
FILE_MODIFICATIONFile modified
FILE_DELETIONFile deleted
USER_LOGINAuthentication
USER_LOGOUTSession end
REGISTRY_CREATIONRegistry key created
REGISTRY_MODIFICATIONRegistry value changed

Example Conversion: Suspicious PowerShell

Original SPL:

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:

yaral
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

SPLCQL
index=mainSelect event source/repository
sourcetype=event_simpleName=
search field=valuefield=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_simpleNameDescription
ProcessRollup2Process execution with full context
SyntheticProcessRollup2Synthetic process events
NetworkConnectIP4IPv4 network connection
NetworkConnectIP6IPv6 network connection
DnsRequestDNS query
FileWrittenFile creation/modification
UserLogonAuthentication event
AsepValueUpdatePersistence mechanism

Platform Filtering

cql
// Windows only
event_platform=Win

// macOS only
event_platform=Mac

// Linux only
event_platform=Lin

CQL Field Reference

Splunk EquivalentCQL Field
hostnameComputerName
userUserName
processFileName
command_lineCommandLine
parent_processParentBaseFileName
src_ipLocalAddressIP4
dest_ipRemoteAddressIP4
dest_portRemotePort
file_hashSHA256HashData
pidTargetProcessId
parent_pidParentProcessId

Example Conversion: Network Beaconing

Original SPL:

spl
index=firewall sourcetype=pan:traffic
| stats count by src_ip, dest_ip, dest_port
| where count > 100
| sort -count

Converted CQL:

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

  1. Identify data sources (index, sourcetype)
  2. Map fields to target schema
  3. Understand aggregation logic
  4. Note time windows and thresholds

Step 2: Translate Core Logic

  1. Convert search conditions
  2. Map aggregation functions
  3. Adapt field names
  4. Adjust time syntax

Step 3: Enhance Detection Quality

  1. Add complementary low-threshold variants
  2. Include distributed attack patterns
  3. Add success correlation for auth failures
  4. Cover cloud identity sources
  5. Add geographic anomaly detection

Step 4: Validate and Test

  1. Syntax validation
  2. Test with sample data
  3. Tune thresholds
  4. Document false positive patterns

MITRE ATT&CK Mapping Reference

Include MITRE mapping in converted rules:

TacticCommon Techniques
Initial AccessT1566 (Phishing), T1190 (Exploit Public-Facing)
ExecutionT1059 (Command/Scripting), T1204 (User Execution)
PersistenceT1547 (Boot/Logon Autostart), T1053 (Scheduled Task)
Privilege EscalationT1548 (Abuse Elevation), T1134 (Access Token)
Defense EvasionT1070 (Indicator Removal), T1036 (Masquerading)
Credential AccessT1003 (OS Credential Dumping), T1110 (Brute Force)
DiscoveryT1082 (System Info), T1083 (File/Directory Discovery)
Lateral MovementT1021 (Remote Services), T1570 (Lateral Tool Transfer)
CollectionT1005 (Data from Local System), T1114 (Email Collection)
ExfiltrationT1041 (Exfil Over C2), T1048 (Exfil Over Alt Protocol)
ImpactT1486 (Data Encrypted), T1489 (Service Stop)

Common Conversion Patterns

Authentication Monitoring

SPL:

spl
index=windows EventCode=4625 | stats count by Account_Name | where count > 10

KQL:

kql
SecurityEvent
| where EventID == 4625
| summarize FailedCount = count() by TargetUserName
| where FailedCount > 10

YARA-L:

yaral
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:

cql
event_simpleName=UserLogon LogonType=10
| groupBy([UserName])
| count(aid, as=logon_attempts)
| test(logon_attempts > 10)

Confidence Scoring

When converting rules, assess confidence:

ScoreMeaning
0.90-1.00Perfect conversion, all fields mapped
0.75-0.89Good conversion, minor assumptions made
0.60-0.74Moderate conversion, some logic simplified
0.40-0.59Partial conversion, manual review needed
<0.40Significant gaps, major rework required

Factors affecting confidence:

  • Field availability in target platform
  • Aggregation complexity
  • Multi-event correlation
  • Custom field extractions
  • Platform-specific features