Detection rules › Kusto
Kusto rules: 35
Awake Security - Model With Multiple Destinations
#This query searches for devices with multiple possibly malicious destinations.
Rule Body
id: dfa3ec92-bdae-410f-b675-fe1814e4d43e
name: Awake Security - Model With Multiple Destinations
description: This query searches for devices with multiple possibly malicious destinations.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
CommonSecurityLog | where DeviceVendor == "Arista Networks" and DeviceProduct == "Awake Security"
| summarize StartTime=min(TimeGenerated), EndTime=max(TimeGenerated), Models=make_set(Activity), ASPMatchURLs=make_set(DeviceCustomString2), SourceIPs=make_set(SourceIP),
DestinationIPs=make_set(DestinationIP), ModelMatchCount=sum(EventCount), MaxSeverity=max(toint(LogSeverity)) by SourceHostName
| where array_length(DestinationIPs) > 1
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: Awake Security - Model Matches With Multiple Destinations On Device {{SourceHostName}}
alertDescriptionFormat: |
Device {{SourceHostName}} communicated with multiple possibly malicious destinations. The destination IPs were:
{{DestinationIPs}}
The associated with Awake model(s) were:
{{Models}}
alertTacticsColumnName: null
alertSeverityColumnName: null
customDetails:
Matched_Models: Models
Matches_ASP_URLs: ASPMatchURLs
Device: SourceHostName
Matches_Count: ModelMatchCount
Matches_Max_Severity: MaxSeverity
Matches_Dest_IPs: DestinationIPs
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: SourceHostName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIPs
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: true
lookbackDuration: 3d
matchingMethod: Selected
groupByEntities:
- Host
groupByAlertDetails: []
groupByCustomDetails:
- Device
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
CommonSecurityLog
Stage 2: where
| where DeviceVendor == "Arista Networks" and DeviceProduct == "Awake Security"
Stage 3: summarize
| summarize StartTime=min(TimeGenerated), EndTime=max(TimeGenerated), Models=make_set(Activity), ASPMatchURLs=make_set(DeviceCustomString2), SourceIPs=make_set(SourceIP),
DestinationIPs=make_set(DestinationIP), ModelMatchCount=sum(EventCount), MaxSeverity=max(toint(LogSeverity)) by SourceHostName
Stage 4: where
| where array_length(DestinationIPs) > 1
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DeviceProduct | eq |
| field:"DeviceProduct" kind:eq value:"Awake Security" |
DeviceVendor | eq |
| field:"DeviceVendor" kind:eq value:"Arista Networks" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ASPMatchURLs | summarize |
DestinationIPs | summarize |
EndTime | summarize |
MaxSeverity | summarize |
ModelMatchCount | summarize |
Models | summarize |
SourceHostName | summarize |
SourceIPs | summarize |
StartTime | summarize |
Box - Many items deleted by user
#'Detects when a user deletes many items in short period of time.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 1b212329-6f2c-46ca-9071-de3464f3d88d
name: Box - Many items deleted by user
description: |
'Detects when a user deletes many items in short period of time.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BoxDataConnector
dataTypes:
- BoxEvents_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1485
query: |
let threshold = 100;
BoxEvents
| where EventType =~ 'DELETE'
| summarize deleted_items = dcount(SourceItemName) by SrcUserName, bin(EventEndTime, 5m)
| where deleted_items > threshold
| extend AccountCustomEntity = SrcUserName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
version: 1.0.1
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 100;
Stage 1: source
BoxEvents
Stage 2: where
| where EventType =~ 'DELETE'
Stage 3: summarize
| summarize deleted_items = dcount(SourceItemName) by SrcUserName, bin(EventEndTime, 5m)
Stage 4: where
| where deleted_items > threshold
Stage 5: extend
| extend AccountCustomEntity = SrcUserName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | eq |
| field:"EventType" kind:eq value:"DELETE" |
deleted_items | gt |
| field:"deleted_items" kind:gt value:"100" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SrcUserName | summarize |
deleted_items | summarize |
AccountCustomEntity | extend |
CloudNGFW By Palo Alto Networks - possible internal to external port scanning
#'Identifies a list of internal Source IPs (10.x.x.x Hosts) that have triggered 10 or more non-graceful tcp server resets from one or more Destination IPs which results in an "app = incomplete" designation. The server resets coupled with an "Incomplete" app designation can be an indication of internal to external port scanning or probing attack. References: https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClUvCAK https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClTaCAK'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Rule Body
id: 5b72f527-e3f6-4a00-9908-8e4fee14da9f
name: CloudNGFW By Palo Alto Networks - possible internal to external port scanning
description: |
'Identifies a list of internal Source IPs (10.x.x.x Hosts) that have triggered 10 or more non-graceful tcp server resets from one or more Destination IPs which results in an "app = incomplete" designation. The server resets coupled with an "Incomplete" app designation can be an indication of internal to external port scanning or probing attack.
References: https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClUvCAK
https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClTaCAK'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: AzureCloudNGFWByPaloAltoNetworks
dataTypes:
- fluentbit_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
relevantTechniques:
- T1046
query: |
fluentbit_CL
| where ident_s == "TRAFFIC"
| where isnotempty(FirewallName_s)
| extend message = parse_json(Message)
| extend DestinationPort = tostring(message.dport)
| extend SourceIP = tostring(message.src_ip)
| extend DestinationIP = tostring(message.dst_ip)
| extend Application = tostring(message.app)
| extend Protocol = tostring(message.proto)
| extend Action = tostring(message.action)
| where isnotempty(DestinationPort) and message.action !in ("reset-both", "deny")
| where DestinationPort !in ("443", "53", "389", "80", "0", "880", "8888", "8080")
| where message.app == "incomplete"
| where toint(DestinationPort) !between (49512 .. 65535)
| where message.dst_ip !startswith "10."
| extend Reason = coalesce(column_ifexists("Reason", ""), tostring(message.session_end_reason), "")
| where Reason !has "aged-out"
| where Reason !has "tcp-fin"
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by FirewallName_s, SourceIP, Application, Reason, DestinationPort, Protocol, ident_s, Action, DestinationIP
| where count_ >= 10
| summarize StartTime = min(StartTime), EndTime = max(EndTime), makeset(DestinationIP), totalcount = sum(count_) by FirewallName_s, SourceIP, Application, Reason, DestinationPort, Protocol, ident_s, Action
| extend IPAddress = SourceIP
| extend HostName = tostring(split(FirewallName_s, ".")[0]), DomainIndex = toint(indexof(FirewallName_s, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(FirewallName_s, DomainIndex + 1), FirewallName_s)
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: FirewallName_s
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: HostNameDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPAddress
version: 1.0.6
kind: Scheduled
Stages and Predicates
Stage 1: source
fluentbit_CL
Stage 2: where
| where ident_s == "TRAFFIC"
Stage 3: where
| where isnotempty(FirewallName_s)
Stage 4: extend (7 consecutive steps)
| extend message = parse_json(Message)
| extend DestinationPort = tostring(message.dport)
| extend SourceIP = tostring(message.src_ip)
| extend DestinationIP = tostring(message.dst_ip)
| extend Application = tostring(message.app)
| extend Protocol = tostring(message.proto)
| extend Action = tostring(message.action)
Stage 5: where
| where isnotempty(DestinationPort) and message.action !in ("reset-both", "deny")
Stage 6: where
| where DestinationPort !in ("443", "53", "389", "80", "0", "880", "8888", "8080")
Stage 7: where
| where message.app == "incomplete"
Stage 8: where
| where toint(DestinationPort) !between (49512 .. 65535)
Stage 9: where
| where message.dst_ip !startswith "10."
Stage 10: extend
| extend Reason = coalesce(column_ifexists("Reason", ""), tostring(message.session_end_reason), "")
Stage 11: where
| where Reason !has "aged-out"
Stage 12: where
| where Reason !has "tcp-fin"
Stage 13: summarize
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by FirewallName_s, SourceIP, Application, Reason, DestinationPort, Protocol, ident_s, Action, DestinationIP
Stage 14: where
| where count_ >= 10
Stage 15: summarize
| summarize StartTime = min(StartTime), EndTime = max(EndTime), makeset(DestinationIP), totalcount = sum(count_) by FirewallName_s, SourceIP, Application, Reason, DestinationPort, Protocol, ident_s, Action
Stage 16: extend (3 consecutive steps)
| extend IPAddress = SourceIP
| extend HostName = tostring(split(FirewallName_s, ".")[0]), DomainIndex = toint(indexof(FirewallName_s, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(FirewallName_s, DomainIndex + 1), FirewallName_s)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DestinationPort | is_not_null | field:"DestinationPort" kind:is_not_null | |
FirewallName_s | is_not_null | field:"FirewallName_s" kind:is_not_null | |
count_ | ge |
| field:"count_" kind:ge value:"10" |
ident_s | eq |
| field:"ident_s" kind:eq value:"TRAFFIC" |
message.app | eq |
| field:"message.app" kind:eq value:"incomplete" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
message.action | in | deny, reset-both | excludes:message.action field:"message.action" value:"deny" field:"message.action" value:"reset-both" |
DestinationPort | in | 0, 389, 443, 53, 80, 8080, 880, 8888 | excludes:DestinationPort |
DestinationPort | ge | 49512 | excludes:DestinationPort field:"DestinationPort" value:"49512" |
DestinationPort | le | 65535 | excludes:DestinationPort field:"DestinationPort" value:"65535" |
message.dst_ip | starts_with | 10. | excludes:message.dst_ip field:"message.dst_ip" value:"10." |
Reason | match | aged-out | excludes:Reason field:"Reason" value:"aged-out" |
Reason | match | tcp-fin | excludes:Reason field:"Reason" value:"tcp-fin" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Action | summarize |
Application | summarize |
DestinationPort | summarize |
EndTime | summarize |
FirewallName_s | summarize |
Protocol | summarize |
Reason | summarize |
SourceIP | summarize |
StartTime | summarize |
ident_s | summarize |
totalcount | summarize |
IPAddress | extend |
DomainIndex | extend |
HostName | extend |
HostNameDomain | extend |
Cross-tenant Access Settings Organization Inbound Direct Settings Changed
#Organizations are added in the Cross-tenant Access Settings to control communication inbound or outbound for users and applications. This detection notifies when Organization Inbound Direct Settings are changed for "Users & Groups" and for "Applications".
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Persistence | |
| Discovery |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Azure | any: Entra ID audit event (any operation) |
Rules Detecting the Same Action
These rules filter on the same operation.
- Cross-tenant Access Settings Organization Added (Kusto)
- Cross-tenant Access Settings Organization Deleted (Kusto)
- Cross-tenant Access Settings Organization Inbound Collaboration Settings Changed (Kusto)
- Cross-tenant Access Settings Organization Outbound Collaboration Settings Changed (Kusto)
- Cross-tenant Access Settings Organization Outbound Direct Settings Changed (Kusto)
Rule Body
id: 276d5190-38de-4eb2-9933-b3b72f4a5737
name: Cross-tenant Access Settings Organization Inbound Direct Settings Changed
description: |
'Organizations are added in the Cross-tenant Access Settings to control communication inbound or outbound for users and applications. This detection notifies when Organization Inbound Direct Settings are changed for "Users & Groups" and for "Applications".'
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 2d
queryPeriod: 2d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- InitialAccess
- Persistence
- Discovery
relevantTechniques:
- T1078.004
- T1136.003
- T1087.004
query: |
// In User & Groups and in Applications, the following "AccessType" values in columns PremodifiedInboundSettings and ModifiedInboundSettings are interpreted accordingly
// When Access Type in premodified inbound settings value was 1 that means that the initial access was allowed. When Access Type in premodified inbound settings value was 2 that means that the initial access was blocked.
// When Access Type in modified inbound settings value is 1 that means that now access is allowed. When Access Type in modified inbound settings value is 2 that means that now access is blocked.
AuditLogs
| where OperationName has "Update a partner cross-tenant access setting"
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type =~ "Policy"
| extend Properties = TargetResource.modifiedProperties
)
| mv-apply Property = Properties on
(
where Property.displayName =~ "b2bDirectConnectInbound"
| extend PremodifiedInboundSettings = trim('"',tostring(Property.oldValue)),
ModifiedInboundSettings = trim(@'"',tostring(Property.newValue))
)
| where PremodifiedInboundSettings != ModifiedInboundSettings
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: InitiatingAppName
- identifier: AadUserId
columnName: InitiatingAppServicePrincipalId
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: InitiatingUserPrincipalName
- identifier: Name
columnName: InitiatingAccountName
- identifier: UPNSuffix
columnName: InitiatingAccountUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: InitiatingAadUserId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: InitiatingIpAddress
version: 1.1.1
kind: Scheduled
Stages and Predicates
Stage 1: source
AuditLogs
Stage 2: where
| where OperationName has "Update a partner cross-tenant access setting"
Stage 3: kusto:mv-apply
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type =~ "Policy"
| extend Properties = TargetResource.modifiedProperties
)
Stage 4: kusto:mv-apply
| mv-apply Property = Properties on
(
where Property.displayName =~ "b2bDirectConnectInbound"
| extend PremodifiedInboundSettings = trim('"',tostring(Property.oldValue)),
ModifiedInboundSettings = trim(@'"',tostring(Property.newValue))
)
Stage 5: where
| where PremodifiedInboundSettings != ModifiedInboundSettings
Stage 6: extend (6 consecutive steps)
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
OperationName | match |
| field:"OperationName" kind:match value:"Update a partner cross-tenant access setting" |
Property.displayName | eq |
| field:"Property.displayName" kind:eq value:"b2bDirectConnectInbound" |
TargetResource.type | eq |
| field:"TargetResource.type" kind:eq value:"Policy" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
InitiatingAppName | extend |
InitiatingAppServicePrincipalId | extend |
InitiatingUserPrincipalName | extend |
InitiatingAadUserId | extend |
InitiatingIpAddress | extend |
InitiatingAccountName | extend |
InitiatingAccountUPNSuffix | extend |
Cyble Vision Alerts OT/ICS Threat Activity Detected
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
'This alert indicates detection of OT/ICS-related network activity involving industrial control protocols (e.g., IEC104). May indicate probing, reconnaissance, or attempted access against critical infrastructure assets.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery | |
| Collection |
Rule Body
id: c1ebc79d-7f46-429e-bf2c-8bb0b75ba6b2
name: Cyble Vision Alerts OT/ICS Threat Activity Detected
description: |
'This alert indicates detection of OT/ICS-related network activity involving industrial control protocols (e.g., IEC104). May indicate probing, reconnaissance, or attempted access against critical infrastructure assets.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
dataTypes:
- CybleVisionAlerts_CL
enabled: true
queryfrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- Discovery
- Collection
relevantTechniques:
- T0842
- T0830
query: |
Alerts_ot_ics
| where Service == "ot_ics"
| extend MappedSeverity = Severity
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
alertDetailsOverride:
alertDisplayNameFormat: OT/ICS Activity Detected from {{OT_SourceIP}}
description: |
Cyble detected industrial control system related traffic involving protocol {{OT_DataType}} from IP {{OT_SourceIP}}. Destination port {{OT_DestPort}}. Country. {{OT_Country}}. This may indicate reconnaissance or hostile probing of OT/ICS infrastructure.
customDetails:
MappedSeverity: Severity
Status: Status
AlertID: AlertID
Service: Service
OT_ASN: OT_ASN
OT_Country: OT_Country
OT_SourceIP: OT_SourceIP
OT_DataType: OT_DataType
OT_DestPort: OT_DestPort
OT_IP_Reputation: OT_IP_Reputation
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: OT_SourceIP
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Alerts_ot_ics
Stage 2: where
| where Service == "ot_ics"
Stage 3: extend
| extend MappedSeverity = Severity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Service | eq |
| field:"Service" kind:eq value:"ot_ics" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
MappedSeverity | extend |
CYFIRMA - Medium severity Malicious Network Indicators with Monitor Action Rule
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
"This rule detects Network indicators such as IP addresses, domains, and URLs reported by CYFIRMA threat intelligence with a recommended action of 'Monitor' and no specified role. These indicators may be associated with infrastructure used in phishing campaigns, malware, or initial access via watering hole or drive-by compromise techniques."
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance | |
| Initial Access | |
| Execution | |
| Impact |
Rule Body
id: 52c2f8d4-1dc8-4141-9152-614c036390a0
name: CYFIRMA - Medium severity Malicious Network Indicators with Monitor Action Rule
description: |
"This rule detects Network indicators such as IP addresses, domains, and URLs reported by CYFIRMA threat intelligence with a recommended action of 'Monitor' and no specified role.
These indicators may be associated with infrastructure used in phishing campaigns, malware, or initial access via watering hole or drive-by compromise techniques."
version: 1.0.1
kind: Scheduled
severity: Medium
enabled: false
requiredDataConnectors:
- connectorId: CyfirmaCyberIntelligenceDC
dataTypes:
- CyfirmaIndicators_CL
query: |
//Network Indicators with Monitor Action
let timeFrame= 5m;
CyfirmaIndicators_CL
| where (ConfidenceScore < 80 and ConfidenceScore >= 50)
and TimeGenerated between (ago(timeFrame) .. now())
and pattern !contains 'file:hashes' and RecommendedActions has 'Monitor' and (isempty(Roles) or not(Roles has_any ('Command & Control', 'Malware', 'TOR', 'Trojan', 'Phishing')))
| extend IPv4 = extract(@"ipv4-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend IPv6 = extract(@"ipv6-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend URL = extract(@"url:value\s*=\s*'([^']+)'", 1, pattern)
| extend Domain = extract(@"domain-name:value\s*=\s*'([^']+)'", 1, pattern)
| extend parsed = parse_json(extensions)
| extend extensionKeys = bag_keys(parsed)
| mv-expand extensionKeys
| extend extensionKeyStr = tostring(extensionKeys)
| extend ext = parsed[extensionKeyStr]
| extend props = ext.properties
| extend
extension_id = extensionKeyStr,
ASN_Owner = props.asn_owner,
ASN = props.asn,
ProviderName = 'CYFIRMA',
ProductName = 'DeCYFIR/DeTCT'
| project
IPv4,
IPv6,
URL,
Domain,
ThreatActors,
RecommendedActions,
Sources,
Roles,
Country,
IPAbuse,
name,
Description,
ConfidenceScore,
IndicatorID,
created,
modified,
valid_from,
Tags,
ThreatType,
TimeGenerated,
SecurityVendors,
ProductName,
ProviderName
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: 5m
suppressionEnabled: true
tactics:
- InitialAccess
- Execution
- Reconnaissance
- Impact
relevantTechniques:
- T1566
- T1189
- T1204
- T1595
- T1498
- T1566.002
- T1204.001
- T1595.002
alertDetailsOverride:
alertDisplayNameFormat: "High-Confidence Malicious Network Indicators with Monitor Action Rule - {{name}} "
alertDescriptionFormat: "{{Description}} - {{name}} "
alertDynamicProperties:
- alertProperty: ProductName
value: ProductName
- alertProperty: ProviderName
value: ProviderName
customDetails:
ThreatActors: ThreatActors
Sources: Sources
RecommendedActions: RecommendedActions
Roles: Roles
Country: Country
Description: Description
ConfidenceScore: ConfidenceScore
SecurityVendors: SecurityVendors
IndicatorID: IndicatorID
Created: created
Modified: modified
ValidFrom: valid_from
Tags: Tags
ThreatType: ThreatType
TimeGenerated: TimeGenerated
IPAbuse: IPAbuse
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPv4
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPv6
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: Domain
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URL
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: AlertPerResult
Stages and Predicates
Parameters
let timeFrame = 5m;
Stage 1: source
CyfirmaIndicators_CL
Stage 2: where
| where (ConfidenceScore < 80 and ConfidenceScore >= 50)
and TimeGenerated between (ago(timeFrame) .. now())
and pattern !contains 'file:hashes' and RecommendedActions has 'Monitor' and (isempty(Roles) or not(Roles has_any ('Command & Control', 'Malware', 'TOR', 'Trojan', 'Phishing')))
Stage 3: extend (6 consecutive steps)
| extend IPv4 = extract(@"ipv4-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend IPv6 = extract(@"ipv6-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend URL = extract(@"url:value\s*=\s*'([^']+)'", 1, pattern)
| extend Domain = extract(@"domain-name:value\s*=\s*'([^']+)'", 1, pattern)
| extend parsed = parse_json(extensions)
| extend extensionKeys = bag_keys(parsed)
Stage 4: mv-expand
| mv-expand extensionKeys
Stage 5: extend (4 consecutive steps)
| extend extensionKeyStr = tostring(extensionKeys)
| extend ext = parsed[extensionKeyStr]
| extend props = ext.properties
| extend
extension_id = extensionKeyStr,
ASN_Owner = props.asn_owner,
ASN = props.asn,
ProviderName = 'CYFIRMA',
ProductName = 'DeCYFIR/DeTCT'
Stage 6: project
| project
IPv4,
IPv6,
URL,
Domain,
ThreatActors,
RecommendedActions,
Sources,
Roles,
Country,
IPAbuse,
name,
Description,
ConfidenceScore,
IndicatorID,
created,
modified,
valid_from,
Tags,
ThreatType,
TimeGenerated,
SecurityVendors,
ProductName,
ProviderName
Indicators
These rows show field, operator, and value matches.
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
pattern | contains | file:hashes | excludes:pattern field:"pattern" value:"file:hashes" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ConfidenceScore | project |
Country | project |
Description | project |
Domain | project |
IPAbuse | project |
IPv4 | project |
IPv6 | project |
IndicatorID | project |
ProductName | project |
ProviderName | project |
RecommendedActions | project |
Roles | project |
SecurityVendors | project |
Sources | project |
Tags | project |
ThreatActors | project |
ThreatType | project |
TimeGenerated | project |
URL | project |
created | project |
modified | project |
name | project |
valid_from | project |
Cynerio - Medical device scanning
#Medical device is scanned with vulnerability scanner
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Lateral Movement |
Rule Body
id: 211e9f49-3fca-4598-bc6e-e2c28d86e72c
name: Cynerio - Medical device scanning
description: Medical device is scanned with vulnerability scanner
severity: Medium
requiredDataConnectors:
- connectorId: CynerioSecurityEvents
dataTypes:
- CynerioEvent_CL
queryFrequency: 5h
queryPeriod: 5h
triggerOperator: gt
triggerThreshold: 0
tactics:
- LateralMovement
relevantTechniques:
- T0866
query: |
CynerioEvent_CL
| where TimeGenerated > ago(24h)
| where module_s == 'IDS'
| where title_s == 'Scanner Activity'
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: asset_ip_s
eventGroupingSettings:
aggregationKind: SingleAlert
customDetails:
DeviceType: asset_type_s
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
CynerioEvent_CL
Stage 2: where
| where TimeGenerated > ago(24h)
Stage 3: where
| where module_s == 'IDS'
Stage 4: where
| where title_s == 'Scanner Activity'
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
module_s | eq |
| field:"module_s" kind:eq value:"IDS" |
title_s | eq |
| field:"title_s" kind:eq value:"Scanner Activity" |
Dataverse - Mass deletion of records
#Identifies large scale record delete operations based on a predefined threshold and also detects scheduled bulk deletion jobs.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft 365 | any: Dataverse and model-driven apps audit (catch-all) |
Rule Body
id: 716cf6d4-97ad-407b-923e-6790083acb58
kind: Scheduled
name: Dataverse - Mass deletion of records
description: Identifies large scale record delete operations based on a predefined
threshold and also detects scheduled bulk deletion jobs.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Dataverse
dataTypes:
- DataverseActivity
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1485
query: |
let mass_delete_threshold = 10000;
let query_frequency = 1d;
let delete_activities = DataverseActivity
| where TimeGenerated >= ago(query_frequency)
| where Message == "Delete";
union
(
delete_activities
| summarize FirstEvent = min(TimeGenerated), TotalEvents = count() by UserId, InstanceUrl
| where TotalEvents > mass_delete_threshold
| join kind=inner (
delete_activities
| summarize DeleteCount = count() by UserId, InstanceUrl, ClientIp, EntityName)
on UserId, InstanceUrl
| extend Entities = bag_pack("Entity", EntityName, "Count", DeleteCount)
| summarize Details = make_set(Entities, 100), FirstEvent = min(FirstEvent) by UserId, InstanceUrl, ClientIp, TotalEvents
),
(
DataverseActivity
| where TimeGenerated >= ago(query_frequency)
| where Message == "BulkDelete"
| summarize FirstEvent = min(TimeGenerated), TotalEvents = count() by UserId, InstanceUrl, ClientIp
| extend Details = todynamic("Bulk delete scheduled")
)
| extend
CloudAppId = int(32780),
AccountName = tostring(split(UserId, '@')[0]),
UPNSuffix = tostring(split(UserId, '@')[1])
| project
FirstEvent,
UserId,
ClientIp,
TotalEvents,
Details,
InstanceUrl,
CloudAppId,
AccountName,
UPNSuffix
eventGroupingSettings:
aggregationKind: AlertPerResult
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: CloudApplication
fieldMappings:
- identifier: AppId
columnName: CloudAppId
- identifier: InstanceName
columnName: InstanceUrl
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ClientIp
alertDetailsOverride:
alertDisplayNameFormat: 'Dataverse - mass deletion or bulk deletion job detected
in {{InstanceUrl}} '
alertDescriptionFormat: '{{UserId}} triggered the mass deletion detection with the
following information: {{Details}}'
version: 3.2.0
Stages and Predicates
Parameters
let mass_delete_threshold = 10000;
let query_frequency = 1d;
Union (2 Sources)
Each leg below queries one source; the rule matches if any leg does. Sources: delete_activities, DataverseActivity
Leg 1: delete_activities
delete_activities
| summarize FirstEvent = min(TimeGenerated), TotalEvents = count() by UserId, InstanceUrl
| where TotalEvents > mass_delete_threshold
| join kind=inner (
delete_activities
| summarize DeleteCount = count() by UserId, InstanceUrl, ClientIp, EntityName)
on UserId, InstanceUrl
| extend Entities = bag_pack("Entity", EntityName, "Count", DeleteCount)
| summarize Details = make_set(Entities, 100), FirstEvent = min(FirstEvent) by UserId, InstanceUrl, ClientIp, TotalEvents
Leg 2: DataverseActivity
DataverseActivity
| where TimeGenerated >= ago(query_frequency)
| where Message == "BulkDelete"
| summarize FirstEvent = min(TimeGenerated), TotalEvents = count() by UserId, InstanceUrl, ClientIp
| extend Details = todynamic("Bulk delete scheduled")
Applied to the Combined Result
| extend
CloudAppId = int(32780),
AccountName = tostring(split(UserId, '@')[0]),
UPNSuffix = tostring(split(UserId, '@')[1])
| project
FirstEvent,
UserId,
ClientIp,
TotalEvents,
Details,
InstanceUrl,
CloudAppId,
AccountName,
UPNSuffix
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Message | eq |
| field:"Message" kind:eq |
TotalEvents | gt |
| field:"TotalEvents" kind:gt value:"10000" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ClientIp | summarize |
EntityName | summarize |
InstanceUrl | summarize |
UserId | summarize |
Failed sign-ins into LastPass due to MFA
#This rule will check if a sign-in failed into LastPass due to MFA. An incident can indicate the potential brute forcing of a LastPass account. The use of MFA is identified by combining the sign-in logs, this rule assumes LastPass is federated to Entra ID.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Entra ID | SigninLogs sign-in event: Sign-in naming a target application |
Rule Body
id: 760b8467-e6cc-4006-9149-5696845c1a54
name: Failed sign-ins into LastPass due to MFA
description: |
'This rule will check if a sign-in failed into LastPass due to MFA.
An incident can indicate the potential brute forcing of a LastPass account.
The use of MFA is identified by combining the sign-in logs, this rule assumes LastPass is federated to Entra ID.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: LastPass
dataTypes:
- LastPassNativePoller_CL
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- InitialAccess
relevantTechniques:
- T1078
- T1190
query: |
LastPassNativePoller_CL
| where Action_s == "Log in"
| join (SigninLogs | where AppDisplayName == "LastPass Enterprise") on $left.IP_Address_s == $right.IPAddress and $left.Username_s == $right.UserPrincipalName
| where ResultType in (50074, 50076)
| extend AccountCustomEntity = Username_s, IPCustomEntity = IP_Address_s
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
LastPassNativePoller_CL
Stage 2: where
| where Action_s == "Log in"
Stage 3: join
| join (SigninLogs | where AppDisplayName == "LastPass Enterprise") on $left.IP_Address_s == $right.IPAddress and $left.Username_s == $right.UserPrincipalName
Stage 4: where
where ResultType in (50074, 50076)
Stage 5: extend
extend AccountCustomEntity, IPCustomEntity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action_s | eq |
| field:"Action_s" kind:eq value:"Log in" |
AppDisplayName | eq |
| field:"Application" kind:eq value:"LastPass Enterprise" |
ResultType | in |
| field:"ResultType" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
IPCustomEntity | extend |
Flare google dork results
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
'This query searches for Google dork search results that indicate publicly accessible information about your organization.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance |
Rule Body
id: 9cb7c337-f174-4af6-b0e8-b6b7552d762d
name: Flare google dork results
description: |
'This query searches for Google dork search results that indicate publicly accessible information about your organization.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Flare
dataTypes:
- FireworkV2_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Reconnaissance
relevantTechniques:
- T1593
query: |
FireworkV2_CL
| where notempty(uid) and RiskScore >= 3
| extend index_name = split(uid, "/")[0]
| extend category_name = split(uid, "/")[1]
| where (index_name == "driller_google") or (index_name == "driller" and category_name contains "google")
version: 3.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
FireworkV2_CL
Stage 2: where
| where notempty(uid) and RiskScore >= 3
Stage 3: extend
| extend index_name = split(uid, "/")[0]
Stage 4: extend
| extend category_name = split(uid, "/")[1]
Stage 5: where
| where (index_name == "driller_google") or (index_name == "driller" and category_name contains "google")
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
RiskScore | ge |
| field:"RiskScore" kind:ge value:"3" |
category_name | contains |
| field:"category_name" kind:contains value:"google" |
index_name | eq |
| field:"index_name" kind:eq |
uid | is_not_null | field:"uid" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
index_name | extend |
category_name | extend |
Guardian- Security Integrity Checks Prompt Injection Policy Violation Detection
#'This alert creates an incident when Security Integrity Checks Prompt Injection Policy Violation detected from the Guardian.'
Rule Body
id: d6b3372a-0fc4-40ec-9630-b96efb527fba
name: Guardian- Security Integrity Checks Prompt Injection Policy Violation Detection
description: |
'This alert creates an incident when Security Integrity Checks Prompt Injection Policy Violation detected from the Guardian.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: BoschAIShield
dataTypes:
- Guardian
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
Guardian
| where PolicyViolatedControlFeature =~ 'Prompt Injection / Jailbreaks'
| where Severity =~ 'High'
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: NTDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIP
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertDisplayNameFormat: Guardian- Security Integrity Checks Prompt Injection Policy Violation detection
alertDescriptionFormat: |
This query detects Security Integrity Checks Prompt Injection Policy Violation detected from the Guardian generated at {{TimeGenerated}}.\n\nPlease check the source for more information and investigate further
alertSeverityColumnName: Severity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Guardian
Stage 2: where
| where PolicyViolatedControlFeature =~ 'Prompt Injection / Jailbreaks'
Stage 3: where
| where Severity =~ 'High'
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
PolicyViolatedControlFeature | eq |
| field:"PolicyViolatedControlFeature" kind:eq value:"Prompt Injection / Jailbreaks" |
Severity | eq |
| field:"Severity" kind:eq value:"High" |
Host Settings Updated
#Detects when host settings are updated in Veeam Backup & Replication. This might indicate configuration changes that require review.
Rule Body
id: ac1a85f1-0e3f-4ae8-9f59-a240fc04b03f
name: Host Settings Updated
description: Detects when host settings are updated in Veeam Backup & Replication.
This might indicate configuration changes that require review.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 28400\n| project\n Date\
\ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n DataSource = original_host,\n\
\ EventId = instanceId,\n [\"User Name\"] = user,\n MessageDetails = Description,\n\
\ Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
Date: Date
VbrHostName: DataSource
EventId: EventId
MessageDetails: MessageDetails
Severity: Severity
Stages and Predicates
Stage 1: source
Veeam_GetSecurityEvents
Stage 2: where
| where instanceId == 28400
Stage 3: project
| project
Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
DataSource = original_host,
EventId = instanceId,
["User Name"] = user,
MessageDetails = Description,
Severity = SeverityDescription
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
instanceId | eq |
| field:"instanceId" kind:eq value:"28400" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DataSource | project |
Date | project |
EventId | project |
MessageDetails | project |
Severity | project |
User Name | project |
Jira - User removed from group
#'Detects when a user was removed from group.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: c13ecb19-4317-4d87-9a1c-52660dd44a7d
name: Jira - User removed from group
description: |
'Detects when a user was removed from group.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: JiraAuditAPI
dataTypes:
- JiraAudit
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1531
query: |
JiraAudit
| where EventMessage =~ 'User removed from group'
| extend user = todynamic(AssociatedItems)[0]['name']
| project EventCreationTime, ObjectItemName, user
| extend AccountCustomEntity = user
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
JiraAudit
Stage 2: where
| where EventMessage =~ 'User removed from group'
Stage 3: extend
| extend user = todynamic(AssociatedItems)[0]['name']
Stage 4: project
| project EventCreationTime, ObjectItemName, user
Stage 5: extend
| extend AccountCustomEntity = user
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventMessage | eq |
| field:"EventMessage" kind:eq value:"User removed from group" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
EventCreationTime | project |
ObjectItemName | project |
user | project |
AccountCustomEntity | extend |
Lumen TI IPAddress in DeviceEvents
#This query maps Lumen IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in DeviceEvents.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceEvents action any: Defender event |
Rule Body
id: fa566691-42a2-4136-6a8b-ffa3ea510000
name: Lumen TI IPAddress in DeviceEvents
displayName: Lumen TI IPAddress in DeviceEvents
description: |
This query maps Lumen IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in DeviceEvents.
severity: Medium
requiredDataConnectors:
- connectorId: LumenThreatFeedConnector
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: ThreatIntelligenceUploadIndicatorsAPI
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceEvents
queryFrequency: 4h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
suppressionDuration: 5h
suppressionEnabled: true
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1d; // Data lookback for DeviceEvents
let ioc_lookBack = 14d; // TI lookback
let IP_Indicators = ThreatIntelIndicators
| where TimeGenerated >= ago(ioc_lookBack)
| where IsActive == true and ValidUntil > now()
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
| where SourceSystem == 'Lumen'
| where ObservableKey == 'ipv4-addr:value'
| extend TI_ipEntity = ObservableValue
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith 'fe80' and TI_ipEntity !startswith '::' and TI_ipEntity !startswith '127.';
IP_Indicators
| join kind=innerunique (
DeviceEvents
| where TimeGenerated >= ago(dt_lookBack)
| extend DE_ipEntity = RemoteIP
| extend DeviceEvents_TimeGenerated = TimeGenerated
) on $left.TI_ipEntity == $right.DE_ipEntity
| where DeviceEvents_TimeGenerated < ValidUntil
| summarize arg_max(DeviceEvents_TimeGenerated, *), StartTime = min(DeviceEvents_TimeGenerated), EndTime = max(DeviceEvents_TimeGenerated) by Id, DE_ipEntity
| project timestamp = EndTime, StartTime, EndTime, DeviceName, RemoteIP, ActionType, ReportId, Id, Tags, ValidUntil, Confidence, TI_ipEntity, DE_ipEntity, Type
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: DE_ipEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1d;
let ioc_lookBack = 14d;
let IP_Indicators is inlined into the numbered stages below.
Stages 1 to 8 define let IP_Indicators as the rule's main pipeline source. Stages 9 to 12 use it.
Stage 1: source
ThreatIntelIndicators
Stage 2: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 3: where
| where IsActive == true and ValidUntil > now()
Stage 4: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
Stage 5: where
| where SourceSystem == 'Lumen'
Stage 6: where
| where ObservableKey == 'ipv4-addr:value'
Stage 7: extend
| extend TI_ipEntity = ObservableValue
Stage 8: where
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith 'fe80' and TI_ipEntity !startswith '::' and TI_ipEntity !startswith '127.'
Stage 9: join
IP_Indicators
| join kind=innerunique (
DeviceEvents
| where TimeGenerated >= ago(dt_lookBack)
| extend DE_ipEntity = RemoteIP
| extend DeviceEvents_TimeGenerated = TimeGenerated
) on $left.TI_ipEntity == $right.DE_ipEntity
Stage 10: where
where DeviceEvents_TimeGenerated < ValidUntil
Stage 11: summarize
summarize EndTime, StartTime by Id, DE_ipEntity
Stage 12: project
project ActionType, Confidence, DE_ipEntity, DeviceName, EndTime, Id, RemoteIP, ReportId, StartTime, TI_ipEntity, Tags, Type, ValidUntil, timestamp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
IsActive | eq |
| field:"IsActive" kind:eq value:"true" |
ObservableKey | eq |
| field:"ObservableKey" kind:eq value:"ipv4-addr:value" |
SourceSystem | eq |
| field:"SourceSystem" kind:eq value:"Lumen" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
TI_ipEntity | cidr_match | 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8 | excludes:TI_ipEntity |
TI_ipEntity | starts_with | 127. | excludes:TI_ipEntity field:"TI_ipEntity" value:"127." |
TI_ipEntity | starts_with | :: | excludes:TI_ipEntity field:"TI_ipEntity" value:"::" |
TI_ipEntity | starts_with | fe80 | excludes:TI_ipEntity field:"TI_ipEntity" value:"fe80" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActionType | project |
Confidence | project |
DE_ipEntity | project |
DeviceName | project |
EndTime | project |
Id | project |
RemoteIP | project |
ReportId | project |
StartTime | project |
TI_ipEntity | project |
Tags | project |
Type | project |
ValidUntil | project |
timestamp | project |
Netskope - Large Outbound Data Transfer / Sensitive Upload (DLP)
#Detects large outbound data transfers and sensitive file uploads. Monitors for potential data exfiltration via cloud applications.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
Rule Body
id: 71e6586e-0d3f-4e33-b390-faa50b5e08fa
name: Netskope - Large Outbound Data Transfer / Sensitive Upload (DLP)
description: |
Detects large outbound data transfers and sensitive file uploads. Monitors for potential data exfiltration via cloud applications.
severity: High
status: Available
requiredDataConnectors:
- connectorId: NetskopeWebTxConnector
dataTypes:
- NetskopeWebTransactions_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
relevantTechniques:
- T1567
- T1048
query: |
let uploadThresholdMB = 100;
NetskopeWebTransactions_CL
| where TimeGenerated > ago(1h)
| where isnotempty(CsUsername)
| where XCsAppActivity =~ 'Upload' or XCsAppActivity =~ 'Post' or XCsAppActivity =~ 'Share'
or CsMethod =~ 'POST' or CsMethod =~ 'PUT'
| where CsBytes > 0 or XRsFileSize > 0
| extend UploadBytes = coalesce(CsBytes, XRsFileSize, 0)
| summarize
TotalUploadBytes = sum(UploadBytes),
FileCount = dcount(XCsAppObjectName),
Files = make_set(XCsAppObjectName, 10),
FileTypes = make_set(XRsFileType),
Apps = make_set(XCsApp),
Destinations = make_set(XCsAppInstanceName),
Countries = make_set(XSCountry)
by CsUsername, XCDevice, XCCountry, bin(TimeGenerated, 1h)
| extend TotalUploadMB = round(TotalUploadBytes / 1048576.0, 2)
| where TotalUploadMB > uploadThresholdMB
| extend AlertSeverity = case(
TotalUploadMB > 1000, 'Critical',
TotalUploadMB > 500, 'High',
TotalUploadMB > 100, 'Medium',
'Low')
| project
TimeGenerated,
User = CsUsername,
UploadVolumeMB = TotalUploadMB,
FileCount,
Files,
FileTypes,
Applications = Apps,
DestinationInstances = Destinations,
SourceCountry = XCCountry,
DestinationCountries = Countries,
Device = XCDevice,
AlertSeverity
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: User
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let uploadThresholdMB = 100;
Stage 1: source
NetskopeWebTransactions_CL
Stage 2: where
| where TimeGenerated > ago(1h)
Stage 3: where
| where isnotempty(CsUsername)
Stage 4: where
| where XCsAppActivity =~ 'Upload' or XCsAppActivity =~ 'Post' or XCsAppActivity =~ 'Share'
or CsMethod =~ 'POST' or CsMethod =~ 'PUT'
Stage 5: where
| where CsBytes > 0 or XRsFileSize > 0
Stage 6: extend
| extend UploadBytes = coalesce(CsBytes, XRsFileSize, 0)
Stage 7: summarize
| summarize
TotalUploadBytes = sum(UploadBytes),
FileCount = dcount(XCsAppObjectName),
Files = make_set(XCsAppObjectName, 10),
FileTypes = make_set(XRsFileType),
Apps = make_set(XCsApp),
Destinations = make_set(XCsAppInstanceName),
Countries = make_set(XSCountry)
by CsUsername, XCDevice, XCCountry, bin(TimeGenerated, 1h)
Stage 8: extend
| extend TotalUploadMB = round(TotalUploadBytes / 1048576.0, 2)
Stage 9: where
| where TotalUploadMB > uploadThresholdMB
Stage 10: extend
| extend AlertSeverity = case(
TotalUploadMB > 1000, 'Critical',
TotalUploadMB > 500, 'High',
TotalUploadMB > 100, 'Medium',
'Low')
AlertSeverity =TotalUploadMB > 1000'Critical'TotalUploadMB > 500'High'TotalUploadMB > 100'Medium''Low'Stage 11: project
| project
TimeGenerated,
User = CsUsername,
UploadVolumeMB = TotalUploadMB,
FileCount,
Files,
FileTypes,
Applications = Apps,
DestinationInstances = Destinations,
SourceCountry = XCCountry,
DestinationCountries = Countries,
Device = XCDevice,
AlertSeverity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CsBytes | gt |
| field:"CsBytes" kind:gt value:"0" |
CsMethod | eq |
| field:"CsMethod" kind:eq |
CsUsername | is_not_null | field:"CsUsername" kind:is_not_null | |
TotalUploadMB | gt |
| field:"TotalUploadMB" kind:gt value:"100" |
XCsAppActivity | eq |
| field:"XCsAppActivity" kind:eq |
XRsFileSize | gt |
| field:"XRsFileSize" kind:gt value:"0" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AlertSeverity | project |
Applications | project |
DestinationCountries | project |
DestinationInstances | project |
Device | project |
FileCount | project |
FileTypes | project |
Files | project |
SourceCountry | project |
TimeGenerated | project |
UploadVolumeMB | project |
User | project |
PaloAlto - File type changed
#'Detects when file type changed.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 9150ad68-51c8-11ec-bf63-0242ac130002
name: PaloAlto - File type changed
description: |
'Detects when file type changed.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
- T1133
query: |
PaloAltoCDLEvent
| where EventResourceId =~ 'THREAT'
| where EventResult =~ 'file'
| where FileType != OldFileType
| extend FileCustomEntity = SrcFileName, AccountCustomEntity = DstUsername
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
- entityType: File
fieldMappings:
- identifier: Name
columnName: FileCustomEntity
version: 1.0.3
kind: Scheduled
Stages and Predicates
Stage 1: source
PaloAltoCDLEvent
Stage 2: where
| where EventResourceId =~ 'THREAT'
Stage 3: where
| where EventResult =~ 'file'
Stage 4: where
| where FileType != OldFileType
Stage 5: extend
| extend FileCustomEntity = SrcFileName, AccountCustomEntity = DstUsername
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventResourceId | eq |
| field:"EventResourceId" kind:eq value:"THREAT" |
EventResult | eq |
| field:"EventResult" kind:eq value:"file" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
FileCustomEntity | extend |
Phishing link click observed in Network Traffic
#'The purpose of this content is to identify successful phishing links accessed by users. Once a user clicks on a phishing link, we observe successful network activity originating from non-Microsoft network devices. These devices may include Palo Alto Networks, Fortinet, Check Point, and Zscaler devices.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 2fed0668-6d43-4c78-87e6-510f96f12145
name: Phishing link click observed in Network Traffic
description: |
'The purpose of this content is to identify successful phishing links accessed by users. Once a user clicks on a phishing link, we observe successful network activity originating from non-Microsoft network devices. These devices may include Palo Alto Networks, Fortinet, Check Point, and Zscaler devices.'
severity: Medium
requiredDataConnectors:
- connectorId: OfficeATP
dataTypes:
- SecurityAlert
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog (PaloAlto)
- connectorId: Fortinet
dataTypes:
- CommonSecurityLog (Fortinet)
- connectorId: CheckPoint
dataTypes:
- CommonSecurityLog (CheckPoint)
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog (Zscaler)
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1566
query: |
//Finding MDO Security alerts and extracting the Entities user, Domain, Ip, and URL.
let Alert_List= dynamic([
"Phishing link click observed in Network Traffic",
"Phish delivered due to an IP allow policy",
"A potentially malicious URL click was detected",
"High Risk Sign-in Observed in Network Traffic",
"A user clicked through to a potentially malicious URL",
"Suspicious network connection to AitM phishing site",
"Messages containing malicious entity not removed after delivery",
"Email messages containing malicious URL removed after delivery",
"Email reported by user as malware or phish",
"Phish delivered due to an ETR override",
"Phish not zapped because ZAP is disabled"]);
SecurityAlert
|where ProviderName in~ ("Office 365 Advanced Threat Protection", "OATP")
| where AlertName in~ (Alert_List)
//extracting Alert Entities
| extend Entities = parse_json(Entities)
| mv-apply Entity = Entities on
(
where Entity.Type == 'account'
| extend EntityUPN = iff(isempty(Entity.UserPrincipalName), tostring(strcat(Entity.Name, "@", tostring (Entity.UPNSuffix))), tostring(Entity.UserPrincipalName))
)
| mv-apply Entity = Entities on
(
where Entity.Type == 'url'
| extend EntityUrl = tostring(Entity.Url)
)
| summarize AccountUpn=tolower(tostring(take_any(EntityUPN))),Url=tostring(tolower(take_any(EntityUrl))),AlertTime= min(TimeGenerated)by SystemAlertId, ProductName
// filtering 3pnetwork devices
| join kind= inner (CommonSecurityLog
| where DeviceVendor has_any ("Palo Alto Networks", "Fortinet", "Check Point", "Zscaler")
| where DeviceAction != "Block"
| where DeviceProduct startswith "FortiGate" or DeviceProduct startswith "PAN" or DeviceProduct startswith "VPN" or DeviceProduct startswith "FireWall" or DeviceProduct startswith "NSSWeblog" or DeviceProduct startswith "URL"
| where isnotempty(RequestURL)
| where isnotempty(SourceUserName)
| extend SourceUserName = tolower(SourceUserName)
| project
3plogTime=TimeGenerated,
DeviceVendor,
DeviceProduct,
Activity,
DestinationHostName,
DestinationIP,
RequestURL=tostring(tolower(RequestURL)),
MaliciousIP,
Name = tostring(split(SourceUserName,"@")[0]),
UPNSuffix =tostring(split(SourceUserName,"@")[1]),
SourceUserName,
IndicatorThreatType,
ThreatSeverity,AdditionalExtensions,
ThreatConfidence)on $left.Url == $right.RequestURL and $left.AccountUpn == $right.SourceUserName
// Applied the condition where alert trigger 1st and then the 3p Network activity execution
| where AlertTime between ((3plogTime - 1h) .. (3plogTime + 1h))
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: SourceUserName
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: DestinationIP
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: DestinationHostName
kind: Scheduled
version: 1.0.4
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Threat Protection" ]
Stages and Predicates
Let Binding: Alert_List
let Alert_List = dynamic([
"Phishing link click observed in Network Traffic",
"Phish delivered due to an IP allow policy",
"A potentially malicious URL click was detected",
"High Risk Sign-in Observed in Network Traffic",
"A user clicked through to a potentially malicious URL",
"Suspicious network connection to AitM phishing site",
"Messages containing malicious entity not removed after delivery",
"Email messages containing malicious URL removed after delivery",
"Email reported by user as malware or phish",
"Phish delivered due to an ETR override",
"Phish not zapped because ZAP is disabled"]);
Stage 1: source
SecurityAlert
Stage 2: where
| where ProviderName in~ ("Office 365 Advanced Threat Protection", "OATP")
Stage 3: where
| where AlertName in~ (Alert_List)
Stage 4: extend
| extend Entities = parse_json(Entities)
Stage 5: kusto:mv-apply
| mv-apply Entity = Entities on
(
where Entity.Type == 'account'
| extend EntityUPN = iff(isempty(Entity.UserPrincipalName), tostring(strcat(Entity.Name, "@", tostring (Entity.UPNSuffix))), tostring(Entity.UserPrincipalName))
)
Stage 6: kusto:mv-apply
| mv-apply Entity = Entities on
(
where Entity.Type == 'url'
| extend EntityUrl = tostring(Entity.Url)
)
Stage 7: summarize
| summarize AccountUpn=tolower(tostring(take_any(EntityUPN))),Url=tostring(tolower(take_any(EntityUrl))),AlertTime= min(TimeGenerated)by SystemAlertId, ProductName
Stage 8: join
| join kind= inner (CommonSecurityLog
| where DeviceVendor has_any ("Palo Alto Networks", "Fortinet", "Check Point", "Zscaler")
| where DeviceAction != "Block"
| where DeviceProduct startswith "FortiGate" or DeviceProduct startswith "PAN" or DeviceProduct startswith "VPN" or DeviceProduct startswith "FireWall" or DeviceProduct startswith "NSSWeblog" or DeviceProduct startswith "URL"
| where isnotempty(RequestURL)
| where isnotempty(SourceUserName)
| extend SourceUserName = tolower(SourceUserName)
| project
3plogTime=TimeGenerated,
DeviceVendor,
DeviceProduct,
Activity,
DestinationHostName,
DestinationIP,
RequestURL=tostring(tolower(RequestURL)),
MaliciousIP,
Name = tostring(split(SourceUserName,"@")[0]),
UPNSuffix =tostring(split(SourceUserName,"@")[1]),
SourceUserName,
IndicatorThreatType,
ThreatSeverity,AdditionalExtensions,
ThreatConfidence)on $left.Url == $right.RequestURL and $left.AccountUpn == $right.SourceUserName
Stage 9: where
where /* macro: (AlertTime between ((3plogTime - 1h) .. (3plogTime + 1h))) */
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AlertName | in |
| field:"AlertName" kind:in |
DeviceAction | ne |
| field:"DeviceAction" kind:ne value:"Block" |
DeviceProduct | starts_with |
| field:"DeviceProduct" kind:starts_with |
DeviceVendor | match |
| field:"DeviceVendor" kind:match |
Entity.Type | eq |
| field:"Entity.Type" kind:eq |
ProviderName | in |
| field:"ProviderName" kind:in |
RequestURL | is_not_null | field:"RequestURL" kind:is_not_null | |
SourceUserName | is_not_null | field:"SourceUserName" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountUpn | summarize |
AlertTime | summarize |
ProductName | summarize |
SystemAlertId | summarize |
Url | summarize |
Potential Kerberoasting
#A service principal name (SPN) is used to uniquely identify a service instance in a Windows environment. Each SPN is usually associated with a service account. Organizations may have used service accounts with weak passwords in their environment. An attacker can try requesting Kerberos ticket-granting service (TGS) service tickets for any SPN from a domain controller (DC) which contains a hash of the Service account. This can then be used for offline cracking. This hunting query looks for accounts that are generating excessive requests to different resources within the last hour compared with the previous 24 hours. Normal users would not make an unusually large number of request within a small time window. This is based on 4769 events which can be very noisy so environment based tweaking might be needed.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 4769: A Kerberos service ticket was requested. |
Rule Body
id: 1572e66b-20a7-4012-9ec4-77ec4b101bc8
name: Potential Kerberoasting
description: |
'A service principal name (SPN) is used to uniquely identify a service instance in a Windows environment.
Each SPN is usually associated with a service account. Organizations may have used service accounts with weak passwords in their environment.
An attacker can try requesting Kerberos ticket-granting service (TGS) service tickets for any SPN from a domain controller (DC) which contains a hash of the Service account. This can then be used for offline cracking.
This hunting query looks for accounts that are generating excessive requests to different resources within the last hour compared with the previous 24 hours. Normal users would not make an unusually large number of request within a small time window. This is based on 4769 events which can be very noisy so environment based tweaking might be needed.'
severity: Medium
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1558
query: |
let starttime = 1d;
let endtime = 1h;
let prev23hThreshold = 4;
let prev1hThreshold = 15;
let Kerbevent = (union isfuzzy=true
(SecurityEvent
| where TimeGenerated >= ago(starttime)
| where EventID == 4769
| parse EventData with * 'TicketEncryptionType">' TicketEncryptionType "<" *
| where TicketEncryptionType == '0x17'
| parse EventData with * 'TicketOptions">' TicketOptions "<" *
| where TicketOptions == '0x40810000'
| parse EventData with * 'Status">' Status "<" *
| where Status == '0x0'
| parse EventData with * 'ServiceName">' ServiceName "<" *
| where ServiceName !contains "$" and ServiceName !contains "krbtgt"
| parse EventData with * 'TargetUserName">' TargetUserName "<" *
| where TargetUserName !contains "$@" and TargetUserName !contains ServiceName
| parse EventData with * 'IpAddress">::ffff:' ClientIPAddress "<" *
),
(
WindowsEvent
| where TimeGenerated >= ago(starttime)
| where EventID == 4769 and EventData has '0x17' and EventData has '0x40810000' and EventData has 'krbtgt'
| extend TicketEncryptionType = tostring(EventData.TicketEncryptionType)
| where TicketEncryptionType == '0x17'
| extend TicketOptions = tostring(EventData.TicketOptions)
| where TicketOptions == '0x40810000'
| extend Status = tostring(EventData.Status)
| where Status == '0x0'
| extend ServiceName = tostring(EventData.ServiceName)
| where ServiceName !contains "$" and ServiceName !contains "krbtgt"
| extend TargetUserName = tostring(EventData.TargetUserName)
| where TargetUserName !contains "$@" and TargetUserName !contains ServiceName
| extend ClientIPAddress = tostring(EventData.IpAddress)
));
let Kerbevent23h = Kerbevent
| where TimeGenerated >= ago(starttime) and TimeGenerated < ago(endtime)
| summarize ServiceNameCountPrev23h = dcount(ServiceName), ServiceNameSet23h = makeset(ServiceName)
by Computer, TargetUserName,TargetDomainName, ClientIPAddress, TicketOptions, TicketEncryptionType, Status
| where ServiceNameCountPrev23h < prev23hThreshold;
let Kerbevent1h =
Kerbevent
| where TimeGenerated >= ago(endtime)
| summarize min(TimeGenerated), max(TimeGenerated), ServiceNameCountPrev1h = dcount(ServiceName), ServiceNameSet1h = makeset(ServiceName)
by Computer, TargetUserName, TargetDomainName, ClientIPAddress, TicketOptions, TicketEncryptionType, Status;
Kerbevent1h
| join kind=leftanti
(
Kerbevent23h
) on TargetUserName, TargetDomainName
// Threshold value set above is based on testing, this value may need to be changed for your environment.
| where ServiceNameCountPrev1h > prev1hThreshold
| project StartTime = min_TimeGenerated, EndTime = max_TimeGenerated, TargetUserName, Computer, ClientIPAddress, TicketOptions,
TicketEncryptionType, Status, ServiceNameCountPrev1h, ServiceNameSet1h, TargetDomainName
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| extend TargetAccount = strcat(TargetDomainName, "\\", TargetUserName)
| project-away DomainIndex
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetAccount
- identifier: Name
columnName: TargetUserName
- identifier: NTDomain
columnName: TargetDomainName
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: HostNameDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ClientIPAddress
version: 1.1.7
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Others", "Identity" ]
Stages and Predicates
Parameters
let starttime = 1d;
let endtime = 1h;
let prev23hThreshold = 4;
let prev1hThreshold = 15;
let Kerbevent and let Kerbevent1h are inlined into the numbered stages below.
Let Binding: Kerbevent23h
let Kerbevent23h = Kerbevent
| where TimeGenerated >= ago(starttime) and TimeGenerated < ago(endtime)
| summarize ServiceNameCountPrev23h = dcount(ServiceName), ServiceNameSet23h = makeset(ServiceName)
by Computer, TargetUserName,TargetDomainName, ClientIPAddress, TicketOptions, TicketEncryptionType, Status
| where ServiceNameCountPrev23h < prev23hThreshold;
Stage 1: source
let Kerbevent
Stage 2: source
let Kerbevent1h
Stage 3: union
union of 2 branches
Stage 4: source
SecurityEvent
Stage 5: where
where TimeGenerated >= ago(86400s)
Stage 6: where
where EventID == 4769
Stage 7: parse
parse
Stage 8: where
where TicketEncryptionType == 0x17
Stage 9: parse
parse
Stage 10: where
where TicketOptions == 0x40810000
Stage 11: parse
parse
Stage 12: where
where Status == 0x0
Stage 13: parse
parse
Stage 14: where
where not (ServiceName contains "$") and not (ServiceName contains "krbtgt")
Stage 15: parse
parse
Stage 16: where
where not (TargetUserName contains "$@") and TargetUserName !contains ServiceName
Stage 17: parse
parse
Stage 18: source
WindowsEvent
Stage 19: where
where TimeGenerated >= ago(86400s)
Stage 20: where
where EventData has 0x17 and EventData has 0x40810000 and EventData has "krbtgt" and EventID == 4769
Stage 21: extend
extend TicketEncryptionType
Stage 22: where
where TicketEncryptionType == 0x17
Stage 23: extend
extend TicketOptions
Stage 24: where
where TicketOptions == 0x40810000
Stage 25: extend
extend Status
Stage 26: where
where Status == 0x0
Stage 27: extend
extend ServiceName
Stage 28: where
where not (ServiceName contains "$") and not (ServiceName contains "krbtgt")
Stage 29: extend
extend TargetUserName
Stage 30: where
where not (TargetUserName contains "$@") and TargetUserName !contains ServiceName
Stage 31: extend
extend ClientIPAddress
Stage 32: where
where TimeGenerated >= ago(3600s)
Stage 33: summarize
summarize ServiceNameCountPrev1h, ServiceNameSet1h by Computer, TargetUserName, TargetDomainName, ClientIPAddress, TicketOptions, TicketEncryptionType, Status
Stage 34: join (negated)
join kind=leftanti (Kerbevent23h) on TargetUserName, TargetDomainName
Stage 35: where
where ServiceNameCountPrev1h > 15
Stage 36: project
project ClientIPAddress, Computer, EndTime, ServiceNameCountPrev1h, ServiceNameSet1h, StartTime, Status, TargetDomainName, TargetUserName, TicketEncryptionType, TicketOptions
Stage 37: extend (3 consecutive steps)
extend DomainIndex, HostName, HostNameDomain, TargetAccount
Stage 38: project-away
project-away DomainIndex
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventData | match |
| field:"EventData" kind:match |
EventID | eq |
| field:"EventID" kind:eq value:"4769" |
ServiceName | contains |
| field:"ServiceName" kind:contains |
ServiceNameCountPrev1h | gt |
| field:"ServiceNameCountPrev1h" kind:gt value:"15" |
Status | eq |
| field:"Status" kind:eq value:"0x0" |
TargetUserName | contains |
| field:"TargetUserName" kind:contains value:"$@" |
TicketEncryptionType | eq |
| field:"TicketEncryptionType" kind:eq value:"0x17" |
TicketOptions | eq |
| field:"TicketOptions" kind:eq value:"0x40810000" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
ServiceName | contains | $ | excludes:ServiceName field:"ServiceName" value:"$" |
ServiceName | contains | krbtgt | excludes:ServiceName field:"ServiceName" value:"krbtgt" |
TargetUserName | contains | $@ | excludes:TargetUserName field:"TargetUserName" value:"$@" |
EventData | match | 0x17 | excludes:EventData field:"EventData" value:"0x17" |
EventData | match | 0x40810000 | excludes:EventData field:"EventData" value:"0x40810000" |
EventData | match | krbtgt | excludes:EventData field:"EventData" value:"krbtgt" |
EventID | eq | 4769 | excludes:EventID field:"EventID" value:"4769" |
ServiceNameCountPrev23h | lt | 4 | excludes:ServiceNameCountPrev23h field:"ServiceNameCountPrev23h" value:"4" |
Status | eq | 0x0 | excludes:Status field:"Status" value:"0x0" |
TargetUserName | cross_field_compare | ServiceName | excludes:TargetUserName field:"TargetUserName" value:"ServiceName" |
TicketEncryptionType | eq | 0x17 | excludes:TicketEncryptionType field:"TicketEncryptionType" value:"0x17" |
TicketOptions | eq | 0x40810000 | excludes:TicketOptions field:"TicketOptions" value:"0x40810000" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ClientIPAddress | project |
Computer | project |
EndTime | project |
ServiceNameCountPrev1h | project |
ServiceNameSet1h | project |
StartTime | project |
Status | project |
TargetDomainName | project |
TargetUserName | project |
TicketEncryptionType | project |
TicketOptions | project |
HostName | extend |
HostNameDomain | extend |
TargetAccount | extend |
Successful logins to SOC Prime platform from bad IP addresses
#'This rule identifies successful logins from IP addresses previously flagged as malicious (e.g., botnets, TOR exit nodes, or known malicious IPs)'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: f8e7d6c5-b4a3-4122-8110-0987654321fe
name: Successful logins to SOC Prime platform from bad IP addresses
description: |
'This rule identifies successful logins from IP addresses previously flagged as malicious (e.g., botnets, TOR exit nodes, or known malicious IPs)'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SOCPrimeAuditLogsDataConnector
dataTypes:
- SOCPrimeAuditLogs_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |
let blacklistIPs = _GetWatchlist('blacklistOfIps')
| project IPAddress = column_ifexists('ip','IPAddress');
SOCPrimeAuditLogs_CL
| where EventName == "Logged in to the SOC Prime Platform"
| where SourceIp in (blacklistIPs)
| project TimeGenerated, EventName, UserEmail, UserName, SourceIp, Uri, Type
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: UserName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIp
version: 1.0.0
kind: Scheduled
Stages and Predicates
Let Binding: blacklistIPs
let blacklistIPs = _GetWatchlist('blacklistOfIps')
| project IPAddress = column_ifexists('ip','IPAddress');
Stage 1: source
SOCPrimeAuditLogs_CL
Stage 2: where
| where EventName == "Logged in to the SOC Prime Platform"
Stage 3: where
| where SourceIp in (blacklistIPs)
Stage 4: project
| project TimeGenerated, EventName, UserEmail, UserName, SourceIp, Uri, Type
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventName | eq |
| field:"aws::eventName" kind:eq value:"Logged in to the SOC Prime Platform" |
SourceIp | in |
| field:"src_ip" kind:in value:"blacklistIPs" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
EventName | project |
SourceIp | project |
TimeGenerated | project |
Type | project |
Uri | project |
UserEmail | project |
UserName | project |
Suspicious Sign In Followed by MFA Modification
#This query looks uses Microsoft Sentinel's UEBA features to look for suspicious logons followed by modifications to MFA settings by that user.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Persistence | |
| Stealth | |
| Defense Impairment | |
| Credential Access |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
- [Entra ID] Authentication Method Changed for Privileged Account (Kusto)
- Account Created and Deleted in Short Timeframe (Kusto)
- Account Created And Deleted Within A Close Time Frame (Sigma)
- Account created from non-approved sources (Kusto)
- Account created or deleted by non-approved user (Kusto)
- Addition of a Temporary Access Pass to a Privileged Account (Kusto)
- Authentication Method Changed for Privileged Account (Kusto)
- Authentication Methods Changed for Privileged Account (Kusto)
Rule Body
id: aec77100-25c5-4254-a20a-8027ed92c46c
name: Suspicious Sign In Followed by MFA Modification
description: |
'This query looks uses Microsoft Sentinel's UEBA features to look for suspicious logons followed by modifications to MFA settings by that user.'
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
- connectorId: BehaviorAnalytics
dataTypes:
- BehaviorAnalytics
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
status: Available
tactics:
- InitialAccess
- DefenseEvasion
relevantTechniques:
- T1078.004
- T1556.006
query: |
let PriorityScore = 9;
BehaviorAnalytics
| where ActionType == "Sign-in"
| where InvestigationPriority > PriorityScore
| extend UserPrincipalName = tolower(UserPrincipalName)
| extend LogOnTime = TimeGenerated
| join kind=inner (AuditLogs
| where Category =~ "UserManagement"
| where OperationName in~ ("Admin registered security info", "Admin updated security info", "Admin deleted security info", "User registered security info", "User changed default security info", "User deleted security info","User registered all required security info","User started security info registration")
| extend InitiatorUPN = tolower(tostring(InitiatedBy.user.userPrincipalName))
| extend InitiatorID = tostring(InitiatedBy.user.id)
| extend FromIP = tostring(InitiatedBy.user.ipAddress)
| extend TargetUPN = tolower(tostring(TargetResources[0].userPrincipalName))
| extend TargetId = tostring(TargetResources[0].id)
| extend MFAModTime = TimeGenerated
| where isnotempty(InitiatorUPN)) on $left.UserPrincipalName == $right.InitiatorUPN
| where MFAModTime between((LogOnTime-30m)..(LogOnTime+1h))
| extend InitiatorName = tostring(split(InitiatorUPN, "@")[0]), InitiatorUPNSuffix = tostring(split(InitiatorUPN, "@")[1]), TargetName = tostring(split(TargetUPN, "@")[0]), TargetUPNSuffix = tostring(split(TargetUPN, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: InitiatorUPN
- identifier: Name
columnName: InitiatorName
- identifier: UPNSuffix
columnName: InitiatorUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: InitiatorID
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetUPN
- identifier: Name
columnName: TargetName
- identifier: UPNSuffix
columnName: TargetUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: TargetId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: FromIP
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIPAddress
alertDetailsOverride:
alertDisplayNameFormat: Suspicious Sign In by {{InitiatorUPN}} Followed by MFA Modification to {{TargetUPN}}
alertDescriptionFormat: |
This query looks uses Microsoft Sentinel's UEBA features to look for suspicious logons followed by modifications to MFA settings by that user.
In this case {{InitiatorUPN}} logged in followed by a modification to MFA settings for {{TargetUPN}}.
The sign in was from {{SourceIPAddress}}.
version: 1.0.1
kind: Scheduled
Stages and Predicates
Parameters
let PriorityScore = 9;
Stage 1: source
BehaviorAnalytics
Stage 2: where
| where ActionType == "Sign-in"
Stage 3: where
| where InvestigationPriority > PriorityScore
Stage 4: extend
| extend UserPrincipalName = tolower(UserPrincipalName)
Stage 5: extend
| extend LogOnTime = TimeGenerated
Stage 6: join
| join kind=inner (AuditLogs
| where Category =~ "UserManagement"
| where OperationName in~ ("Admin registered security info", "Admin updated security info", "Admin deleted security info", "User registered security info", "User changed default security info", "User deleted security info","User registered all required security info","User started security info registration")
| extend InitiatorUPN = tolower(tostring(InitiatedBy.user.userPrincipalName))
| extend InitiatorID = tostring(InitiatedBy.user.id)
| extend FromIP = tostring(InitiatedBy.user.ipAddress)
| extend TargetUPN = tolower(tostring(TargetResources[0].userPrincipalName))
| extend TargetId = tostring(TargetResources[0].id)
| extend MFAModTime = TimeGenerated
| where isnotempty(InitiatorUPN)) on $left.UserPrincipalName == $right.InitiatorUPN
Stage 7: where
where /* macro: (MFAModTime between ((LogOnTime - 30m) .. (LogOnTime + 1h))) */
Stage 8: extend
extend InitiatorName, InitiatorUPNSuffix, TargetName, TargetUPNSuffix
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | eq |
| field:"ActionType" kind:eq value:"Sign-in" |
Category | eq |
| field:"Category" kind:eq value:"UserManagement" |
InitiatorUPN | is_not_null | field:"InitiatorUPN" kind:is_not_null | |
InvestigationPriority | gt |
| field:"InvestigationPriority" kind:gt value:"9" |
OperationName | in |
| field:"OperationName" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
UserPrincipalName | extend |
LogOnTime | extend |
InitiatorName | extend |
InitiatorUPNSuffix | extend |
TargetName | extend |
TargetUPNSuffix | extend |