Detection rules › Kusto
Speculus - Network traffic to or from high-risk IP indicator
'Matches network events in CommonSecurityLog against high-risk Speculus IP indicators (risk score >= 75) received in the last 14 days. These IPs are associated with malicious activity such as command and control, botnets, ransomware infrastructure, or brute-force campaigns.'
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule body
id: a7c3f9e2-4d18-4b6a-9f0c-2e5d8b1a6c43
name: Speculus - Network traffic to or from high-risk IP indicator
description: |
'Matches network events in CommonSecurityLog against high-risk Speculus IP indicators (risk score >= 75)
received in the last 14 days. These IPs are associated with malicious activity such as command and control,
botnets, ransomware infrastructure, or brute-force campaigns.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: SpeculusThreatIntel
dataTypes:
- Speculus_Indicators_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
- InitialAccess
relevantTechniques:
- T1071
- T1090
query: |
let indicators = Speculus_Indicators_CL
| where TimeGenerated > ago(14d)
| where isnotempty(IndicatorValue)
| where RiskScore >= 75
| summarize arg_max(Modified, *) by IndicatorValue
| project
IndicatorValue,
RiskScore,
SpeculusActivity = Activity,
Attribution,
Labels,
IndicatorDescription = Description;
CommonSecurityLog
| where TimeGenerated > ago(1h)
| where isnotempty(SourceIP) or isnotempty(DestinationIP)
| extend MatchIP = iff(isnotempty(DestinationIP), DestinationIP, SourceIP)
| join kind=inner indicators on $left.MatchIP == $right.IndicatorValue
| project
TimeGenerated,
MatchIP,
SourceIP,
DestinationIP,
DeviceVendor,
DeviceProduct,
RiskScore,
SpeculusActivity,
Attribution,
Labels,
IndicatorDescription
suppressionDuration: 1h
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1d
matchingMethod: Selected
groupByEntities:
- IP
eventGroupingSettings:
aggregationKind: AlertPerResult
customDetails:
RiskScore: RiskScore
Attribution: Attribution
Labels: Labels
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: MatchIP
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 0: let
let indicators = Speculus_Indicators_CL
| where TimeGenerated > ago(14d)
| where isnotempty(IndicatorValue)
| where RiskScore >= 75
| summarize arg_max(Modified, *) by IndicatorValue
| project
IndicatorValue,
RiskScore,
SpeculusActivity = Activity,
Attribution,
Labels,
IndicatorDescription = Description;
Stage 1: source
CommonSecurityLog
Stage 2: where
| where TimeGenerated > ago(1h)
Stage 3: where
| where isnotempty(SourceIP) or isnotempty(DestinationIP)
Stage 4: extend
| extend MatchIP = iff(isnotempty(DestinationIP), DestinationIP, SourceIP)
MatchIP =if
isnotempty(DestinationIP)DestinationIPelse
SourceIPStage 5: join
| join kind=inner indicators on $left.MatchIP == $right.IndicatorValue
Stage 6: project
project Attribution, DestinationIP, DeviceProduct, DeviceVendor, IndicatorDescription, Labels, MatchIP, RiskScore, SourceIP, SpeculusActivity, TimeGenerated
Stage 7: summarize aggregation inside the join branch
summarize by IndicatorValue
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DestinationIP | is_not_null | field:"DestinationIP" kind:is_not_null | |
IndicatorValue | is_not_null | field:"IndicatorValue" kind:is_not_null | |
RiskScore | ge |
| field:"RiskScore" kind:ge value:"75" |
SourceIP | is_not_null | field:"SourceIP" kind:is_not_null |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
IndicatorValue | summarize |