Detection rules › Kusto

Kusto rules: 09

[Entra ID] Authentication Method Changed for Privileged Account

#
Status
available
Severity
high
Time window
1h
Group by
IP, InitiatingAppServicePrincipalId, InitiatingAppServicePrincipalName, Initiator, Result, Targets
Source
github.com/Azure/Azure-Sentinel

Detects changes to authentication methods on privileged accounts. This may indicate an attacker added a new method to maintain access.

MITRE ATT&CK Coverage

TacticTechniques
Persistence

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 9f7197b6-eeb2-46f3-83b1-a2c4dfca46a0
name: "[Entra ID] Authentication Method Changed for Privileged Account"
version: 1.0.0
kind: Scheduled
description: |
  Detects changes to authentication methods on privileged accounts. This may indicate an attacker added a new method to maintain access.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
relevantTechniques:
  - T1098
query: |
  let queryperiod = 1d;
  let queryfrequency = 1d;
  let security_info_actions = dynamic(["User registered security info", "User changed default security info", "User deleted security info", "Admin updated security info", "Admin deleted security info", "Admin registered security info"]);
  let VIPUsers = (
      IdentityInfo
      | where TimeGenerated > ago(queryperiod)
      | mv-expand AssignedRoles
      | where AssignedRoles contains 'Admin'
      | summarize by AccountUPN);
  AuditLogs
  | where TimeGenerated > ago(queryperiod)
  | where Category =~ "UserManagement"
  | where ActivityDisplayName in (security_info_actions)
  | extend Initiator = tostring(InitiatedBy.user.userPrincipalName)
  | extend IP = tostring(InitiatedBy.user.ipAddress)
  | extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
  | extend InitiatingAppServicePrincipalName = tostring(InitiatedBy.app.servicePrincipalName)
  | mv-apply TargetResource = TargetResources on
      (
      where TargetResource.type =~ "User"
      | extend Target = tostring(TargetResource.userPrincipalName)
      )
  | where Target in~ (VIPUsers)
  // Uncomment the line below if you are experiencing high volumes of Target entities. If this is uncommented, the Target column will not be mapped to an entity.
  //| summarize Start=min(TimeGenerated), End=max(TimeGenerated), Actions = make_set(ResultReason, MaxSize=8), Targets=make_set(Target, MaxSize=256) by Initiator, IP, Result
  // Comment out this line below, if line above is used.
  | summarize
      Start=min(TimeGenerated),
      End=max(TimeGenerated),
      Actions = make_set(ResultReason, MaxSize=8),
      Target_ResourceName = make_set(TargetResources)
      by
      Initiator,
      IP,
      Result,
      Targets = Target,
      InitiatingAppServicePrincipalId,
      InitiatingAppServicePrincipalName,
      bin(TimeGenerated, 1h)
  | mv-expand Actions
  | extend
      Action = tostring(Actions),
      InitiatorName = tostring(split(Initiator, '@', 0)[0]),
      InitiatorUPNSuffix = tostring(split(Initiator, '@', 1)[0]),
      //TargetName = iff(tostring(Targets) has "[", "", tostring(split(Targets, '@', 0)[0])),
      TargetUPNSuffix = iff(tostring(Targets) has "[", "", tostring(split(Targets, '@', 1)[0]))
  | extend Source_Network_IPLocation = ""
  | project
          Alert_Category_en = "Entra ID",
      Alert_SubCategory_en = "Anomaly Identity Privilege Modification",
      Alert_Name_en = "Authentication Method Changed for Privileged Account",
      Alert_Description_en =  strcat(
                              "At Taiwan time: ",
                              format_datetime(datetime_utc_to_local(TimeGenerated, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                              "in the Microsoft Entra ID tenant"
      ", user:  ",
                              iff(isnotempty(Initiator), Initiator, ""),
                              " at IP: ",
                              IP,
                              ", performed an authentication method change"
                          ),
      Alert_TriageStep_en = strcat(
                            "1. Check whether there are successful records for the authentication method change."
      "2. Confirm with user: ",
                            iff(isnotempty(Initiator), Initiator, ""),
                            " whether the change was performed."
                        ),
      Alert_Containment_en = strcat(
                             "1. Immediately revoke all existing sign-in tokens and sessions (sign-in session / refresh token) for the account, and force password and MFA reset.  ",
                             "2. Immediately disable the account."
                         ),
      Alert_Remediation_en = strcat(
                             "1. Strengthen security info governance for Admin/VIP accounts and require additional verification or manual review for security info changes.",
                             "2. Enforce advanced Conditional Access policies for high-privilege accounts, including MFA/compliant devices/risk-based sign-in policies, and block change activity from unknown locations or new devices.  ",
                             "3. Establish and optimize real-time alerts and automated response SOAR for security info changes, such as automatically blocking accounts/notifying SOC/requesting user confirmation.  "
                         ),
              Alert_Time_TW = datetime_utc_to_local(TimeGenerated, "Asia/Taipei"),
      Alert_Time_UTC0 = TimeGenerated,
      Event_Action = Actions,
      Event_Status = Result,
      //Event_Description = ActivityDisplayName,
      Source_Identity_FullName = Initiator,
      //Source_Identity_ID = ActorID,
      Source_Identity_Type = iff(isnotempty(Initiator), "User", "Service"),
      Source_Network_IPAddress = IP,
      Source_Network_IPLocation = Source_Network_IPLocation,
      Source_Resource_Name = InitiatingAppServicePrincipalName,
      Target_Identity_FullName = Targets,
      Target_Identity_Type = "User",
      Target_Resource_Type = "Entra ID"
  //CreateTime:2025/1/5
  //EditTime:
  //Editor:Lancy
  //Version v1.0
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Source_Identity_FullName
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Target_Identity_FullName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: Source_Network_IPAddress

Stages and Predicates

Parameters

let queryperiod = 1d;
let queryfrequency = 1d;

Let Binding: security_info_actions

let security_info_actions = dynamic(["User registered security info", "User changed default security info", "User deleted security info", "Admin updated security info", "Admin deleted security info", "Admin registered security info"]);

Let Binding: VIPUsers used in Stages 1, 8

let VIPUsers = (
    IdentityInfo
    | where TimeGenerated > ago(queryperiod)
    | mv-expand AssignedRoles
    | where AssignedRoles contains 'Admin'
    | summarize by AccountUPN);

Stage 1: source

let VIPUsers

Stage 2: source

AuditLogs

Stage 3: where

where TimeGenerated > ago(86400s)

Stage 4: where

where Category =~ "UserManagement"

Stage 5: where

where ActivityDisplayName in ("Admin deleted security info", "Admin registered security info", "Admin updated security info", "User changed default security info", "User deleted security info", "User registered security info")

Stage 6: extend (4 consecutive steps)

extend IP, InitiatingAppServicePrincipalId, InitiatingAppServicePrincipalName, Initiator

Stage 7: kusto:mv-apply

kusto:mv-apply TargetResource.type =~ "User"

Stage 8: where

where Target =~ "VIPUsers"

Stage 9: summarize

summarize Actions, End, Start, Target_ResourceName, Targets by Initiator, IP, Result, Targets, InitiatingAppServicePrincipalId, InitiatingAppServicePrincipalName

Stage 10: mv-expand

mv-expand Actions

Stage 11: extend

extend Action, InitiatorName, InitiatorUPNSuffix, TargetUPNSuffix
TargetUPNSuffix =
ifTargets has "["""
elsetostring(split(Targets, '@', 1)[0])

Stage 12: extend

extend Source_Network_IPLocation

Stage 13: project

project Alert_Category_en, Alert_Containment_en, Alert_Description_en, Alert_Name_en, Alert_Remediation_en, Alert_SubCategory_en, Alert_Time_TW, Alert_Time_UTC0, Alert_TriageStep_en, Event_Action, Event_Status, Source_Identity_FullName, Source_Identity_Type, Source_Network_IPAddress, Source_Network_IPLocation, Source_Resource_Name, Target_Identity_FullName, Target_Identity_Type, Target_Resource_Type

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActivityDisplayNamein
  • Admin deleted security info
  • Admin registered security info
  • Admin updated security info
  • User changed default security info
  • User deleted security info
  • User registered security info
field:"ActivityDisplayName" kind:in
Categoryeq
  • UserManagement
field:"Category" kind:eq value:"UserManagement"
Targetin
  • VIPUsers
field:"Target" kind:in value:"VIPUsers"
TargetResource.typeeq
  • User transforms: kusto_dynamic_path
field:"TargetResource.type" kind:eq value:"User"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Alert_Category_enproject
Alert_Containment_enproject
Alert_Description_enproject
Alert_Name_enproject
Alert_Remediation_enproject
Alert_SubCategory_enproject
Alert_Time_TWproject
Alert_Time_UTC0project
Alert_TriageStep_enproject
Event_Actionproject
Event_Statusproject
Source_Identity_FullNameproject
Source_Identity_Typeproject
Source_Network_IPAddressproject
Source_Network_IPLocationproject
Source_Resource_Nameproject
Target_Identity_FullNameproject
Target_Identity_Typeproject
Target_Resource_Typeproject

Cisco SE - Connection to known C2 server

#
Status
available
Severity
high
Time window
15m
Source
github.com/Azure/Azure-Sentinel

'This rule is triggered when connection to known C2 is detected from host.'

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: 0f788a93-dc88-4f80-89ef-bef7cd0fef05
name: Cisco SE - Connection to known C2 server
description: |
  'This rule is triggered when connection to known C2 is detected from host.'
severity: High
requiredDataConnectors:
  - connectorId: CiscoSecureEndpoint
    dataTypes:
      - CiscoSecureEndpoint
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  CiscoSecureEndpoint
  | where EventMessage has 'Suspected botnet connection'
  | extend HostCustomEntity = DstHostname, MalwareCustomEntity = ThreatName
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostCustomEntity
  - entityType: Malware
    fieldMappings:
      - identifier: Name
        columnName: MalwareCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

CiscoSecureEndpoint

Stage 2: where

| where EventMessage has 'Suspected botnet connection'

Stage 3: extend

| extend HostCustomEntity = DstHostname, MalwareCustomEntity = ThreatName

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventMessagematch
  • Suspected botnet connection transforms: term
field:"EventMessage" kind:match value:"Suspected botnet connection"

Output Fields

These fields are emitted when the rule matches.

FieldSource
HostCustomEntityextend
MalwareCustomEntityextend

Digital Shadows Incident Creation for exclude-app

#
Severity
medium
Time window
6m
Group by
EventOriginalUid
Source
github.com/Azure/Azure-Sentinel

Digital Shadows Analytic rule for generating Microsoft Sentinel incidents for the data ingested by app polling for excluded classifications

Rule Body

id: f7abe9c1-1e6c-4317-b907-25769e7764c5
name: Digital Shadows Incident Creation for exclude-app
description: 'Digital Shadows Analytic rule for generating Microsoft Sentinel incidents for the data ingested by app polling for excluded classifications'
severity: Medium
requiredDataConnectors:
  - connectorId: DigitalShadows
    dataTypes:
      - DigitalShadows_V2_CL
queryFrequency: 5m
queryPeriod: 6m
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query:
  let DSSearchLight_view  = view () {
  DigitalShadows_V2_CL
  | project-rename app_s = App, title_s = Title, raised_t = TimeRaised, updated_t = TimeUpdated, classification_s = Classification, risk_level_s = RiskLevel, risk_assessment_risk_level_s = RiskAssessmentRiskLevel, gm_link_s = GreyMatterLink, assets_s = Assets, comments_s = Comments, description_s = Description, impact_description_s = ImpactDescription, mitigation_s = Mitigation, risk_factors_s = RiskFactors, portal_id_s = PortalId, id_d = IncidentId, id_g = AlertId, status_s = Status, triage_id_g = TriageId, triage_raised_time_t = TriageRaisedTime, triage_updated_time_t = TriageUpdatedTime
  | where app_s == "exclude"
  | extend EventVendor="Digital Shadows", EventProduct="SearchLight",Type="DigitalShadows_V2_CL",EventStartTime=raised_t,EventMessage=title_s,EventOriginalUid=triage_id_g,EventOriginalType=classification_s
  | extend EventOriginalSeverity=iif(isempty(risk_level_s), risk_assessment_risk_level_s, risk_level_s) 
  | extend EventSeverity = case(EventOriginalSeverity == 'none', 'Informational', EventOriginalSeverity == 'very-low', 'Low',EventOriginalSeverity == 'low', 'Low',EventOriginalSeverity == 'medium', 'Medium',EventOriginalSeverity == 'high', 'High',EventOriginalSeverity == 'very-high', 'High','Informational') 
  | extend EventReportUrl = column_ifexists('gm_link_s', '')
  | extend AdditionalFields = pack("assets", assets_s, "comments", comments_s, "description", description_s, "incident_id", id_d, "alert_id", id_g, "short_code", portal_id_s, "impact", impact_description_s, "mitigation", mitigation_s, "risk_factors", risk_factors_s, "triage_status", status_s, "triage_id", triage_id_g, "triage_raised", triage_raised_time_t,"triage_updated", triage_updated_time_t, "updated", updated_t) 
  | project TimeGenerated, EventVendor,EventProduct, Type, EventStartTime,EventMessage, EventOriginalUid, EventOriginalType,EventOriginalSeverity, EventSeverity, EventReportUrl,AdditionalFields};DSSearchLight_view
  | summarize arg_max(TimeGenerated, *) by EventOriginalUid 
  | extend description = AdditionalFields.description
  | extend impact = AdditionalFields.impact
  | extend mitigation = AdditionalFields.mitigation
  | extend status = AdditionalFields.triage_status
  | extend comments = AdditionalFields.comments
entityMappings: 
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: EventReportUrl
enabled: true
suppressionDuration: 5h
suppressionEnabled: false
techniques: []
alertRuleTemplateName:
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: true
    lookbackDuration: 7d
    matchingMethod: Selected
    groupByCustomDetails:
    - triage_id
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: Digital Shadows - {{EventMessage}}
  alertDescriptionFormat: |-
    {{description}}

    {{impact}}

    {{mitigation}}
  alertSeverityColumnName: EventSeverity
customDetails:
  triage_id: EventOriginalUid
  severity: EventOriginalSeverity
  description: description
  status: status
  impact: impact
  mitigation: mitigation
version: 1.1.0
kind: Scheduled

Stages and Predicates

Let Binding: DSSearchLight_view

let DSSearchLight_view = view () { DigitalShadows_V2_CL | project-rename app_s = App, title_s = Title, raised_t = TimeRaised, updated_t = TimeUpdated, classification_s = Classification, risk_level_s = RiskLevel, risk_assessment_risk_level_s = RiskAssessmentRiskLevel, gm_link_s = GreyMatterLink, assets_s = Assets, comments_s = Comments, description_s = Description, impact_description_s = ImpactDescription, mitigation_s = Mitigation, risk_factors_s = RiskFactors, portal_id_s = PortalId, id_d = IncidentId, id_g = AlertId, status_s = Status, triage_id_g = TriageId, triage_raised_time_t = TriageRaisedTime, triage_updated_time_t = TriageUpdatedTime | where app_s == "exclude" | extend EventVendor="Digital Shadows", EventProduct="SearchLight",Type="DigitalShadows_V2_CL",EventStartTime=raised_t,EventMessage=title_s,EventOriginalUid=triage_id_g,EventOriginalType=classification_s | extend EventOriginalSeverity=iif(isempty(risk_level_s), risk_assessment_risk_level_s, risk_level_s) | extend EventSeverity = case(EventOriginalSeverity == 'none', 'Informational', EventOriginalSeverity == 'very-low', 'Low',EventOriginalSeverity == 'low', 'Low',EventOriginalSeverity == 'medium', 'Medium',EventOriginalSeverity == 'high', 'High',EventOriginalSeverity == 'very-high', 'High','Informational') | extend EventReportUrl = column_ifexists('gm_link_s', '') | extend AdditionalFields = pack("assets", assets_s, "comments", comments_s, "description", description_s, "incident_id", id_d, "alert_id", id_g, "short_code", portal_id_s, "impact", impact_description_s, "mitigation", mitigation_s, "risk_factors", risk_factors_s, "triage_status", status_s, "triage_id", triage_id_g, "triage_raised", triage_raised_time_t,"triage_updated", triage_updated_time_t, "updated", updated_t) | project TimeGenerated, EventVendor,EventProduct, Type, EventStartTime,EventMessage, EventOriginalUid, EventOriginalType,EventOriginalSeverity, EventSeverity, EventReportUrl,AdditionalFields};

Stage 1: source

DigitalShadows_V2_CL

Stage 2: project-rename

project-rename

Stage 3: where

where app_s == "exclude"

Stage 4: extend (5 consecutive steps)

extend AdditionalFields, EventMessage, EventOriginalSeverity, EventOriginalType, EventOriginalUid, EventProduct, EventReportUrl, EventSeverity, EventStartTime, EventVendor, Type

Stage 5: project

project AdditionalFields, EventMessage, EventOriginalSeverity, EventOriginalType, EventOriginalUid, EventProduct, EventReportUrl, EventSeverity, EventStartTime, EventVendor, TimeGenerated, Type

Stage 6: summarize

summarize by EventOriginalUid

Stage 7: extend (5 consecutive steps)

extend comments, description, impact, mitigation, status

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
app_seq
  • exclude
field:"App" kind:eq value:"exclude"

Output Fields

These fields are emitted when the rule matches.

FieldSource
EventOriginalUidsummarize
descriptionextend
impactextend
mitigationextend
statusextend
commentsextend

Google Threat Intelligence - Threat Hunting IP

#
Severity
medium
Time window
1d
Group by
$left.DstIpAddr, $right.ObservableValue, Id
Source
github.com/Azure/Azure-Sentinel

Google Threat Intelligence IP correlation.

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Telemetry Coverage

Rule Body

id: "7edb2abb-7ef7-4685-92eb-a628703ccf9f"
name: Google Threat Intelligence - Threat Hunting IP
description: |
  'Google Threat Intelligence IP correlation.'
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
queryFrequency: 30m
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: 'Google Threat Intelligence Match'
  alertDescriptionFormat: 'Correlation found from the {{Type}} table.'
query: |
  let ioc_lookBack = 1d;
  _Im_NetworkSession
  | where isnotempty(DstIpAddr)
  | join kind=inner (
  ThreatIntelIndicators
  | where ObservableKey == 'ipv4-addr:value'
  | where isnotempty(ObservableValue)
  | where SourceSystem == "Google Threat Intelligence"
  | where TimeGenerated >= ago(ioc_lookBack)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
  | where IsActive == true and (isnull(ValidUntil) or ValidUntil > now())
  ) on $left.DstIpAddr == $right.ObservableValue
  | project NetworkIP=ObservableValue, Description=Data.description, Type, TimeGenerated
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: NetworkIP
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let ioc_lookBack = 1d;

Stage 1: source

_Im_NetworkSession

Stage 2: where

| where isnotempty(DstIpAddr)

Stage 3: join

| join kind=inner (
ThreatIntelIndicators
| where ObservableKey == 'ipv4-addr:value'
| where isnotempty(ObservableValue)
| where SourceSystem == "Google Threat Intelligence"
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
| where IsActive == true and (isnull(ValidUntil) or ValidUntil > now())
) on $left.DstIpAddr == $right.ObservableValue

Stage 4: project

project Description, NetworkIP, TimeGenerated, Type

Stage 5: summarize aggregation inside the join branch

summarize by Id

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Idsummarize

Google Threat Intelligence - Threat Hunting Url

#
Severity
medium
Time window
1d
Group by
Id, lowerUrl
Source
github.com/Azure/Azure-Sentinel

'Google Threat Intelligence Url correlation.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: "89290690-54c4-4196-91c5-d32b1df5d873"
name: Google Threat Intelligence - Threat Hunting Url
description: |
  'Google Threat Intelligence Url correlation.'
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
queryFrequency: 30m
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1566
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: 'Google Threat Intelligence Match'
  alertDescriptionFormat: 'Correlation found from the {{Type}} table.'
query: |
  let ioc_lookBack = 1d;
  _Im_WebSession
  | where isnotempty(Url)
  | extend lowerUrl=tolower(Url)
  | join kind=inner (
  ThreatIntelIndicators
  | where ObservableKey == 'url:value'
  | where isnotempty(ObservableValue)
  | where SourceSystem == "Google Threat Intelligence"
  | where TimeGenerated >= ago(ioc_lookBack)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
  | where IsActive == true and (isnull(ValidUntil) or ValidUntil > now())
  | extend lowerUrl=tolower(ObservableValue)
  ) on lowerUrl
  | project Url=ObservableValue, Description=Data.description, Type, TimeGenerated
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let ioc_lookBack = 1d;

Stage 1: source

_Im_WebSession

Stage 2: where

| where isnotempty(Url)

Stage 3: extend

| extend lowerUrl=tolower(Url)

Stage 4: join

| join kind=inner (
ThreatIntelIndicators
| where ObservableKey == 'url:value'
| where isnotempty(ObservableValue)
| where SourceSystem == "Google Threat Intelligence"
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
| where IsActive == true and (isnull(ValidUntil) or ValidUntil > now())
| extend lowerUrl=tolower(ObservableValue)
) on lowerUrl

Stage 5: project

project Description, TimeGenerated, Type, Url

Stage 6: summarize aggregation inside the join branch

summarize by Id

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Idsummarize

GSA - TI Domain Entity

#
Status
available
Severity
medium
Time window
14d
Group by
$left.DomainName, $right.Domain, DestinationFqdn, Id, ObservableValue
Source
github.com/Azure/Azure-Sentinel

This query identifies Domain indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in GSA NetworkAccessTraffic.

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: 4ef07345-5d89-4f5b-9c64-a180d81a6176
name: GSA - TI Domain Entity
description: |
  This query identifies Domain indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in GSA NetworkAccessTraffic.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - NetworkAccessTrafficLogs
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  let dt_lookBack = 1h;
  let ioc_lookBack = 14d;
  let NetworkAccessTraff_ = NetworkAccessTraffic
  | where TimeGenerated >= ago(dt_lookBack)
  | where TrafficType == "internet"
  | where isnotempty(DestinationFqdn)
  | extend Domain = extract(@'([^.]+\.[^.]+)$', 1, DestinationFqdn)
  | project-rename GSANetworkAccessTraffic_TimeGenerated = TimeGenerated;
  let NetworkAccessTraffiDomains =
      NetworkAccessTraff_
      | distinct Domain;
  ThreatIntelIndicators
    | extend IndicatorType = replace(@'\[|\]|"""', "", tostring(split(ObservableKey, ":", 0)))
    | where IndicatorType == "domain-name"
    | extend DomainName = tolower(ObservableValue)
    | where TimeGenerated >= ago(ioc_lookBack)
    | extend IndicatorId = tostring(split(Id, "--")[2])
    | where DomainName in (NetworkAccessTraffiDomains)
    | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
    | where IsActive and (ValidUntil > now() or isempty(ValidUntil))
    | extend Description = tostring(parse_json(Data).description)
    | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
    | where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
    | project-reorder *, IsActive, Tags, TrafficLightProtocolLevel, DomainName, Type
    | join kind=innerunique (NetworkAccessTraff_) on $left.DomainName == $right.Domain
    | extend GSAThreatType = ThreatType
    | summarize GSANetworkAccessTraffic_TimeGenerated = arg_max(GSANetworkAccessTraffic_TimeGenerated, *) by Id, DestinationFqdn
    | project-reorder *,
      Tags,
      TrafficLightProtocolLevel,
      Type,
      DomainName,
      UserPrincipalName,
      UserId,
      DeviceId,
      InitiatingProcessName,
      GSAThreatType,
      DestinationUrl,
      DestinationPort,
      SourceIp,
      SourcePort,
      HttpUserAgent
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: UserPrincipalName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIp
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: DestinationFqdn
  - entityType: Process
    fieldMappings:
      - identifier: CommandLine
        columnName: InitiatingProcessName
kind: Scheduled

Stages and Predicates

Parameters

let dt_lookBack = 1h;
let ioc_lookBack = 14d;

Let Binding: NetworkAccessTraff_ used in Stage 14

let NetworkAccessTraff_ = NetworkAccessTraffic
| where TimeGenerated >= ago(dt_lookBack)
| where TrafficType == "internet"
| where isnotempty(DestinationFqdn)
| extend Domain = extract(@'([^.]+\.[^.]+)$', 1, DestinationFqdn)
| project-rename GSANetworkAccessTraffic_TimeGenerated = TimeGenerated;

Let Binding: NetworkAccessTraffiDomains used in Stage 7

let NetworkAccessTraffiDomains = NetworkAccessTraff_
    | distinct Domain;

Stage 1: source

ThreatIntelIndicators

Stage 2: extend

| extend IndicatorType = replace(@'\[|\]|"""', "", tostring(split(ObservableKey, ":", 0)))

Stage 3: where

| where IndicatorType == "domain-name"

Stage 4: extend

| extend DomainName = tolower(ObservableValue)

Stage 5: where

| where TimeGenerated >= ago(ioc_lookBack)

Stage 6: extend

| extend IndicatorId = tostring(split(Id, "--")[2])

Stage 7: where

| where DomainName in (NetworkAccessTraffiDomains)

Stage 8: summarize

| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue

Stage 9: where

| where IsActive and (ValidUntil > now() or isempty(ValidUntil))

Stage 10: extend

| extend Description = tostring(parse_json(Data).description)

Stage 11: extend

| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)

Stage 12: where

| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"

Stage 13: project-reorder

| project-reorder *, IsActive, Tags, TrafficLightProtocolLevel, DomainName, Type

Stage 14: join

| join kind=innerunique (NetworkAccessTraff_) on $left.DomainName == $right.Domain

Stage 15: extend

extend GSAThreatType

Stage 16: summarize

summarize GSANetworkAccessTraffic_TimeGenerated by Id, DestinationFqdn

Stage 17: project-reorder

project-reorder

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
DescriptioncontainsState: falsepos;excludes:Description field:"Description" value:"State: falsepos;"
DescriptioncontainsState: inactive;excludes:Description field:"Description" value:"State: inactive;"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DestinationFqdnsummarize
GSANetworkAccessTraffic_TimeGeneratedsummarize
Idsummarize

IaaS policy not attached to any identity

#

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.

Status
available
Severity
informational
Time window
30m
Source
github.com/Azure/Azure-Sentinel

The rule detects AWS policies that are not attached to any identities, meaning they can be deleted.

MITRE ATT&CK Coverage

TacticTechniques
Persistence
Privilege Escalation

Rule Body

id: 57bae0c4-50b7-4552-9de9-19dfecddbace
name: IaaS policy not attached to any identity
kind: Scheduled
description: The rule detects AWS policies that are not attached to any identities, meaning they can be deleted.
severity: Informational
status: Available
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - PrivilegeEscalation
  - Persistence
relevantTechniques:
  - T1098
query: |-
  Authomize_v2_CL
  | where ingestion_time() >= ago(30m)
  | extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s
  | where Policy has "IaaS policy not attached to any identity"
  | project  EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: URL
requiredDataConnectors:
- connectorId: Authomize
  dataTypes: [ "Authomize_v2_CL" ]
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AnyAlert
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
eventGroupingSettings:
  aggregationKind: SingleAlert
alertDetailsOverride:
  alertnameFormat: Alert from Authomize - IaaS policy not attached to any identity
  alertDescriptionFormat: IaaS policy not attached to any identity. The rule detects 'IaaS policies' attached to a role that has not used them during the past X days. It is recommended to remove unused policies from identities to reduce risk.
  alertSeverity: Severity
  alertTactics: Tactics  
  alertDynamicProperties:
  - alertProperty: AlertLink
    value: URL
customDetails:
  AuthomizeEventID: EventID
  EventName: Policy
  EventDescription: Description
  EventRecommendation: Recommendation
  ReferencedURL: URL
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.3

Stages and Predicates

Stage 1: source

Authomize_v2_CL

Stage 2: where

| where ingestion_time() >= ago(30m)

Stage 3: extend

| extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s

Stage 4: where

| where Policy has "IaaS policy not attached to any identity"

Stage 5: project

| project  EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Policymatch
  • IaaS policy not attached to any identity transforms: term
field:"policy_name_s" kind:match value:"IaaS policy not attached to any identity"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Categoryproject
Descriptionproject
EventIDproject
Policyproject
Recommendationproject
Severityproject
Tacticsproject
URLproject

License Support Expiring

#
Status
available
Severity
low
Time window
3h
Group by
$left.Edition, $left.Type, $right.Edition, $right.Type
Source
github.com/Azure/Azure-Sentinel

Detects when the Veeam support contract expires shortly.

Rule Body

id: 3caa6e6d-a3e2-4776-be97-15d20e2675ed
name: License Support Expiring
description: Detects when the Veeam support contract expires shortly.
severity: Low
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 3h
queryPeriod: 3h
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "let license_editions_lookup = union isfuzzy=true (datatable(Edition:string,\
  \ EditionDescription:string)[]), (_GetWatchlist(\"license_editions_lookup\")); \n\
  let license_types_lookup = union isfuzzy=true (datatable(Type:string, TypeDescription:string)[]),\
  \ (_GetWatchlist(\"license_types_lookup\")); \nVeeam_GetSecurityEvents\n| where\
  \ instanceId == 24040\n| extend Edition = extract(\"Edition=\\\"([^\\\"]*)\\\"\"\
  , 1, SyslogMessage)\n| lookup kind=leftouter (license_editions_lookup) \n    on\
  \ $left.Edition == $right.Edition\n| extend Type = extract(\"Type=\\\"([^\\\"]*)\\\
  \"\", 1, SyslogMessage)\n| lookup kind=leftouter (license_types_lookup) \n    on\
  \ $left.Type == $right.Type\n| extend TenantID = extract(\"TenantID=\\\"([^\\\"\
  ]*)\\\"\", 1, SyslogMessage)\n| extend DaysLeft = extract(\"DaysLeft=\\\"([^\\\"\
  ]*)\\\"\", 1, SyslogMessage)\n| extend SupportLeft = extract(\"SupportLeft=\\\"\
  ([^\\\"]*)\\\"\", 1, SyslogMessage)\n| project\n    Date = format_datetime(TimeGenerated,\
  \ 'dd.MM.yyyy HH:mm'),\n    DataSource = original_host,\n    EventId = instanceId,\n\
  \    [\"License Edition\"] = EditionDescription,\n    [\"License Type\"] = TypeDescription,\n\
  \    [\"Days Left\"] = DaysLeft,\n    [\"Days of Support Left\"] = SupportLeft,\n\
  \    MessageDetails = Description,\n    Severity = SeverityDescription\n"
version: 1.0.1
kind: Scheduled
customDetails:
  Date: Date
  VbrHostName: DataSource
  EventId: EventId
  MessageDetails: MessageDetails
  Severity: Severity

Stages and Predicates

Parameters

let license_editions_lookup = union isfuzzy=true (datatable(Edition:string, EditionDescription:string)[]), (_GetWatchlist("license_editions_lookup"));
let license_types_lookup = union isfuzzy=true (datatable(Type:string, TypeDescription:string)[]), (_GetWatchlist("license_types_lookup"));

Stage 1: source

Veeam_GetSecurityEvents

Stage 2: where

| where instanceId == 24040

Stage 3: extend

| extend Edition = extract("Edition=\"([^\"]*)\"", 1, SyslogMessage)

Stage 4: kusto:lookup

lookup kind=leftouter (license_editions_lookup) on $left.Edition, $right.Edition

Stage 5: extend

extend Type

Stage 6: kusto:lookup

lookup kind=leftouter (license_types_lookup) on $left.Type, $right.Type

Stage 7: extend (3 consecutive steps)

extend DaysLeft, SupportLeft, TenantID

Stage 8: project

project DataSource, Date, Days Left, Days of Support Left, EventId, License Edition, License Type, MessageDetails, Severity

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
Days Leftproject
Days of Support Leftproject
EventIdproject
License Editionproject
License Typeproject
MessageDetailsproject
Severityproject

New service account gained access to IaaS resource

#

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.

Status
available
Severity
informational
Time window
30m
Source
github.com/Azure/Azure-Sentinel

This policy detects when an application or service account gained new access to your assets. This policy is defined by Authomize and can be edited to change the configuration.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 6c17f270-cd56-48cc-9196-1728ffea6538
name: New service account gained access to IaaS resource
kind: Scheduled
description: This policy detects when an application or service account gained new access to your assets. This policy is defined by Authomize and can be edited to change the configuration.
severity: Informational
status: Available
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |-
  Authomize_v2_CL
  | where ingestion_time() >= ago(30m)
  | extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s
  | where Policy has "New service account gained access to IaaS resource"
  | project  EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: URL
requiredDataConnectors:
- connectorId: Authomize
  dataTypes: [ "Authomize_v2_CL" ]
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AnyAlert
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
eventGroupingSettings:
  aggregationKind: SingleAlert
alertDetailsOverride:
  alertnameFormat: Alert from Authomize - New service account gained access to IaaS resource
  alertDescriptionFormat: New service account gained access to IaaS resource. This policy detects when an application or service account gained new access to your assets. This policy is defined by Authomize and can be edited to change the configuration.
  alertSeverity: Severity
  alertTactics: Tactics
  alertDynamicProperties:
  - alertProperty: AlertLink
    value: URL
customDetails:
  AuthomizeEventID: EventID
  EventName: Policy
  EventDescription: Description
  EventRecommendation: Recommendation
  ReferencedURL: URL
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.3

Stages and Predicates

Stage 1: source

Authomize_v2_CL

Stage 2: where

| where ingestion_time() >= ago(30m)

Stage 3: extend

| extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s

Stage 4: where

| where Policy has "New service account gained access to IaaS resource"

Stage 5: project

| project  EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Policymatch
  • New service account gained access to IaaS resource transforms: term
field:"policy_name_s" kind:match value:"New service account gained access to IaaS resource"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Categoryproject
Descriptionproject
EventIDproject
Policyproject
Recommendationproject
Severityproject
Tacticsproject
URLproject

New Sonrai Ticket

#

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.

Status
available
Severity
medium
Time window
5m
Source
github.com/Azure/Azure-Sentinel

'Checks for new Sonrai tickets. It uses the action type to check if a ticket has been created'

MITRE ATT&CK Coverage

Rule Body

id: bcc3362d-b6f9-4de0-b41c-707fafd5a416
name: New Sonrai Ticket
description: |
  'Checks for new Sonrai tickets. 
  It uses the action type to check if a ticket has been created'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SonraiDataConnector
    dataTypes:
      - Sonrai_Tickets_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Collection
  - CommandAndControl
  - CredentialAccess
  - DefenseEvasion
  - Discovery
  - Execution
  - Exfiltration
  - Impact
  - InitialAccess
  - LateralMovement
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1566
  - T1059
  - T1547
  - T1548
  - T1562
  - T1003
  - T1087
  - T1021
  - T1119
  - T1071
  - T1041
  - T1499
query: |
  Sonrai_Tickets_CL
  | where action_d == 1
entityMappings: 
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: digest_criticalResourceName_s
eventGroupingSettings:
  aggregationKind: AlertPerResult
customDetails:
  ticketName: digest_title_s
  ticketStatus: digest_status_s
  resourceLabel: digest_resourceLabel_s
  resourceType: digest_resourceType_s
  criticalResource: digest_criticalResourceName_s
  ticketOrg: digest_org_s
alertDetailsOverride:
  alertSeverityColumnName: digest_severityCategory_s
  alertDisplayNameFormat: New - {{digest_ticketSrn_s}} - {{digest_ticketKeyName_s}}
  alertDescriptionFormat: digest_ticketKeyDescription_s
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

Sonrai_Tickets_CL

Stage 2: where

| where action_d == 1

Indicators

These rows show field, operator, and value matches.

NGINX - Private IP address in URL

#
Status
available
Severity
medium
Time window
1h
Source
github.com/Azure/Azure-Sentinel

'Detects requests to unusual URL'

MITRE ATT&CK Coverage

Rule Body

id: 1aa6bfed-f11b-402f-9007-0dccc1152ede
name: NGINX - Private IP address in URL
description: |
  'Detects requests to unusual URL'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    dataTypes:
      - NGINX_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  NGINXHTTPServer
  | where UrlOriginal matches regex @'(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.1[6-9]\.\d{1,3}\.\d{1,3})|(172\.2[0-9]\.\d{1,3}\.\d{1,3})|(172\.3[0-1]\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3})'
  | extend UrlCustomEntity = UrlOriginal
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: UrlCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

NGINXHTTPServer

Stage 2: where

| where UrlOriginal matches regex @'(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.1[6-9]\.\d{1,3}\.\d{1,3})|(172\.2[0-9]\.\d{1,3}\.\d{1,3})|(172\.3[0-1]\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3})'

Stage 3: extend

| extend UrlCustomEntity = UrlOriginal

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
UrlOriginalregex_match
    • (10.\d{1,3}.\d{1,3}.\d{1,3})
    • (172.1[6-9].\d{1,3}.\d{1,3})
    • (172.2[0-9].\d{1,3}.\d{1,3})
    • (172.3[0-1].\d{1,3}.\d{1,3})
    • (192.168.\d{1,3}.\d{1,3})
field:"UrlOriginal" kind:regex_match

Output Fields

These fields are emitted when the rule matches.

FieldSource
UrlCustomEntityextend

OracleDBAudit - User connected to database from new IP

#
Status
available
Severity
low
Time window
14d
Group by
DstUserName
Source
github.com/Azure/Azure-Sentinel

'Detects when a user connects to database from new IP address.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 39a0995e-f4a9-4869-a0ae-36d6d9049bfd
name: OracleDBAudit - User connected to database from new IP
description: |
  'Detects when a user connects to database from new IP address.'
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |
  let lbtime_d = 14d;
  let lbtime_24h = 24h;
  OracleDatabaseAuditEvent
  | where TimeGenerated between (ago(lbtime_d) .. ago(lbtime_24h))
  | where isnotempty(SrcIpAddr)
  | where isnotempty(DstUserName)
  | summarize knownIPs = make_set(SrcIpAddr) by DstUserName
  | join (OracleDatabaseAuditEvent
        | where isnotempty(SrcIpAddr)
        | where isnotempty(DstUserName)
        | where DbAction =~ 'connect'
        ) on DstUserName
  | where knownIPs !contains SrcIpAddr
  | project DstUserName, SrcIpAddr
  | extend AccountCustomEntity = DstUserName
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let lbtime_d = 14d;
let lbtime_24h = 24h;

Stage 1: source

OracleDatabaseAuditEvent

Stage 2: where

| where TimeGenerated between (ago(lbtime_d) .. ago(lbtime_24h))

Stage 3: where

| where isnotempty(SrcIpAddr)

Stage 4: where

| where isnotempty(DstUserName)

Stage 5: summarize

| summarize knownIPs = make_set(SrcIpAddr) by DstUserName

Stage 6: join

| join (OracleDatabaseAuditEvent
      | where isnotempty(SrcIpAddr)
      | where isnotempty(DstUserName)
      | where DbAction =~ 'connect'
      ) on DstUserName

Stage 7: where

where knownIPs !contains SrcIpAddr

Stage 8: project

project DstUserName, SrcIpAddr

Stage 9: extend

extend AccountCustomEntity

Stage 10: extend

extend IPCustomEntity

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
DbActioneq
  • connect
field:"DbAction" kind:eq value:"connect"
DstUserNameis_not_null
  • (no value, null check)
field:"DstUserName" kind:is_not_null
SrcIpAddris_not_null
  • (no value, null check)
field:"SrcIpAddr" kind:is_not_null

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstUserNameproject
SrcIpAddrproject
AccountCustomEntityextend
IPCustomEntityextend

Pathlock TDnR - Kerberos Keytab Changes

#
Status
available
Severity
high
Time window
1h
Source
github.com/Azure/Azure-Sentinel

Detects changes to Kerberos keytab configuration in SAP, forwarded by Pathlock Threat Detection and Response. Modifications to Kerberos settings may indicate credential theft, SSO bypass attempts, or persistent access mechanisms leveraging Kerberos delegation.

MITRE ATT&CK Coverage

TacticTechniques
Persistence
Credential Access

Rule Body

id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c12
name: Pathlock TDnR - Kerberos Keytab Changes
kind: Scheduled
description: >-
  Detects changes to Kerberos keytab configuration in SAP, forwarded by Pathlock Threat Detection
  and Response. Modifications to Kerberos settings may indicate credential theft, SSO bypass
  attempts, or persistent access mechanisms leveraging Kerberos delegation.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: Pathlock_TDnR
    dataTypes:
      - Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - Persistence
relevantTechniques:
  - T1558
  - T1098
query: |
  Pathlock_TDnR_CL
  | where DataSource == "CHANGEDOC_KERBEROS"
  | project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
            Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
            MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Bname
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: Hostname
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIp
eventGroupingSettings:
  aggregationKind: SingleAlert
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AnyAlert
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.0

Stages and Predicates

Stage 1: source

Pathlock_TDnR_CL

Stage 2: where

| where DataSource == "CHANGEDOC_KERBEROS"

Stage 3: project

| project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
          Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
          MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AffectedUserproject
Areaproject
Bnameproject
CentralTsproject
DataSourceproject
DestIpproject
Eventidproject
Hostnameproject
Instanceproject
LogLineproject
MessageV1project
MessageV2project
MessageV3project
MessageV4project
MsgIdproject
MsgNoproject
MsgTypeproject
Reportproject
SrcIpproject
Subidproject
Sysidproject
Tcodeproject
TimeGeneratedproject

Ping Federate - Unexpected country for user

#
Status
available
Severity
medium
Time window
1d
Source
github.com/Azure/Azure-Sentinel

'Detects requests from different countries for user in shotr term.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 64e65105-c4fc-4c28-a4e9-bb1a3ce7652d
name: Ping Federate - Unexpected country for user
description: |
  'Detects requests from different countries for user in shotr term.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |
  let known_countries = 
  PingFederateEvent
  | where TimeGenerated between (ago(1d) .. (1h))
  | where isnotempty(DstGeoCountry)
  | summarize makeset(DstGeoCountry);
  PingFederateEvent
  | where isnotempty(DstGeoCountry)
  | where DstGeoCountry !in (known_countries)
  | extend AccountCustomEntity = DstUserName
  | extend IpCustomEntity = SrcIpAddr
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IpCustomEntity
version: 1.0.3
kind: Scheduled

Stages and Predicates

Let Binding: known_countries used in Stage 3

let known_countries = PingFederateEvent
| where TimeGenerated between (ago(1d) .. (1h))
| where isnotempty(DstGeoCountry)
| summarize makeset(DstGeoCountry);

Stage 1: source

PingFederateEvent

Stage 2: where

| where isnotempty(DstGeoCountry)

Stage 3: where

| where DstGeoCountry !in (known_countries)

Stage 4: extend

| extend AccountCustomEntity = DstUserName

Stage 5: extend

| extend IpCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
DstGeoCountryis_not_null
  • (no value, null check)
field:"DstGeoCountry" kind:is_not_null

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
DstGeoCountryeqknown_countriesexcludes:DstGeoCountry field:"DstGeoCountry" value:"known_countries"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend
IpCustomEntityextend

Potential DHCP Starvation Attack

#
Status
available
Severity
medium
Time window
5m
Group by
SrcIpAddr
Source
github.com/Azure/Azure-Sentinel

'This creates an incident in the event that an excessive amount of DHCPREQUEST have been recieved by a DHCP Server and could potentially be an indication of a DHCP Starvation Attack.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 57e56fc9-417a-4f41-a579-5475aea7b8ce
name: Potential DHCP Starvation Attack
description: |
  'This creates an incident in the event that an excessive amount of DHCPREQUEST have been recieved by a DHCP Server and could potentially be an indication of a DHCP Starvation Attack.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1200
query: |
  let threshold = 1000;
  Infoblox
  | where ProcessName =~ "dhcpd" and Log_Type =~ "DHCPREQUEST"
  | summarize count() by SrcIpAddr, bin(TimeGenerated,5m)
  | where count_ > threshold
  | join kind=inner (Infoblox
      | where ProcessName =~ "dhcpd" and Log_Type =~ "DHCPREQUEST"
      ) on SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
version: 1.0.5
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 1000;

Stage 1: source

Infoblox

Stage 2: where

| where ProcessName =~ "dhcpd" and Log_Type =~ "DHCPREQUEST"

Stage 3: summarize

| summarize count() by SrcIpAddr, bin(TimeGenerated,5m)

Stage 4: where

| where count_ > threshold

Stage 5: join

| join kind=inner (Infoblox
    | where ProcessName =~ "dhcpd" and Log_Type =~ "DHCPREQUEST"
    ) on SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
SrcIpAddrsummarize

PROD (TM015.1) - ACL - Modified at KRBTGT or AdminSDHolder Object Level

#
Status
available
Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

ACL changes to the KRBTGT or AdminSDHolder special objects.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: a9f94bf1-856e-40ff-a176-24f967da1e27
name: 'PROD (TM015.1) - ACL - Modified at KRBTGT or AdminSDHolder Object Level'
description: |
  ACL changes to the KRBTGT or AdminSDHolder special objects.
severity: High
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - Persistence
relevantTechniques:
  - T1078.002
query: |
  SecurityEvent 
  | where EventID == 5136 
      and (EventData matches regex @"(?i)CN=AdminSDHolder" or EventData matches regex @"(?i)CN=krbtgt")
      and Activity has "modified"
      and (EventData has '"ObjectClass">container' or EventData matches regex @"(?i)user")
      and EventData has '<Data Name="AttributeLDAPDisplayName">nTSecurityDescriptor</Data>' 
      and EventData has '"OperationType">%%14674'
  | extend ObjectName = extract("<Data Name=\"ObjectDN\">(((?i)CN=[^,]+,?)+),[dD][cC]=", 1, EventData)
  | extend ObjectTier = "Tier 0"
  | extend AttributeLDAP = extract("<Data Name=\"AttributeLDAPDisplayName\">([^<]+)</Data>", 1, EventData)
  | extend OperationType = iff(EventData has 'nTSecurityDescriptor', "ACL Modified", tostring(OperationType))
  | extend ActivityType = extract("([a-zA-Z]+)\\.$", 1, Activity)
  | extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
  | extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)
  | project TimeGenerated
     , ObjectName
     , ObjectTier
     , ObjectClass
     , OperationType
     , ActivityType
     , AttributeLDAP
     , Account
     , Domain
     , Computer
     , Channel
     , EventID
     , EventData
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ObjectName
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: Computer
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
customDetails:
  ObjectName: ObjectName
  ObjectTier: ObjectTier
  ObjectClass: ObjectClass
  OperationType: OperationType
  ActivityType: ActivityType
  AttributeLDAP: AttributeLDAP
  Account: Account
  Domain: Domain
  Computer: Computer
  Channel: Channel
  EventID: EventID
  EventData: EventData
eventGroupingSettings:
  aggregationKind: AlertPerResult
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
alertDetailsOverride:
  alertDisplayNameFormat: '(TM015.1) The {{ObjectTier}} {{ObjectName}} ACL was modified'
  alertDescriptionFormat: 'The following user {{Account}} modified the {{ObjectName}} ACLs.'
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID == 5136 
    and (EventData matches regex @"(?i)CN=AdminSDHolder" or EventData matches regex @"(?i)CN=krbtgt")
    and Activity has "modified"
    and (EventData has '"ObjectClass">container' or EventData matches regex @"(?i)user")
    and EventData has '<Data Name="AttributeLDAPDisplayName">nTSecurityDescriptor</Data>' 
    and EventData has '"OperationType">%%14674'

Stage 3: extend (7 consecutive steps)

| extend ObjectName = extract("<Data Name=\"ObjectDN\">(((?i)CN=[^,]+,?)+),[dD][cC]=", 1, EventData)
| extend ObjectTier = "Tier 0"
| extend AttributeLDAP = extract("<Data Name=\"AttributeLDAPDisplayName\">([^<]+)</Data>", 1, EventData)
| extend OperationType = iff(EventData has 'nTSecurityDescriptor', "ACL Modified", tostring(OperationType))
| extend ActivityType = extract("([a-zA-Z]+)\\.$", 1, Activity)
| extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
| extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)

Stage 4: project

| project TimeGenerated
   , ObjectName
   , ObjectTier
   , ObjectClass
   , OperationType
   , ActivityType
   , AttributeLDAP
   , Account
   , Domain
   , Computer
   , Channel
   , EventID
   , EventData

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Activitymatch
  • modified transforms: term corpus 10 (kusto 10)
field:"Activity" kind:match value:"modified"
EventDatamatch
  • "ObjectClass">container transforms: term
  • "OperationType">%%14674 transforms: term corpus 7 (kusto 7)
  • <Data Name="AttributeLDAPDisplayName">nTSecurityDescriptor</Data> transforms: term corpus 3 (kusto 3)
field:"EventData" kind:match
EventDataregex_match
  • (?i)CN=AdminSDHolder
  • (?i)CN=krbtgt
  • (?i)user
field:"EventData" kind:regex_match
EventIDeq
  • 5136 corpus 20 (kusto 18, elastic 2)
field:"EventID" kind:eq value:"5136"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Accountproject
ActivityTypeproject
AttributeLDAPproject
Channelproject
Computerproject
Domainproject
EventDataproject
EventIDproject
ObjectClassproject
ObjectNameproject
ObjectTierproject
OperationTypeproject
TimeGeneratedproject

Remote Desktop Protocol - SharpRDP

#
Status
available
Severity
medium
Time window
1h
Group by
DeviceId, LogonId
Source
github.com/Azure/Azure-Sentinel

This detection monitors for the behavior that SharpRDP exhibits on the target system. The most relevant is leveraging taskmgr.exe to gain elevated execution, which means that taskmgr.exe is creating unexpected child processes.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: cc46e76c-0d04-40b0-9c8b-929aa40513e7
name: Remote Desktop Protocol - SharpRDP
description: |
  This detection monitors for the behavior that SharpRDP exhibits on the target system. The most relevant is leveraging taskmgr.exe to gain elevated execution, which means that taskmgr.exe is creating unexpected child processes.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
      - DeviceLogonEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - LateralMovement
relevantTechniques:
  - T1021.001
query: |
  let executions = DeviceProcessEvents
  | where InitiatingProcessFileName contains "taskmgr" and AccountName !contains "_ladm" // Include your LAPS /RID500 admin naming convention here. 
  // Add the below filter to look for stock SharpRDP behavior. However, with minorchanges to the stock version of SharpRDP the filter below can be bypassed.
  //| where not(InitiatingProcessCommandLine has_any ("/1","/2","/3","/4"))
  | where not(FolderPath =~ @"c:\Windows\system32\WerFault.exe" and ProcessCommandLine contains "-u -p")
  | where not(FolderPath =~ @"c:\windows\system32\mmc.exe" and ProcessCommandLine contains @"C:\WINDOWS\System32\services.msc")
  | where not(FolderPath =~ @"c:\windows\system32\resmon.exe");
  executions
  | join kind=leftsemi  (DeviceLogonEvents
  | where LogonType in ('Unlock', 'RemoteInteractive') and not (LogonType == 'Unlock' and RemoteIP == '127.0.0.1') and RemoteIP != "" and ActionType == "LogonSuccess") on DeviceId, LogonId
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: DeviceName
  - entityType: Account
    fieldMappings:
      - identifier: Sid
        columnName: AccountSid 
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountDomain
  - entityType: Process
    fieldMappings:
      - identifier: CommandLine
        columnName: ProcessCommandLine
version: 1.0.0
kind: Scheduled

Stages and Predicates

let executions is inlined into the numbered stages below.

Stage 1: source

let executions

Stage 2: source

DeviceProcessEvents

Stage 3: where

where not (AccountName contains "_ladm") and InitiatingProcessFileName contains "taskmgr"

Stage 4: where

where not ((FolderPath =~ @"c:\Windows\system32\WerFault.exe" and ProcessCommandLine contains "-u -p"))

Stage 5: where

where not ((FolderPath =~ @"c:\windows\system32\mmc.exe" and ProcessCommandLine contains @"C:\WINDOWS\System32\services.msc"))

Stage 6: where

where not (FolderPath =~ @"c:\windows\system32\resmon.exe")

Stage 7: join

join kind=leftsemi (DeviceLogonEvents) on DeviceId, LogonId

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

Sonrai Ticket Reopened

#

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.

Status
available
Severity
medium
Time window
5m
Source
github.com/Azure/Azure-Sentinel

'Checks if Sonrai tickets have been reopened. It uses the action type to check if a ticket has been reopened'

MITRE ATT&CK Coverage

Rule Body

id: b60129ab-ce22-4b76-858d-3204932a13cc
name: Sonrai Ticket Reopened
description: |
  'Checks if Sonrai tickets have been reopened. 
  It uses the action type to check if a ticket has been reopened'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SonraiDataConnector
    dataTypes:
      - Sonrai_Tickets_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Collection
  - CommandAndControl
  - CredentialAccess
  - DefenseEvasion
  - Discovery
  - Execution
  - Exfiltration
  - Impact
  - InitialAccess
  - LateralMovement
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1566
  - T1059
  - T1547
  - T1548
  - T1562
  - T1003
  - T1087
  - T1021
  - T1119
  - T1071
  - T1041
  - T1499
query: |
  Sonrai_Tickets_CL
  | where action_d == 3
entityMappings: 
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: digest_criticalResourceName_s
eventGroupingSettings:
  aggregationKind: AlertPerResult
customDetails:
  ticketName: digest_title_s
  ticketStatus: digest_status_s
  ticketSeverity: digest_severityCategory_s
  resourceLabel: digest_resourceLabel_s
  resourceType: digest_resourceType_s
  criticalResource: digest_criticalResourceName_s
  ticketOrg: digest_org_s
  reopenedDate: digest_lastReopenDate_d
alertDetailsOverride:
  alertSeverityColumnName: digest_severityCategory_s
  alertDisplayNameFormat: Reopened - {{digest_ticketSrn_s}} - {{digest_ticketKeyName_s}}
  alertDescriptionFormat: digest_ticketKeyDescription_s
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

Sonrai_Tickets_CL

Stage 2: where

| where action_d == 3

Indicators

These rows show field, operator, and value matches.

Tailscale: MagicDNS disabled

#
Status
available
Severity
medium
Time window
15m
Source
github.com/Azure/Azure-Sentinel

Identifies when MagicDNS is turned off on the tailnet. Disabling MagicDNS changes DNS behaviour for every device and is occasionally a precursor to wider DNS hijacking - verify the change was intentional.

MITRE ATT&CK Coverage

Rule Body

id: f8a9b0c1-4567-8901-23ab-cdef12345020
name: "Tailscale: MagicDNS disabled"
description: |
  Identifies when MagicDNS is turned off on the tailnet. Disabling MagicDNS changes DNS behaviour for every device and is occasionally a precursor to wider DNS hijacking - verify the change was intentional.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: TailscaleCCF
    dataTypes:
      - Tailscale_Audit_CL
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1556
query: |
  Tailscale_Audit_CL
  | where Action == "UPDATE"
  | where tostring(Target.type) == "TAILNET"
  | where tostring(Target.property) == "MAGICDNS_PREFERENCES"
  | extend ActorLogin = tostring(Actor.loginName)
  | extend OldMagicDns = tobool(Old.magicDNS)
  | extend NewMagicDns = tobool(New.magicDNS)
  | where OldMagicDns == true and NewMagicDns == false
  | project TimeGenerated, ActorLogin, OldMagicDns, NewMagicDns, Origin
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ActorLogin
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: P1D
    matchingMethod: AllEntities
kind: Scheduled
version: 1.0.0

Stages and Predicates

Stage 1: source

Tailscale_Audit_CL

Stage 2: where

| where Action == "UPDATE"

Stage 3: where

| where tostring(Target.type) == "TAILNET"

Stage 4: where

| where tostring(Target.property) == "MAGICDNS_PREFERENCES"

Stage 5: extend (3 consecutive steps)

| extend ActorLogin = tostring(Actor.loginName)
| extend OldMagicDns = tobool(Old.magicDNS)
| extend NewMagicDns = tobool(New.magicDNS)

Stage 6: where

| where OldMagicDns == true and NewMagicDns == false

Stage 7: project

| project TimeGenerated, ActorLogin, OldMagicDns, NewMagicDns, Origin

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActorLoginproject
NewMagicDnsproject
OldMagicDnsproject
Originproject
TimeGeneratedproject

TI Map IP Entity to VMConnection

#
Severity
medium
Time window
14d
Group by
$left.TI_ipEntity, $right.RemoteIp, Id, ObservableValue, RemoteIp
Source
github.com/Azure/Azure-Sentinel

This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in VMConnection.

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: aed70d71-adb2-4f73-becd-02150b13950b
name: TI Map IP Entity to VMConnection
description: |
  This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in VMConnection.
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: AzureMonitor(VMInsights)
    dataTypes:
      - VMConnection
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  let dt_lookBack = 1h; // Look back 1 hour for VMConnection events
  let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
  // Fetch threat intelligence indicators related to IP addresses
  let IP_Indicators = ThreatIntelIndicators
  //extract key part of kv pair
       | extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
       | where IndicatorType in ("ipv4-addr", "ipv6-addr", "network-traffic")
       | extend NetworkSourceIP = toupper(ObservableValue)
       | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
    | where TimeGenerated >= ago(ioc_lookBack)
    | extend TI_ipEntity = iff(isnotempty(NetworkSourceIP), NetworkSourceIP, NetworkSourceIP)
    | extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
    | extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
    | where ipv4_is_private(TI_ipEntity) == false and  TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
    | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
    | where IsActive and (ValidUntil > now() or isempty(ValidUntil));
  // Perform a join between IP indicators and VMConnection events
  IP_Indicators
     | project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
    // Use innerunique to keep performance fast and result set low, as we only need one match to indicate potential malicious activity that needs investigation
    | join kind=innerunique (
        VMConnection
        | where TimeGenerated >= ago(dt_lookBack)
        | extend VMConnection_TimeGenerated = TimeGenerated
    )
    on $left.TI_ipEntity == $right.RemoteIp
    // Filter out VMConnection events that occurred after the expiration of the corresponding indicator
    | where VMConnection_TimeGenerated < ValidUntil
    // Group the results by IndicatorId and keep the VMConnection event with the latest timestamp
    | summarize VMConnection_TimeGenerated = arg_max(VMConnection_TimeGenerated, *) by Id, RemoteIp
    // Select the desired output fields
    | extend Description = tostring(parse_json(Data).description)
    | extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
    | project VMConnection_TimeGenerated, Description, ActivityGroupNames, Id, ValidUntil, Confidence,
      TI_ipEntity, Computer, Direction, ProcessName, SourceIp, DestinationIp, RemoteIp, Protocol, DestinationPort, NetworkSourceIP, Type, Url
    | extend timestamp = VMConnection_TimeGenerated, HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: RemoteIp
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
version: 1.4.6
kind: Scheduled

Stages and Predicates

Parameters

let dt_lookBack = 1h;
let ioc_lookBack = 14d;

let IP_Indicators is inlined into the numbered stages below.

Stages 1 to 10 define let IP_Indicators as the rule's main pipeline source. Stages 11 to 18 use it.

Stage 1: source

ThreatIntelIndicators

Stage 2: extend

| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))

Stage 3: where

| where IndicatorType in ("ipv4-addr", "ipv6-addr", "network-traffic")

Stage 4: extend

| extend NetworkSourceIP = toupper(ObservableValue)

Stage 5: extend

| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)

Stage 6: where

| where TimeGenerated >= ago(ioc_lookBack)

Stage 7: extend (3 consecutive steps)

| extend TI_ipEntity = iff(isnotempty(NetworkSourceIP), NetworkSourceIP, NetworkSourceIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
TI_ipEntity =
ifisnotempty(NetworkSourceIP)NetworkSourceIP
elseNetworkSourceIP

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

| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue

Stage 10: where

| where IsActive and (ValidUntil > now() or isempty(ValidUntil))

Stage 11: project-reorder

IP_Indicators
| project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity

Stage 12: join

| join kind=innerunique (
      VMConnection
      | where TimeGenerated >= ago(dt_lookBack)
      | extend VMConnection_TimeGenerated = TimeGenerated
  )
  on $left.TI_ipEntity == $right.RemoteIp

Stage 13: where

where VMConnection_TimeGenerated < ValidUntil

Stage 14: summarize

summarize VMConnection_TimeGenerated by Id, RemoteIp

Stage 15: extend

extend Description

Stage 16: extend

extend ActivityGroupNames

Stage 17: project

project ActivityGroupNames, Computer, Confidence, Description, DestinationIp, DestinationPort, Direction, Id, NetworkSourceIP, ProcessName, Protocol, RemoteIp, SourceIp, TI_ipEntity, Type, Url, VMConnection_TimeGenerated, ValidUntil

Stage 18: extend

extend DnsDomain, HostName, timestamp

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
IndicatorTypein
  • ipv4-addr
  • ipv6-addr
  • network-traffic
field:"IndicatorType" kind:in
IsActiveeq
  • true transforms: boolean
field:"IsActive" kind:eq value:"true"
ValidUntilis_null
  • (no value, null check)
field:"ValidUntil" kind:is_null

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
TI_ipEntitycidr_match10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8excludes:TI_ipEntity
TI_ipEntitystarts_with127.excludes:TI_ipEntity field:"TI_ipEntity" value:"127."
TI_ipEntitystarts_with::excludes:TI_ipEntity field:"TI_ipEntity" value:"::"
TI_ipEntitystarts_withfe80excludes:TI_ipEntity field:"TI_ipEntity" value:"fe80"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActivityGroupNamesproject
Computerproject
Confidenceproject
Descriptionproject
DestinationIpproject
DestinationPortproject
Directionproject
Idproject
NetworkSourceIPproject
ProcessNameproject
Protocolproject
RemoteIpproject
SourceIpproject
TI_ipEntityproject
Typeproject
Urlproject
VMConnection_TimeGeneratedproject
ValidUntilproject
DnsDomainextend
HostNameextend
timestampextend

Tomcat - Commands in URI

#
Status
available
Severity
high
Time window
1h
Source
github.com/Azure/Azure-Sentinel

'Detects commands in URI'

MITRE ATT&CK Coverage

Rule Body

id: 91f59cea-486f-11ec-81d3-0242ac130003
name: Tomcat - Commands in URI
description: |
  'Detects commands in URI'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    datatypes:
      - Tomcat_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  let commands = dynamic(['cat%20/etc/passwd', '/etc/passwd', 'ping -i', '/usr/bin/id(', '%2f%75%73%72%2f%62%69%6e%2f%69%64', 'phpinfo()', '%70%68%70%69%6e%66%6f%28%29', ';id', '%3b%69%64', '/bin/bash -c', '%2f%62%69%6e%2f%62%61%73%68%20%2d%63%27', '/bin/bash', '%2f%62%69%6e%2f%62%61%73%68', 'sleep(', '%73%6c%65%65%70%28', 'curl', '%63%75%72%6c', '&dir', '%26%64%69%72', '& dir', '%26%20%64%69%72', '<script>', '%3c%73%63%72%69%70%74%3e', 'eval(', '%65%76%61%6c%28', 'exec(', '%65%78%65%63%28', 'whoami', '%77%68%6f%61%6d%69', 'wget', 'python', 'gcc', 'uname', 'systeminfo', '%70%79%74%68%6f%6e', '%75%6e%61%6d%65', '%73%79%73%74%65%6d%69%6e%66%6f']);
  TomcatEvent
  | where UrlOriginal has_any (commands)
  | extend UrlCustomEntity = UrlOriginal
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: UrlCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Let Binding: commands used in Stage 2

let commands = dynamic(['cat%20/etc/passwd', '/etc/passwd', 'ping -i', '/usr/bin/id(', '%2f%75%73%72%2f%62%69%6e%2f%69%64', 'phpinfo()', '%70%68%70%69%6e%66%6f%28%29', ';id', '%3b%69%64', '/bin/bash -c', '%2f%62%69%6e%2f%62%61%73%68%20%2d%63%27', '/bin/bash', '%2f%62%69%6e%2f%62%61%73%68', 'sleep(', '%73%6c%65%65%70%28', 'curl', '%63%75%72%6c', '&dir', '%26%64%69%72', '& dir', '%26%20%64%69%72', '<script>', '%3c%73%63%72%69%70%74%3e', 'eval(', '%65%76%61%6c%28', 'exec(', '%65%78%65%63%28', 'whoami', '%77%68%6f%61%6d%69', 'wget', 'python', 'gcc', 'uname', 'systeminfo', '%70%79%74%68%6f%6e', '%75%6e%61%6d%65', '%73%79%73%74%65%6d%69%6e%66%6f']);

Stage 1: source

TomcatEvent

Stage 2: where

| where UrlOriginal has_any (commands)

Stage 3: extend

| extend UrlCustomEntity = UrlOriginal

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
UrlOriginalmatch
  • %26%20%64%69%72 transforms: term
  • %26%64%69%72 transforms: term
  • %2f%62%69%6e%2f%62%61%73%68 transforms: term
  • %2f%62%69%6e%2f%62%61%73%68%20%2d%63%27 transforms: term
  • %2f%75%73%72%2f%62%69%6e%2f%69%64 transforms: term
  • %3b%69%64 transforms: term
  • %3c%73%63%72%69%70%74%3e transforms: term
  • %63%75%72%6c transforms: term
  • %65%76%61%6c%28 transforms: term
  • %65%78%65%63%28 transforms: term
  • %70%68%70%69%6e%66%6f%28%29 transforms: term
  • %70%79%74%68%6f%6e transforms: term
  • %73%6c%65%65%70%28 transforms: term
  • %73%79%73%74%65%6d%69%6e%66%6f transforms: term
  • %75%6e%61%6d%65 transforms: term
  • %77%68%6f%61%6d%69 transforms: term
  • & dir transforms: term
  • &dir transforms: term
  • /bin/bash transforms: term
  • /bin/bash -c transforms: term
  • /etc/passwd transforms: term
  • /usr/bin/id( transforms: term
  • ;id transforms: term
  • <script> transforms: term
  • cat%20/etc/passwd transforms: term
  • curl transforms: term
  • eval( transforms: term
  • exec( transforms: term
  • gcc transforms: term
  • phpinfo() transforms: term
  • ping -i transforms: term
  • python transforms: term
  • sleep( transforms: term
  • systeminfo transforms: term
  • uname transforms: term
  • wget transforms: term
  • whoami transforms: term
field:"UrlOriginal" kind:match

Output Fields

These fields are emitted when the rule matches.

FieldSource
UrlCustomEntityextend

Unusual Anomaly

#
Severity
medium
Time window
4d
Group by
RuleName
Source
github.com/Azure/Azure-Sentinel

'Anomaly Rules generate events in the Anomalies table. This scheduled rule tries to detect Anomalies that are not usual, they could be a type of Anomaly that has recently been activated, or an infrequent type. The detected Anomaly should be reviewed, if it is relevant enough, eventually a separate scheduled Analytics Rule could be created specifically for that Anomaly Type, so an alert and/or incident is generated everytime that type of Anomaly happens.'

Rule Body

id: d0255b5f-2a3c-4112-8744-e6757af3283a
name: Unusual Anomaly
description: |
  'Anomaly Rules generate events in the Anomalies table. This scheduled rule tries to detect Anomalies that are not usual, they could be a type of Anomaly that has recently been activated, or an infrequent type. The detected Anomaly should be reviewed, if it is relevant enough, eventually a separate scheduled Analytics Rule could be created specifically for that Anomaly Type, so an alert and/or incident is generated everytime that type of Anomaly happens.'
severity: Medium
requiredDataConnectors: []
queryFrequency: 1h
queryPeriod: 4d
triggerOperator: gt
triggerThreshold: 0
tactics: []
techniques: []
query: |
  // You can leave out Anomalies that are already monitored through other Analytics Rules
  //let _MonitoredRules = dynamic(["TestAlertName"]);
  let query_frequency = 1h;
  let query_lookback = 3d;
  Anomalies
  | where TimeGenerated > ago(query_frequency)
  //| where not(RuleName has_any (_MonitoredRules))
  | join kind = leftanti (
      Anomalies
      | where TimeGenerated between (ago(query_frequency + query_lookback)..ago(query_frequency))
      | distinct RuleName
  ) on RuleName
  | extend Name = tostring(split(UserPrincipalName, "@")[0]), UPNSuffix = tostring(split(UserPrincipalName, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: Unusual Anomaly - {{RuleName}}
  alertTacticsColumnName: Tactics
  alertDynamicProperties:
    - alertProperty: Techniques
      value: Techniques
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let query_frequency = 1h;
let query_lookback = 3d;

Stage 1: source

Anomalies

Stage 2: where

| where TimeGenerated > ago(query_frequency)

Stage 3: join (negated)

join kind=leftanti (Anomalies) on RuleName

Stage 4: extend

extend Name, UPNSuffix

Output Fields

These fields are emitted when the rule matches.

FieldSource
Nameextend
UPNSuffixextend

Vectra RUX - Create Incident for Escalated Account Detection or Unresolved Priority Account

#
Status
available
Severity
high
Time window
10m
Source
github.com/Azure/Azure-Sentinel

This query creates a Microsoft Sentinel incident when a detection on an account entity has been escalated in Vectra (investigation_status = escalated). Escalation indicates that a human analyst or MDR service has reviewed the detection and determined it requires immediate customer attention or notification. One incident is created per detection - grouping is based on alert display name so that repeated rule evaluations against the same detection do not create duplicate incidents. This query creates a Microsoft Sentinel incident when Vectra AI identifies an account entity with one or more detections marked as unresolved and prioritized (unresolved_priority = true). Vectra's AI engine assigns priority based on attack rating, velocity, breadth, and identity privilege level. This rule surfaces account-based threats that require analyst attention. Incidents are grouped per account entity and remain open while the threat persists. Once all associated detections are resolved in Vectra, the incident can be closed in Microsoft Sentinel.

MITRE ATT&CK Coverage

Rule Body

id: 231904f5-b670-4223-9bec-2e9aeca9cf5a
name: Vectra RUX - Create Incident for Escalated Account Detection or Unresolved Priority Account
version: 1.0.0
kind: Scheduled
description: |
  This query creates a Microsoft Sentinel incident when a detection on an account entity has been escalated in Vectra
  (investigation_status = escalated). Escalation indicates that a human analyst or MDR service
  has reviewed the detection and determined it requires immediate customer attention or notification.
  One incident is created per detection - grouping is based on alert display name so that
  repeated rule evaluations against the same detection do not create duplicate incidents.

  This query creates a Microsoft Sentinel incident when Vectra AI identifies an account entity with one or more detections
  marked as unresolved and prioritized (unresolved_priority = true). Vectra's AI engine assigns
  priority based on attack rating, velocity, breadth, and identity privilege level. This rule surfaces
  account-based threats that require analyst attention.

  Incidents are grouped per account entity and remain open while the threat persists. Once all associated
  detections are resolved in Vectra, the incident can be closed in Microsoft Sentinel.
status: Available
requiredDataConnectors:
  - connectorId: VectraRUXConnector
    dataTypes:
      - VectraDetectionsCombined
severity: High
queryFrequency: 10m
queryPeriod: 10m
suppressionDuration: PT1H
suppressionEnabled: false
triggerOperator: GreaterThan
triggerThreshold: 0
tactics:
  - Discovery
  - LateralMovement
  - CredentialAccess
  - Exfiltration
  - CommandAndControl
  - Persistence
relevantTechniques:
  - T1078
  - T1110
  - T1003
  - T1041
  - T1071
query: |
  VectraDetectionsCombined
  | where ["Entity Type"] == "account"
  | summarize arg_max(TimeGenerated, *) by ["Detection ID"]
  | where ["Investigation Status"] == "escalated" or ["Unresolved Priority"] == true
  | extend
      detection_name     = ["D Type Vname"],
      detection_category = ["Detection Category"],
      entity_url         = replace_string(replace_string(URL, "api/v3.4/", ""), "api/v3.5/", ""),
      mitre_techniques   = tostring(Mitre),
      detection_tags     = tostring(Tags),
      assigned_to        = ["Assigned To"],
      entity_name = ["Entity Name"],
      entity_uid = ["Entity UID"],
      entity_id = ["Entity ID"],
      detection_id = ["Detection ID"],
      external_reference = ["External Reference ID"],
      investigation_status = ["Investigation Status"],
      entity_type = ["Entity Type"]
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: entity_name
      - identifier: UPNSuffix
        columnName: entity_uid
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: P7D
    matchingMethod: Selected
    groupByEntities:
      - Account
    groupByAlertDetails:
      - DisplayName
customDetails:
  detection_name: detection_name
  assigned_to: assigned_to
  entity_uid: entity_uid
  entity_url: entity_url
  entity_id: entity_id
  detection_id: detection_id
  external_reference: external_reference
  investigation_status: investigation_status
  category: detection_category
  tags: detection_tags
  entity_type: entity_type
  mitre_techniques: mitre_techniques
alertDetailsOverride:
  alertDisplayNameFormat: "Vectra AI - {{detection_name}} on {{entity_name}}"
  alertDescriptionFormat: >
    Vectra AI has escalated detection {{detection_name}} ({{detection_category}}) on account or identified
    account {{entity_name}} as a priority threat. See custom details for full context.
  alertDynamicProperties:
    - alertProperty: AlertLink
      value: entity_url
    - alertProperty: Techniques
      value: mitre_techniques
eventGroupingSettings:
  aggregationKind: AlertPerResult

Stages and Predicates

Stage 1: source

VectraDetectionsCombined

Stage 2: where

| where ["Entity Type"] == "account"

Stage 3: summarize

| summarize arg_max(TimeGenerated, *) by ["Detection ID"]

Stage 4: where

| where ["Investigation Status"] == "escalated" or ["Unresolved Priority"] == true

Stage 5: extend

| extend
    detection_name     = ["D Type Vname"],
    detection_category = ["Detection Category"],
    entity_url         = replace_string(replace_string(URL, "api/v3.4/", ""), "api/v3.5/", ""),
    mitre_techniques   = tostring(Mitre),
    detection_tags     = tostring(Tags),
    assigned_to        = ["Assigned To"],
    entity_name = ["Entity Name"],
    entity_uid = ["Entity UID"],
    entity_id = ["Entity ID"],
    detection_id = ["Detection ID"],
    external_reference = ["External Reference ID"],
    investigation_status = ["Investigation Status"],
    entity_type = ["Entity Type"]

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Entity Typeeq
  • account transforms: kusto_literal_column
field:"Entity Type" kind:eq value:"account"
Investigation Statuseq
  • escalated transforms: kusto_literal_column
field:"Investigation Status" kind:eq value:"escalated"
Unresolved Priorityeq
  • true transforms: kusto_literal_column
field:"Unresolved Priority" kind:eq value:"true"

Output Fields

These fields are emitted when the rule matches.

FieldSource
assigned_toextend
detection_categoryextend
detection_idextend
detection_nameextend
detection_tagsextend
entity_idextend
entity_nameextend
entity_typeextend
entity_uidextend
entity_urlextend
external_referenceextend
investigation_statusextend
mitre_techniquesextend

Veeam ONE Unusual Job Duration (Veeam Backup for Microsoft 365)

#

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.

Status
available
Severity
medium
Time window
3h
Source
github.com/Azure/Azure-Sentinel

Detects Veeam Backup for Microsoft 365 jobs with unusual execution duration.

Rule Body

id: cab7396a-9a18-45e7-b685-945e8bf825cb
name: Veeam ONE Unusual Job Duration (Veeam Backup for Microsoft 365)
description: Detects Veeam Backup for Microsoft 365 jobs with unusual execution duration.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: VeeamCustomTablesDataConnector
  dataTypes:
  - VeeamOneTriggeredAlarms_CL
queryFrequency: 3h
queryPeriod: 3h
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: VeeamOneTriggeredAlarms_CL | where PredefinedAlarmId == 381
version: 1.0.1
kind: Scheduled
customDetails:
  PredefinedAlarmId: PredefinedAlarmId
  TriggeredAlarmId: TriggeredAlarmId
  Description: Description
  Name: Name
  VoneHostName: VoneHostName
  TriggeredTime: TriggeredTime
  Status: Status
  Comment: Comment
  ObjectId: ObjectId
  ObjectName: ObjectName
  ObjectType: ObjectType

Stages and Predicates

Stage 1: source

VeeamOneTriggeredAlarms_CL

Stage 2: where

| where PredefinedAlarmId == 381

Indicators

These rows show field, operator, and value matches.

VMware SD-WAN Edge - IDS/IPS Alert triggered (Syslog)

#
Severity
high
Time window
1h
Source
github.com/Azure/Azure-Sentinel

The VMware SD-WAN Edge appliance captured a potentially malicious traffic flow. Please investigate the IOC information available. This analytics rule analyzes Syslog streams.

MITRE ATT&CK Coverage

TacticTechniques
Lateral Movement

Rule Body

id: a8e2bfd2-5d9c-4acc-aa55-30029e50d574
name: VMware SD-WAN Edge - IDS/IPS Alert triggered (Syslog)
version: 1.0.0
kind: Scheduled
description: |-
  The VMware SD-WAN Edge appliance captured a potentially malicious traffic flow. Please investigate the IOC information available.

  This analytics rule analyzes Syslog streams.
severity: High
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
requiredDataConnectors:
  - connectorId: VMwareSDWAN
    dataTypes:
      - SDWAN
tactics:
- LateralMovement
relevantTechniques:
- T1210
query: |-
  Syslog
  | where SyslogMessage contains "VCF Alert"
  | project-rename EdgeName=HostName
  | project-away Computer, HostIP, SourceSystem, Type
  | extend IdpsSignatureName = extract("SIGNATURE=(.+) CATEGORY=", 1, SyslogMessage)
  | extend IdpsAlertCategory = extract("CATEGORY=(.+) SEVERITY=", 1, SyslogMessage)
  | extend IdpsAlertSeverity = extract("SEVERITY=(.+) SRC_IP=", 1, SyslogMessage)
  | extend IdpsSignatureId = extract("SIG_ID=([0-9]+) SIGNATURE=", 1, SyslogMessage)
  | extend OverlaySegmentName = extract("SEGMENT_NAME=(.+) SIG_ID=", 1, SyslogMessage)
  | extend IpProtocol = extract("PROTO=([A-Z]+) SRC=", 1, SyslogMessage)
  | extend SrcIpAddress = extract("SRC=(.+) DST=", 1, SyslogMessage)
  | extend SrcPort = extract("SPT=([0-9]+) DPT=", 1, SyslogMessage)
  | extend DstIpAddress = extract("DST=(.+) SPT=", 1, SyslogMessage)
  | extend DstPort = extract("DPT=(.+) DEST_DOMAIN=", 1, SyslogMessage)
  | extend VictimIp = extract("TARGET_IP=(.+) TARGET_PORT=", 1, SyslogMessage)
  | extend AttackerIp = extract("SRC_IP=(.+) SRC_PORT=", 1, SyslogMessage)
  | extend DomainName = extract("DEST_DOMAIN=(.+) FW_POLICY_NAME=", 1, SyslogMessage)
  | extend EdgeFwAction = extract("ATP_ACTION=(.+) SEGMENT=", 1, SyslogMessage)
  | extend SyslogTag = extract("$(.+): ACTION=", 1, SyslogMessage)
  | extend FwPolicyName = extract("FW_POLICY_NAME=(.+) SEGMENT_NAME=", 1, SyslogMessage)
  | project
      TimeGenerated,
      IdpsSignatureName,
      IdpsAlertSeverity,
      IdpsAlertCategory,
      IdpsSignatureId,
      EdgeFwAction,
      EdgeName,
      SrcIpAddress,
      IpProtocol,
      SrcPort,
      DstIpAddress,
      DstPort,
      DomainName,
      AttackerIp,
      VictimIp,
      FwPolicyName,
      SyslogTag
suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
eventGroupingSettings:
  aggregationKind: AlertPerResult
customDetails:
  IDPS_Signature: IdpsSignatureName
  IDPS_Event_Category: IdpsAlertCategory
  Edge_Name: EdgeName
entityMappings:
- entityType: DNS
  fieldMappings:
  - identifier: DomainName
    columnName: DomainName
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: VictimIp
suppressionDuration: 5h

Stages and Predicates

Stage 1: source

Syslog

Stage 2: where

| where SyslogMessage contains "VCF Alert"

Stage 3: project-rename

| project-rename EdgeName=HostName

Stage 4: project-away

| project-away Computer, HostIP, SourceSystem, Type

Stage 5: extend (16 consecutive steps)

| extend IdpsSignatureName = extract("SIGNATURE=(.+) CATEGORY=", 1, SyslogMessage)
| extend IdpsAlertCategory = extract("CATEGORY=(.+) SEVERITY=", 1, SyslogMessage)
| extend IdpsAlertSeverity = extract("SEVERITY=(.+) SRC_IP=", 1, SyslogMessage)
| extend IdpsSignatureId = extract("SIG_ID=([0-9]+) SIGNATURE=", 1, SyslogMessage)
| extend OverlaySegmentName = extract("SEGMENT_NAME=(.+) SIG_ID=", 1, SyslogMessage)
| extend IpProtocol = extract("PROTO=([A-Z]+) SRC=", 1, SyslogMessage)
| extend SrcIpAddress = extract("SRC=(.+) DST=", 1, SyslogMessage)
| extend SrcPort = extract("SPT=([0-9]+) DPT=", 1, SyslogMessage)
| extend DstIpAddress = extract("DST=(.+) SPT=", 1, SyslogMessage)
| extend DstPort = extract("DPT=(.+) DEST_DOMAIN=", 1, SyslogMessage)
| extend VictimIp = extract("TARGET_IP=(.+) TARGET_PORT=", 1, SyslogMessage)
| extend AttackerIp = extract("SRC_IP=(.+) SRC_PORT=", 1, SyslogMessage)
| extend DomainName = extract("DEST_DOMAIN=(.+) FW_POLICY_NAME=", 1, SyslogMessage)
| extend EdgeFwAction = extract("ATP_ACTION=(.+) SEGMENT=", 1, SyslogMessage)
| extend SyslogTag = extract("$(.+): ACTION=", 1, SyslogMessage)
| extend FwPolicyName = extract("FW_POLICY_NAME=(.+) SEGMENT_NAME=", 1, SyslogMessage)

Stage 6: project

| project
    TimeGenerated,
    IdpsSignatureName,
    IdpsAlertSeverity,
    IdpsAlertCategory,
    IdpsSignatureId,
    EdgeFwAction,
    EdgeName,
    SrcIpAddress,
    IpProtocol,
    SrcPort,
    DstIpAddress,
    DstPort,
    DomainName,
    AttackerIp,
    VictimIp,
    FwPolicyName,
    SyslogTag

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AttackerIpproject
DomainNameproject
DstIpAddressproject
DstPortproject
EdgeFwActionproject
EdgeNameproject
FwPolicyNameproject
IdpsAlertCategoryproject
IdpsAlertSeverityproject
IdpsSignatureIdproject
IdpsSignatureNameproject
IpProtocolproject
SrcIpAddressproject
SrcPortproject
SyslogTagproject
TimeGeneratedproject
VictimIpproject

Votiro - File Blocked in Email

#
Severity
low
Time window
10m
Group by
FileWithEmailDetails, LogSeverity, SrcFileSHA256, fileName, policyName, sanitizationResult
Source
github.com/Azure/Azure-Sentinel

'The analytic rule is designed to identify when an email is blocked by Votiro Sanitization Engine policy. The rule generates an alert when an email is blocked after Sanitization process which is not password protected and has a from field meaning its a valid email. More information in terms of details about the blocked attachment, such as the file name and hash, and information about the email, such as the sender and recipient(s). The alert also includes information about the Votiro policy that blocked the attachment and provides a link to additional details about the incident.'

MITRE ATT&CK Coverage

Rule Body

id: 0b8b91de-c63e-4bc2-b5f4-b15d3b379ec9
name: Votiro - File Blocked in Email
description: |
 'The analytic rule is designed to identify when an email is blocked by Votiro Sanitization Engine policy. The rule generates an alert when an email is blocked after Sanitization process which is not password protected and has a from field meaning its a valid email. More information in terms of details about the blocked attachment, such as the file name and hash, and information about the email, such as the sender and recipient(s). The alert also includes information about the Votiro policy that blocked the attachment and provides a link to additional details about the incident.'
severity: Low
requiredDataConnectors:
  - connectorId: Votiro
    dataTypes:
      - CommonSecurityLog 
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
  - DefenseEvasion
  - Impact
  - InitialAccess
relevantTechniques:
  - T0885
  - T1036
  - T1027
  - T1486
  - T1566
query: let Votiro_view  = view () {
  VotiroEvents
   | where sanitizationResult has "Blocked" and passwordProtected == "false" and from =~ "null"
   | extend FileWithEmailDetails = strcat_delim(' ', fileName, 'with the hash', SrcFileSHA256, 'was blocked in an email that was sent from user', from, 'to the following recipients', recipients)
   | summarize count() by fileName, SrcFileSHA256, FileWithEmailDetails, policyName, tostring(incidentURL), sanitizationResult, LogSeverity
   | extend FileHashAlgo = "SHA256", FileHashValue = SrcFileSHA256};Votiro_view
entityMappings:
- entityType: FileHash
  fieldMappings:
    - identifier: Algorithm
      columnName: FileHashAlgo
    - identifier: Value
      columnName: FileHashValue
incidentConfiguration:
  createIncident: true
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride: 
   alertDisplayNameFormat: File with hash {{SrcFileSHA256}} was blocked
   alertDescriptionFormat: Attachment {{FileWithEmailDetails}} by Votiro due to Policy rules, see more detail in the following link {{incidentURL}}
   alertTacticsColumnName: sanitizationResult
   alertSeverityColumnName: LogSeverity
version: 1.0.1
kind: Scheduled

Stages and Predicates

Let Binding: Votiro_view

let Votiro_view = view () { VotiroEvents | where sanitizationResult has "Blocked" and passwordProtected == "false" and from =~ "null" | extend FileWithEmailDetails = strcat_delim(' ', fileName, 'with the hash', SrcFileSHA256, 'was blocked in an email that was sent from user', from, 'to the following recipients', recipients) | summarize count() by fileName, SrcFileSHA256, FileWithEmailDetails, policyName, tostring(incidentURL), sanitizationResult, LogSeverity | extend FileHashAlgo = "SHA256", FileHashValue = SrcFileSHA256};

Stage 1: source

VotiroEvents

Stage 2: where

where from =~ "null" and passwordProtected == false and sanitizationResult has "Blocked"

Stage 3: extend

extend FileWithEmailDetails

Stage 4: summarize

summarize by fileName, SrcFileSHA256, FileWithEmailDetails, policyName, sanitizationResult, LogSeverity

Stage 5: extend

extend FileHashAlgo, FileHashValue

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
FileWithEmailDetailssummarize
LogSeveritysummarize
SrcFileSHA256summarize
fileNamesummarize
policyNamesummarize
sanitizationResultsummarize
FileHashAlgoextend
FileHashValueextend

Back to all Kusto rules