Detection rules › Kusto

Kusto rules: 22

AWSCloudTrail - Suspicious AWS CLI Command Execution

#
Severity
medium
Time window
1m
Group by
AccountName, AccountUPNSuffix, RecipientAccountId, SessionMfaAuthenticated, SourceIpAddress, UserIdentityUserName
Source
github.com/Azure/Azure-Sentinel

'This detection focuses on identifying potentially suspicious activities involving the execution of AWS Command Line Interface (CLI) commands, particularly focusing on reconnaissance operations.'

MITRE ATT&CK Coverage

Rule Body

id: 8c2dc344-9352-4ca1-8863-b1b7a5e09e59
name: AWSCloudTrail - Suspicious AWS CLI Command Execution
description: |
  'This detection focuses on identifying potentially suspicious activities involving the execution of AWS Command Line Interface (CLI) commands, particularly focusing on reconnaissance operations.'
severity: Medium
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Reconnaissance
relevantTechniques:
  - T1595
  - T1592.004
  - T1589.002
  - T1589.003
  - T1590
  - T1591
  - T1596
query: |
  let SuspiciousCommands= pack_array('iam.list-users', 'iam.list-groups', 'ec2.describe-vpcs', 'ec2.describe-subnets', 'route53.list-hosted-zones', 'kms.list-keys', 'kms.list-aliases', 'ecs.list-clusters', 'ecs.list-services', 'iam.list-roles', 'iam.get-user''iam.list-access-keys', 'ec2.describe-security-groups', 'ec2.describe-network-acls', 'ec2.describe-network-interfaces', 'ec2.describe-route-tables', 'ec2.describe-internet-gateways', 'ec2.describe-vpc-peering-connections', 'ec2.describe-network-interfaces', 'ec2.describe-network-interfaces', 'ec2.describe-transit-gateway-vpc-attachment', 'ec2.describe-vpc');
  // Retrieve AWS CloudTrail events
  AWSCloudTrail 
  // Filter events with UserAgent starting with "aws-cli"
  | where UserAgent startswith "aws-cli" 
  // Extract the command from the UserAgent using string splitting
  | extend command = tostring(split(UserAgent, "off command/", 1)[0])  
  // Filter events based on predefined suspicious command list
  | where command has_any (SuspiciousCommands)  
  | extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
  | extend UserName = tostring(split(UserIdentityArn, '/')[-1])
  | extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
  | extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
    AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
  // Summarize relevant information for further analysis
  | summarize 
      CommadCount = dcount(command), 
      EventCount = dcount(EventName), 
      commands = make_list(command), 
      Events = make_list(EventName) 
      by 
      bin(TimeGenerated, 1min), 
      RecipientAccountId, AccountName, AccountUPNSuffix, 
      UserIdentityUserName, 
      SourceIpAddress, 
      SessionMfaAuthenticated 
  // Filter out results with a sufficient count of unique suspicious commands in 1 min time window
  | where CommadCount >= 8
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
      - identifier: CloudAppAccountId
        columnName: RecipientAccountId
customDetails:
   SuspiciousCommand: commands
   AWSUser: UserIdentityUserName
   AWSUserIp: SourceIpAddress
alertDetailsOverride:
  alertDisplayNameFormat: Suspicious AWS CLI reconnaissance by {{AccountName}} - {{CommadCount}} commands
  alertDescriptionFormat: User {{AccountName}} executed {{CommadCount}} suspicious AWS CLI reconnaissance commands from {{SourceIpAddress}} within 1 minute window.
kind: Scheduled
version: 1.0.2

Stages and Predicates

Let Binding: SuspiciousCommands used in Stage 4

let SuspiciousCommands = pack_array('iam.list-users', 'iam.list-groups', 'ec2.describe-vpcs', 'ec2.describe-subnets', 'route53.list-hosted-zones', 'kms.list-keys', 'kms.list-aliases', 'ecs.list-clusters', 'ecs.list-services', 'iam.list-roles', 'iam.get-user''iam.list-access-keys', 'ec2.describe-security-groups', 'ec2.describe-network-acls', 'ec2.describe-network-interfaces', 'ec2.describe-route-tables', 'ec2.describe-internet-gateways', 'ec2.describe-vpc-peering-connections', 'ec2.describe-network-interfaces', 'ec2.describe-network-interfaces', 'ec2.describe-transit-gateway-vpc-attachment', 'ec2.describe-vpc');

Stage 1: source

AWSCloudTrail

Stage 2: where

| where UserAgent startswith "aws-cli"

Stage 3: extend

| extend command = tostring(split(UserAgent, "off command/", 1)[0])

Stage 4: where

| where command has_any (SuspiciousCommands)

Stage 5: extend (4 consecutive steps)

| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
  AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")

Stage 6: summarize

| summarize 
    CommadCount = dcount(command), 
    EventCount = dcount(EventName), 
    commands = make_list(command), 
    Events = make_list(EventName) 
    by 
    bin(TimeGenerated, 1min), 
    RecipientAccountId, AccountName, AccountUPNSuffix, 
    UserIdentityUserName, 
    SourceIpAddress, 
    SessionMfaAuthenticated

Stage 7: where

| where CommadCount >= 8

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNamesummarize
AccountUPNSuffixsummarize
CommadCountsummarize
EventCountsummarize
Eventssummarize
RecipientAccountIdsummarize
SessionMfaAuthenticatedsummarize
SourceIpAddresssummarize
UserIdentityUserNamesummarize
commandssummarize

BloodHound Attack Path Finding - ForceChangePassword Privileges on Tier Zero Objects

#

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
1h
Source
github.com/Azure/Azure-Sentinel

BloodHound Attack Path Finding - ForceChangePassword Privileges on Tier Zero Objects

Rule Body

id: e426256f-eb14-4b05-9aad-f630944e7012
name: BloodHound Attack Path Finding - ForceChangePassword Privileges on Tier Zero
  Objects
description: BloodHound Attack Path Finding - ForceChangePassword Privileges on Tier
  Zero Objects
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BloodHoundEnterprise
    dataTypes:
      - BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: "BHEAttackPathsData_CL \n| where Finding == \"T0ForceChangePassword\""
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: domain_name
customDetails:
  Description: ShortDescription
  Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

BHEAttackPathsData_CL

Stage 2: where

| where Finding == "T0ForceChangePassword"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • T0ForceChangePassword
field:"Finding" kind:eq value:"T0ForceChangePassword"

BloodHound Attack Path Finding - Large Default Groups With Add Key Credential Link Privileges

#

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
1h
Source
github.com/Azure/Azure-Sentinel

BloodHound Attack Path Finding - Large Default Groups With Add Key Credential Link Privileges

Rule Body

id: 2f65df97-23a1-403b-93ea-6e478ee75c5a
name: BloodHound Attack Path Finding - Large Default Groups With Add Key Credential
  Link Privileges
description: BloodHound Attack Path Finding - Large Default Groups With Add Key Credential
  Link Privileges
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BloodHoundEnterprise
    dataTypes:
      - BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "LargeDefaultGroupsAddKeyCredentialLink"
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: domain_name
customDetails:
  Description: ShortDescription
  Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

BHEAttackPathsData_CL

Stage 2: where

| where Finding == "LargeDefaultGroupsAddKeyCredentialLink"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • LargeDefaultGroupsAddKeyCredentialLink
field:"Finding" kind:eq value:"LargeDefaultGroupsAddKeyCredentialLink"

BloodHound Attack Path Finding - Non-Tier Zero Principals With DCSync Privileges

#

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
1h
Source
github.com/Azure/Azure-Sentinel

BloodHound Attack Path Finding - Non-Tier Zero Principals With DCSync Privileges

Rule Body

id: d6bf4db8-f2f3-4738-9051-bb892b268da3
name: BloodHound Attack Path Finding - Non-Tier Zero Principals With DCSync Privileges
description: BloodHound Attack Path Finding - Non-Tier Zero Principals With DCSync
  Privileges
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BloodHoundEnterprise
    dataTypes:
      - BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: "BHEAttackPathsData_CL \n| where Finding == \"NonT0DCSyncers\""
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: domain_name
customDetails:
  Description: ShortDescription
  Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

BHEAttackPathsData_CL

Stage 2: where

| where Finding == "NonT0DCSyncers"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • NonT0DCSyncers
field:"Finding" kind:eq value:"NonT0DCSyncers"

CiscoISE - Certificate has expired

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

'Detects certificate expiration.'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Rule Body

id: 6107cba5-2974-4c22-8222-2a6f7bbea664
name: CiscoISE - Certificate has expired
description: |
  'Detects certificate expiration.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
relevantTechniques:
  - T1552
query: |
  let lbtime = 1h;
  CiscoISEEvent
  | where TimeGenerated > ago(lbtime)
  | where EventId == '60167'
  | project SrcIpAddr, DvcIpAddr, DvcHostname, DstUserName
  | extend HostCustomEntity = DvcHostname
  | extend IPCustomEntity = SrcIpAddr
  | extend AccountCustomEntity = DstUserName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: HostCustomEntity
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let lbtime = 1h;

Stage 1: source

CiscoISEEvent

Stage 2: where

| where TimeGenerated > ago(lbtime)

Stage 3: where

| where EventId == '60167'

Stage 4: project

| project SrcIpAddr, DvcIpAddr, DvcHostname, DstUserName

Stage 5: extend (3 consecutive steps)

| extend HostCustomEntity = DvcHostname
| extend IPCustomEntity = SrcIpAddr
| extend AccountCustomEntity = DstUserName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstUserNameproject
DvcHostnameproject
DvcIpAddrproject
SrcIpAddrproject
HostCustomEntityextend
IPCustomEntityextend
AccountCustomEntityextend

CyberArkEPM - MSBuild usage as LOLBin

#
Severity
medium
Time window
30m
Source
github.com/Azure/Azure-Sentinel

'Detects usage of msbuild tool as LOLBin.'

MITRE ATT&CK Coverage

Rule Body

id: a11bf869-458e-49fd-be03-58021b14be15
name: CyberArkEPM - MSBuild usage as LOLBin
description: |
  'Detects usage of msbuild tool as LOLBin.'
severity: Medium
requiredDataConnectors:
  - connectorId: CyberArkEPM
    dataTypes:
      - CyberArkEPM
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1127
query: | 
  CyberArkEPM
  | where EventSubType != 'AttackAttempt'
  | where ActingProcessName endswith @'msbuild.exe'
  | where ActingProcessCommandLine has_any ('xml', 'csproj', 'vcxproj', 'vbproj', 'fsproj')
  | extend AccountCustomEntity = ActorUsername
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

CyberArkEPM

Stage 2: where

| where EventSubType != 'AttackAttempt'

Stage 3: where

| where ActingProcessName endswith @'msbuild.exe'

Stage 4: where

| where ActingProcessCommandLine has_any ('xml', 'csproj', 'vcxproj', 'vbproj', 'fsproj')

Stage 5: extend

| extend AccountCustomEntity = ActorUsername

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActingProcessCommandLinematch
  • csproj transforms: term
  • fsproj transforms: term
  • vbproj transforms: term
  • vcxproj transforms: term
  • xml transforms: term
field:"ParentCommandLine" kind:match
ActingProcessNameends_with
  • msbuild.exe
field:"ParentImage" kind:ends_with value:"msbuild.exe"
EventSubTypene
  • AttackAttempt
field:"EventSubType" kind:ne value:"AttackAttempt"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend

CyberBlindSpot - Any Issue Detected

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

Generic alert that triggers when ANY CyberBlindSpot issue/incident is detected in the logs. Extracts nested metadata from RawPayload.

MITRE ATT&CK Coverage

Rule Body

id: abe1a662-d00d-482e-aa68-9394622ae02e
name: CyberBlindSpot - Any Issue Detected
description: Generic alert that triggers when ANY CyberBlindSpot issue/incident is detected in the logs. Extracts nested metadata from RawPayload.
severity: Informational
status: Available
requiredDataConnectors:
  - connectorId: CTM360CBSConnectorDefinition
    dataTypes:
      - CBS_MalwareLogs_AzureV2_CL
      - CBS_BreachedCredentials_AzureV2_CL
      - CBS_CompromisedCards_AzureV2_CL
      - CBS_DomainInfringement_AzureV2_CL
      - CBS_SubdomainInfringement_AzureV2_CL
      - CBSLog_AzureV2_CL
tactics:
  - Reconnaissance
  - Discovery
  - ResourceDevelopment
  - InitialAccess
relevantTechniques:
  - T1592
  - T1598
  - T1566
query: |
  CBSLog
  | project TimeGenerated, IncidentId, Subject, Severity, IncidentType, Status, Class, ExternalLink, Brand, FirstSeen, LastSeen, Remarks
eventGroupingSettings:
  aggregationKind: AlertPerResult
customDetails:
  Subject: Subject
  IncidentType: IncidentType
  Brand: Brand
alertDetailsOverride:
  alertDisplayNameFormat: CyberBlindSpot Alert - {{IncidentType}} on {{Subject}} - {{Brand}}
  alertDescriptionFormat: Type {{IncidentType}} | Subject {{Subject}} | Severity {{Severity}} 
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Subject
kind: Scheduled
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: PT5H
suppressionEnabled: false
version: 1.0.0

Stages and Predicates

Stage 1: source

CBSLog

Stage 2: project

| project TimeGenerated, IncidentId, Subject, Severity, IncidentType, Status, Class, ExternalLink, Brand, FirstSeen, LastSeen, Remarks

Output Fields

These fields are emitted when the rule matches.

FieldSource
Brandproject
Classproject
ExternalLinkproject
FirstSeenproject
IncidentIdproject
IncidentTypeproject
LastSeenproject
Remarksproject
Severityproject
Statusproject
Subjectproject
TimeGeneratedproject

Cyble Vision Alerts Compromised Endpoint Cookies

#

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
low
Time window
30m
Source
github.com/Azure/Azure-Sentinel

'Detects compromised browser cookies associated with monitored entities. Identifies exposed authentication cookies with future expiry, enabling potential session hijacking or persistent unauthorized access.'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access
Lateral Movement

Rule Body

id: 33B07EAA-F451-4C38-AC9F-8AF3F7E99F0E
name: Cyble Vision Alerts Compromised Endpoint Cookies
description: |
  'Detects compromised browser cookies associated with monitored entities. Identifies exposed authentication cookies with future expiry, enabling potential session hijacking or persistent unauthorized access.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
  dataTypes:
  - CybleVisionAlerts_CL
enabled: true
queryfrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: PT1H
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics:
- CredentialAccess
- DefenseEvasion
relevantTechniques:
- T1539
- T1550
query: |
  Alerts_compromised_endpoints_cookies
  | where Service == "compromised_endpoints_cookies"
  | extend MappedSeverity = Severity
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
alertDetailsOverride:
  alertDisplayNameFormat: Compromised Session Cookie {{CEC_Email}} {{CEC_URL}}
  alertDescriptionFormat: |
    A compromised browser session cookie associated with {{CEC_EmailDomain}} was detected for service {{CEC_URL}}. The cookie has not expired and may allow session hijacking. Session ID {{CEC_SessionID}}.
customDetails:
  MappedSeverity: Severity
  Status: Status
  AlertID: AlertID
  Service: Service
  CookieEmail: CEC_Email
  CookieDomain: CEC_EmailDomain
  CookieSessionID: CEC_SessionID
  CookieExpiry: CEC_Expiry
  CookieURL: CEC_URL
entityMappings:
- entityType: Url
  fieldMappings:
  - identifier: Url
    columnName: CEC_URL
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: CEC_Email
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

Alerts_compromised_endpoints_cookies

Stage 2: where

| where Service == "compromised_endpoints_cookies"

Stage 3: extend

| extend MappedSeverity = Severity

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Serviceeq
  • compromised_endpoints_cookies
field:"Service" kind:eq value:"compromised_endpoints_cookies"

Output Fields

These fields are emitted when the rule matches.

FieldSource
MappedSeverityextend

Darktrace System Status (Legacy)

#

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.

Severity
informational
Time window
5m
Source
github.com/Azure/Azure-Sentinel

'This rule creates Microsoft Sentinel Alerts based on Darktrace system status alerts for health monitoring, fetched every 5 minutes.'

MITRE ATT&CK Coverage

Rule Body

id: 2e629769-60eb-4a14-8bfc-bde9be66ebeb
name: Darktrace System Status (Legacy)
description: |
  'This rule creates Microsoft Sentinel Alerts based on Darktrace system status alerts for health monitoring, fetched every 5 minutes.'
severity: Informational
requiredDataConnectors:
  - connectorId: DarktraceRESTConnector
    dataTypes:
      - darktrace_model_alerts_CL
queryFrequency: 5m
queryPeriod: 5m 
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
  - Impact
relevantTechniques:
  - T1082
  - T1498
query: |
  darktrace_model_alerts_CL //anything starting with 'Dt' is not an ASIM mapping 
  | where dtProduct_s =="System Alert"
  | extend EventVendor="Darktrace", EventProduct="Darktrace DETECT"
  | project-rename ThreatCategory=dtProduct_s, EventStartTime=time_s, NetworkRuleName=friendlyName_s, SrcIpAddr=deviceIP_s, SrcHostname=hostname_s, ThreatRiskLevel=priority_code_d, ThreatRiskCategory=priority_s, DtSeverity=Severity, DtName=name_s, DtStatus=status_s, DtMessage=Message, DtURL=url_s, DtUUID=uuid_g
eventGroupingSettings:
  aggregationKind: AlertPerResult
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: SrcHostname
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
customDetails:
  EventStartTime: EventStartTime
  NetworkRuleName: NetworkRuleName
  ThreatRiskLevel: ThreatRiskLevel
  ThreatRiskCategory: ThreatRiskCategory
  DtName: DtName
  DtStatus: DtStatus
  DtMessage: DtMessage
  DtSeverity: DtSeverity
alertDetailsOverride:
  alertDisplayNameFormat: 'Darktrace: {{ThreatRiskLevel}} - {{NetworkRuleName}}'
  alertDescriptionFormat: '{{DtMessage}}'
  alertTacticsColumnName: # none
  alertSeverityColumnName: # none 
  alertDynamicProperties:
    - alertProperty: AlertLink
      value: DtURL
    - alertProperty: ProviderName
      value: EventVendor
    - alertProperty: ProductName
      value: EventProduct
    - alertProperty: ProductComponentName
      value: ThreatCategory
version: 1.1.1
kind: Scheduled

Stages and Predicates

Stage 1: source

darktrace_model_alerts_CL

Stage 2: where

| where dtProduct_s =="System Alert"

Stage 3: extend

| extend EventVendor="Darktrace", EventProduct="Darktrace DETECT"

Stage 4: project-rename

| project-rename ThreatCategory=dtProduct_s, EventStartTime=time_s, NetworkRuleName=friendlyName_s, SrcIpAddr=deviceIP_s, SrcHostname=hostname_s, ThreatRiskLevel=priority_code_d, ThreatRiskCategory=priority_s, DtSeverity=Severity, DtName=name_s, DtStatus=status_s, DtMessage=Message, DtURL=url_s, DtUUID=uuid_g

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
EventProductextend
EventVendorextend
DtMessageproject-rename
DtNameproject-rename
DtSeverityproject-rename
DtStatusproject-rename
DtURLproject-rename
DtUUIDproject-rename
EventStartTimeproject-rename
NetworkRuleNameproject-rename
SrcHostnameproject-rename
SrcIpAddrproject-rename
ThreatCategoryproject-rename
ThreatRiskCategoryproject-rename
ThreatRiskLevelproject-rename

Deleted a Custom Field Mapping profile

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

'Deleted a Custom Field Mapping profile from SOC Prime platform'

MITRE ATT&CK Coverage

TacticTechniques
Defense Impairment

Rule Body

id: a1b2c3d4-e5f6-4078-9012-3456789abcde
name: Deleted a Custom Field Mapping profile
description: |
  'Deleted a Custom Field Mapping profile from SOC Prime platform'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SOCPrimeAuditLogsDataConnector
    dataTypes:
      - SOCPrimeAuditLogs_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1562.001
query: |
  SOCPrimeAuditLogs_CL
  | where EventName == "Deleted a Custom Field Mapping profile"
  | project TimeGenerated, EventName, UserEmail, UserName, SourceIp, Uri, Type
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: UserName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIp
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

SOCPrimeAuditLogs_CL

Stage 2: where

| where EventName == "Deleted a Custom Field Mapping profile"

Stage 3: project

| project TimeGenerated, EventName, UserEmail, UserName, SourceIp, Uri, Type

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventNameeq
  • Deleted a Custom Field Mapping profile
field:"aws::eventName" kind:eq value:"Deleted a Custom Field Mapping profile"

Output Fields

These fields are emitted when the rule matches.

FieldSource
EventNameproject
SourceIpproject
TimeGeneratedproject
Typeproject
Uriproject
UserEmailproject
UserNameproject

Exchange AuditLog Disabled

#
Status
available
Severity
medium
Time window
1d
Group by
AdminAuditLogEnabledValue, ClientIP, Operation, Parameters, ResultStatus, UserId, UserType
Source
github.com/Azure/Azure-Sentinel

Identifies when the exchange audit logging has been disabled which may be an adversary attempt to evade detection or avoid other defenses.

MITRE ATT&CK Coverage

TacticTechniques
Defense Impairment

Telemetry Coverage

Rule Body

id: 194dd92e-d6e7-4249-85a5-273350a7f5ce
name: Exchange AuditLog Disabled
description: |
  'Identifies when the exchange audit logging has been disabled which may be an adversary attempt to evade detection or avoid other defenses.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Office365
    dataTypes:
      - OfficeActivity (Exchange)
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1562
query: |
  OfficeActivity
  | where OfficeWorkload =~ "Exchange" 
  | where UserType in~ ("Admin","DcAdmin")
  // Only admin or global-admin can disable audit logging
  | where Operation =~ "Set-AdminAuditLogConfig"
  | extend AdminAuditLogEnabledValue = tostring(parse_json(tostring(parse_json(tostring(array_slice(parse_json(Parameters),3,3)))[0])).Value)
  | where AdminAuditLogEnabledValue =~ "False"
  | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP, ResultStatus, Parameters, AdminAuditLogEnabledValue
  | extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId)
  | extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '')
  | extend AccountName = iff(UserId contains '\\', tostring(split(UserId, '\\')[1]), AccountName)
  | extend AccountNTDomain = iff(UserId contains '\\', tostring(split(UserId, '\\')[0]), '')
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserId
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountNTDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIP
version: 2.0.6
kind: Scheduled

Stages and Predicates

Stage 1: source

OfficeActivity

Stage 2: where

| where OfficeWorkload =~ "Exchange"

Stage 3: where

| where UserType in~ ("Admin","DcAdmin")

Stage 4: where

| where Operation =~ "Set-AdminAuditLogConfig"

Stage 5: extend

| extend AdminAuditLogEnabledValue = tostring(parse_json(tostring(parse_json(tostring(array_slice(parse_json(Parameters),3,3)))[0])).Value)

Stage 6: where

| where AdminAuditLogEnabledValue =~ "False"

Stage 7: summarize

| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), OperationCount = count() by Operation, UserType, UserId, ClientIP, ResultStatus, Parameters, AdminAuditLogEnabledValue

Stage 8: extend (4 consecutive steps)

| extend AccountName = iff(UserId contains '@', tostring(split(UserId, '@')[0]), UserId)
| extend AccountUPNSuffix = iff(UserId contains '@', tostring(split(UserId, '@')[1]), '')
| extend AccountName = iff(UserId contains '\\', tostring(split(UserId, '\\')[1]), AccountName)
| extend AccountNTDomain = iff(UserId contains '\\', tostring(split(UserId, '\\')[0]), '')
AccountName =
ifUserId contains "@"tostring(split(UserId, '@')[0])
elseUserId

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AdminAuditLogEnabledValuesummarize
ClientIPsummarize
EndTimeUtcsummarize
Operationsummarize
OperationCountsummarize
Parameterssummarize
ResultStatussummarize
StartTimeUtcsummarize
UserIdsummarize
UserTypesummarize
AccountNameextend
AccountUPNSuffixextend
AccountNTDomainextend

GreyNoise TI Map IP Entity to DnsEvents

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

This query maps any IP indicators of compromise (IOCs) from GreyNoise Threat Intelligence (TI), by searching for matches in DnsEvents.

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: ddf47b6f-870c-5712-a296-1383acb13c82
name: GreyNoise TI Map IP Entity to DnsEvents
version: 1.0.2
kind: Scheduled
description: |
  This query maps any IP indicators of compromise (IOCs) from GreyNoise Threat Intelligence (TI), by searching for matches in DnsEvents.
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: DNS
    dataTypes:
      - DnsEvents
  - connectorId: ASimDnsActivityLogs
    dataTypes:
      - DnsEvents
  - connectorId: GreyNoise2SentinelAPI
    dataTypes:
      - ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  let dt_lookBack = 1h; // Look back 1 hour for DNS events
  let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
  // Fetch threat intelligence indicators related to IP addresses
  let IP_Indicators = ThreatIntelligenceIndicator
    | where TimeGenerated >= ago(ioc_lookBack)
    | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
    | where Active == true and ExpirationDateTime > now()
    | where SourceSystem == 'GreyNoise'
    | where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
    | extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
    | extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
    | extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
    | where ipv4_is_private(TI_ipEntity) == false and  TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127.";
  // Perform a join between IP indicators and DNS events
  IP_Indicators
    // 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 (
        DnsEvents
        | where TimeGenerated >= ago(dt_lookBack)
        | where SubType =~ "LookupQuery" and isnotempty(IPAddresses)
        | mv-expand SingleIP = split(IPAddresses, ", ") to typeof(string)
        | extend DNS_TimeGenerated = TimeGenerated
    )
    on $left.TI_ipEntity == $right.SingleIP
    // Filter out DNS events that occurred after the expiration of the corresponding indicator
    | where DNS_TimeGenerated < ExpirationDateTime
    // Group the results by IndicatorId and SingleIP, and keep the DNS event with the latest timestamp
    | summarize DNS_TimeGenerated = arg_max(DNS_TimeGenerated, *) by IndicatorId, SingleIP
    // Select the desired output fields
    | project DNS_TimeGenerated, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, DomainName, ExpirationDateTime, ConfidenceScore,
      TI_ipEntity, Computer, EventId, SubType, ClientIP, Name, IPAddresses, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress, Type
    | extend timestamp = DNS_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: ClientIP
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url

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 8 define let IP_Indicators as the rule's main pipeline source. Stages 9 to 13 use it.

Stage 1: source

ThreatIntelligenceIndicator

Stage 2: where

| where TimeGenerated >= ago(ioc_lookBack)

Stage 3: summarize

| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId

Stage 4: where

| where Active == true and ExpirationDateTime > now()

Stage 5: where

| where SourceSystem == 'GreyNoise'

Stage 6: where

| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)

Stage 7: extend (3 consecutive steps)

| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
TI_ipEntity =
ifisnotempty(NetworkIP)NetworkIP
elseNetworkDestinationIP

Stage 8: where

| where ipv4_is_private(TI_ipEntity) == false and  TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."

Stage 9: join

IP_Indicators
| join kind=innerunique (
      DnsEvents
      | where TimeGenerated >= ago(dt_lookBack)
      | where SubType =~ "LookupQuery" and isnotempty(IPAddresses)
      | mv-expand SingleIP = split(IPAddresses, ", ") to typeof(string)
      | extend DNS_TimeGenerated = TimeGenerated
  )
  on $left.TI_ipEntity == $right.SingleIP

Stage 10: where

where DNS_TimeGenerated < ExpirationDateTime

Stage 11: summarize

summarize DNS_TimeGenerated by IndicatorId, SingleIP

Stage 12: project

project ActivityGroupNames, ClientIP, Computer, ConfidenceScore, DNS_TimeGenerated, Description, DomainName, EmailSourceIpAddress, EventId, ExpirationDateTime, IPAddresses, IndicatorId, Name, NetworkDestinationIP, NetworkIP, NetworkSourceIP, SubType, TI_ipEntity, ThreatType, Type, Url

Stage 13: extend

extend DnsDomain, HostName, timestamp

Indicators

These rows show field, operator, and value matches.

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
ClientIPproject
Computerproject
ConfidenceScoreproject
DNS_TimeGeneratedproject
Descriptionproject
DomainNameproject
EmailSourceIpAddressproject
EventIdproject
ExpirationDateTimeproject
IPAddressesproject
IndicatorIdproject
Nameproject
NetworkDestinationIPproject
NetworkIPproject
NetworkSourceIPproject
SubTypeproject
TI_ipEntityproject
ThreatTypeproject
Typeproject
Urlproject
DnsDomainextend
HostNameextend
timestampextend

McAfee ePO - Firewall disabled

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

Detects when firewall was disabled from Mctray.

MITRE ATT&CK Coverage

TacticTechniques
Defense Impairment
Command & Control

Telemetry Coverage

Rule Body

id: bd3cedc3-efba-455a-85bd-0cf9ac1b0727
name: McAfee ePO - Firewall disabled
description: |
  'Detects when firewall was disabled from Mctray.'
severity: Medium
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - DefenseEvasion
  - CommandAndControl
relevantTechniques:
  - T1562
  - T1071
query: |
  McAfeeEPOEvent
  | where EventId in ('35009')
  | extend IPCustomEntity = DvcIpAddr, HostCustomEntity = DvcHostname
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: HostCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

McAfeeEPOEvent

Stage 2: where

| where EventId in ('35009')

Stage 3: extend

| extend IPCustomEntity = DvcIpAddr, HostCustomEntity = DvcHostname

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
HostCustomEntityextend
IPCustomEntityextend

Mimecast Targeted Threat Protection - Impersonation Protect

#

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.

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

Detects a maliciously tagged impersonation

MITRE ATT&CK Coverage

TacticTechniques
Collection

Rule Body

id: d8e7eca6-4b59-4069-a31e-a022b2a12ea4
name: Mimecast Targeted Threat Protection - Impersonation Protect
description: Detects a maliciously tagged impersonation
severity: High
requiredDataConnectors:
  - connectorId: MimecastTTPAPI
    dataTypes:
      - MimecastTTPImpersonation_CL
enabled: true
query: MimecastTTPImpersonation_CL| where taggedMalicious_b == true;
queryFrequency: 5m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
suppressionDuration: 5h
suppressionEnabled: false
tactics:
- Exfiltration
- Collection
- Discovery
relevantTechniques:
- T1114
alertRuleTemplateName:
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1d
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult
entityMappings:
  - entityType: MailMessage
    fieldMappings:
      - identifier: Sender
        columnName: senderAddress_s
      - identifier: SenderIP
        columnName: senderIpAddress_s
      - identifier: Recipient
        columnName: recipientAddress_s
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

MimecastTTPImpersonation_CL

Stage 2: where

| where taggedMalicious_b == true;

Indicators

These rows show field, operator, and value matches.

NGINX - Multiple server errors from single IP address

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

'Detects multiple server errors from one source in short timeframe'

MITRE ATT&CK Coverage

Rule Body

id: b3ae0033-552e-4c3c-b493-3edffb4473bb
name: NGINX - Multiple server errors from single IP address
description: |
  'Detects multiple server errors from one source in short timeframe'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    dataTypes:
      - NGINX_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
  - InitialAccess
relevantTechniques:
  - T1498
  - T1190
  - T1133
query: |
  let threshold = 100;
  NGINXHTTPServer
  | where tolong(HttpStatusCode) >= 500 and tolong(HttpStatusCode) <= 599 
  | summarize MultipleServerErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)
  | where MultipleServerErrors > threshold
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 100;

Stage 1: source

NGINXHTTPServer

Stage 2: where

| where tolong(HttpStatusCode) >= 500 and tolong(HttpStatusCode) <= 599

Stage 3: summarize

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

Stage 4: where

| where MultipleServerErrors > threshold

Stage 5: extend

| extend IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
MultipleServerErrorssummarize
SrcIpAddrsummarize
IPCustomEntityextend

NordPass - Declined invitation

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

This will alert you when the user declines the invite to the NordPass organization.

MITRE ATT&CK Coverage

TacticTechniques
Stealth

Rule Body

id: 283d7506-f3c6-419a-ae9c-d9afe6a15d6d
name: NordPass - Declined invitation
version: 1.0.0
kind: Scheduled
displayName: Declined invitation
description: This will alert you when the user declines the invite to the NordPass organization.
severity: Low
query: |
  NordPassEventLogs_CL
  | where event_type == "invites"
  | where action == "user_invite_declined"
  | extend TargetEmail = user_email
requiredDataConnectors:
  - connectorId: NordPass
    dataTypes:
      - NordPassEventLogs_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1078
incidentConfiguration:
  createIncident: false
entityMappings:
  - entityType: Mailbox
    fieldMappings:
      - identifier: MailboxPrimaryAddress
        columnName: TargetEmail

Stages and Predicates

Stage 1: source

NordPassEventLogs_CL

Stage 2: where

| where event_type == "invites"

Stage 3: where

| where action == "user_invite_declined"

Stage 4: extend

| extend TargetEmail = user_email

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
TargetEmailextend

Ping Federate - Authentication from new IP.

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

'Detects authentication requests from new IP address.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 30583ed4-d13c-43b8-baf2-d75fbe727210
name: Ping Federate - Authentication from new IP.
description: |
  'Detects authentication requests from new IP address.'
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |
  let known_src = 
  PingFederateEvent
  | where TimeGenerated between (ago(14d) .. (1d))
  | where EventType in~ ('AUTHN_ATTEMPT', 'SSO')
  | where isnotempty(SrcIpAddr)
  | summarize makeset(SrcIpAddr);
  PingFederateEvent
  | where EventType in~ ('AUTHN_ATTEMPT', 'SSO')
  | where isnotempty(SrcIpAddr)
  | where SrcIpAddr !in (known_src)
  | 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_src used in Stage 4

let known_src = PingFederateEvent
| where TimeGenerated between (ago(14d) .. (1d))
| where EventType in~ ('AUTHN_ATTEMPT', 'SSO')
| where isnotempty(SrcIpAddr)
| summarize makeset(SrcIpAddr);

Stage 1: source

PingFederateEvent

Stage 2: where

| where EventType in~ ('AUTHN_ATTEMPT', 'SSO')

Stage 3: where

| where isnotempty(SrcIpAddr)

Stage 4: where

| where SrcIpAddr !in (known_src)

Stage 5: extend

| extend AccountCustomEntity = DstUserName

Stage 6: extend

| extend IpCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventTypein
  • AUTHN_ATTEMPT
  • SSO
field:"EventType" kind:in
SrcIpAddris_not_null
  • (no value, null check)
field:"src_ip" kind:is_not_null

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
SrcIpAddreqknown_srcexcludes:SrcIpAddr field:"SrcIpAddr" value:"known_src"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend
IpCustomEntityextend

Port Sweep

#
Status
available
Severity
medium
Time window
30s
Group by
DestinationPort, SourceIp
Source
github.com/Azure/Azure-Sentinel

'Identifies a source IP scanning same open ports on the Azure Firewall IPs. This can indicate malicious scanning of port by an attacker, trying to reveal IPs with specific ports open in the organization. The ports can be compromised by attackers for initial access, most often by exploiting vulnerability. Configurable Parameters: - Port sweep time - the time range to look for multiple hosts scanned. Default is set to 30 seconds. - Minimum different hosts threshold - alert only if more than this number of hosts scanned. Default is set to 200.'

MITRE ATT&CK Coverage

Rule Body

id: 720335f4-ee8c-4270-9424-d0859222168c
name: Port Sweep
description: |
  'Identifies a source IP scanning same open ports on the Azure Firewall IPs. This can indicate malicious scanning of port by an attacker, trying to reveal IPs with specific ports open in the organization. The ports can be compromised by attackers for initial access, most often by exploiting vulnerability.
  
  Configurable Parameters:
  
  - Port sweep time - the time range to look for multiple hosts scanned. Default is set to 30 seconds.
  - Minimum different hosts threshold - alert only if more than this number of hosts scanned. Default is set to 200.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: AzureFirewall
    dataTypes: 
      - AzureDiagnostics
      - AZFWApplicationRule
      - AZFWNetworkRule
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
  - Reconnaissance
relevantTechniques:
  - T1046
  - T1595.001
query: |
  let MinimumDifferentHostsThreshold = 200;
  let ExcludedPorts = dynamic([80 , 443]);
  let BinTime = 30s;
  union isfuzzy=true(
  AZFWApplicationRule
  | where DestinationPort !in (ExcludedPorts)
  | summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
  | where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
  | mv-expand Fqdn = AlertTimedCountHostsInBinTime),
  (AZFWNetworkRule
  | extend Fqdn = DestinationIp
  | where DestinationPort !in (ExcludedPorts)
  | summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
  | where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
  | mv-expand Fqdn = AlertTimedCountHostsInBinTime),
  (AzureDiagnostics
  | where OperationName == "AzureFirewallApplicationRuleLog" or OperationName == "AzureFirewallNetworkRuleLog"
  | parse msg_s with * "from " SourceIp ":" SourcePort:int " to " Fqdn ":" DestinationPort:int ". " * "Action: " Action "." *
  | where DestinationPort !in (ExcludedPorts)
  | where isnotempty(Fqdn) and isnotempty(SourceIp) and isnotempty(DestinationPort)
  | summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
  | where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
  | mv-expand Fqdn = AlertTimedCountHostsInBinTime)
  | project bin(TimeGenerated, BinTime), SourceIp, DestinationPort, AlertTimedCountHostsInBinTime, Fqdn
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIp
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Fqdn
customDetails:
  DestinationPort: DestinationPort
alertDetailsOverride:
  alertDisplayNameFormat: 'Port Sweep Detected from {{SourceIp}} on Port {{DestinationPort}}'
  alertDescriptionFormat: 'Source IP {{SourceIp}} has scanned {{AlertTimedCountHostsInBinTime}} different hosts on port {{DestinationPort}} within a short time frame, which may indicate reconnaissance activity.'
version: 1.2.3
kind: Scheduled

Stages and Predicates

Parameters

let MinimumDifferentHostsThreshold = 200;
let ExcludedPorts = dynamic([80 , 443]);
let BinTime = 30s;

Union isfuzzy=true (3 Sources)

Each leg below queries one source; the rule matches if any leg does. Sources: AZFWApplicationRule, AZFWNetworkRule, AzureDiagnostics

Leg 1: AZFWApplicationRule

AZFWApplicationRule
| where DestinationPort !in (ExcludedPorts)
| summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
| where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
| mv-expand Fqdn = AlertTimedCountHostsInBinTime

Leg 2: AZFWNetworkRule

AZFWNetworkRule
| extend Fqdn = DestinationIp
| where DestinationPort !in (ExcludedPorts)
| summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
| where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
| mv-expand Fqdn = AlertTimedCountHostsInBinTime

Leg 3: AzureDiagnostics

AzureDiagnostics
| where OperationName == "AzureFirewallApplicationRuleLog" or OperationName == "AzureFirewallNetworkRuleLog"
| parse msg_s with * "from " SourceIp ":" SourcePort:int " to " Fqdn ":" DestinationPort:int ". " * "Action: " Action "." *
| where DestinationPort !in (ExcludedPorts)
| where isnotempty(Fqdn) and isnotempty(SourceIp) and isnotempty(DestinationPort)
| summarize AlertTimedCountHostsInBinTime = make_set(Fqdn) by SourceIp, bin(TimeGenerated, BinTime), DestinationPort
| where array_length(AlertTimedCountHostsInBinTime) > MinimumDifferentHostsThreshold
| mv-expand Fqdn = AlertTimedCountHostsInBinTime

Applied to the Combined Result

| project bin(TimeGenerated, BinTime), SourceIp, DestinationPort, AlertTimedCountHostsInBinTime, Fqdn

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
DestinationPortis_not_null
  • (no value, null check)
field:"DestinationPort" kind:is_not_null
Fqdnis_not_null
  • (no value, null check)
field:"Fqdn" kind:is_not_null
OperationNameeq
  • AzureFirewallApplicationRuleLog
  • AzureFirewallNetworkRuleLog
field:"OperationName" kind:eq
SourceIpis_not_null
  • (no value, null check)
field:"src_ip" kind:is_not_null

Exclusions

The rule actively suppresses these predicates.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AlertTimedCountHostsInBinTimeproject
DestinationPortproject
Fqdnproject
SourceIpproject

Potential re-named sdelete usage (ASIM Version)

#
Severity
low
Time window
1h
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

This detection looks for command line parameters associated with the use of Sysinternals sdelete (https://docs.microsoft.com/sysinternals/downloads/sdelete) to delete multiple files on a host's C drive. A threat actor may re-name the tool to avoid detection and then use it for destructive attacks on a host. This detection uses the ASIM imProcess parser, this will need to be deployed before use - https://docs.microsoft.com/azure/sentinel/normalization

MITRE ATT&CK Coverage

TacticTechniques
Stealth
Impact

Telemetry Coverage

Rule Body

id: 5b6ae038-f66e-4f74-9315-df52fd492be4
name: Potential re-named sdelete usage (ASIM Version)
description: |
  'This detection looks for command line parameters associated with the use of Sysinternals sdelete (https://docs.microsoft.com/sysinternals/downloads/sdelete) to delete multiple files on a host's C drive.
  A threat actor may re-name the tool to avoid detection and then use it for destructive attacks on a host.
  This detection uses the ASIM imProcess parser, this will need to be deployed before use - https://docs.microsoft.com/azure/sentinel/normalization'
severity: Low
requiredDataConnectors: []
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
  - Impact
relevantTechniques:
  - T1485
  - T1036
query: |
  imProcess
  | where CommandLine has_all ("accepteula", "-s", "-r", "-q")
  | where Process !endswith "sdelete.exe"
  | where CommandLine !has "sdelete"
  | extend AccountName = tostring(split(ActorUsername, @'\')[1]), AccountNTDomain = tostring(split(ActorUsername, @'\')[0])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ActorUsername
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountNTDomain
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Dvc
      - identifier: HostName
        columnName: DvcHostname
      - identifier: DnsDomain
        columnName: DvcDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: DvcIpAddr
version: 1.0.6
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Threat Protection" ]

Stages and Predicates

Stage 1: source

imProcess

Stage 2: where

| where CommandLine has_all ("accepteula", "-s", "-r", "-q")

Stage 3: where

| where Process !endswith "sdelete.exe"

Stage 4: where

| where CommandLine !has "sdelete"

Stage 5: extend

| extend AccountName = tostring(split(ActorUsername, @'\')[1]), AccountNTDomain = tostring(split(ActorUsername, @'\')[0])

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CommandLinematch
  • -q transforms: term corpus 2 (kusto 2)
  • -r transforms: term corpus 2 (kusto 2)
  • -s transforms: term corpus 2 (kusto 2)
  • accepteula transforms: term corpus 3 (kusto 3)
field:"CommandLine" kind:match

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
Processends_withsdelete.exeexcludes:Process field:"Process" value:"sdelete.exe"
CommandLinematchsdeleteexcludes:CommandLine field:"CommandLine" value:"sdelete"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNTDomainextend
AccountNameextend

Sentinel One - Admin login from new location

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

'Detects admin user login from new location (IP address).'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Privilege Escalation

Rule Body

id: 382f37b3-b49a-492f-b436-a4717c8c5c3e
name: Sentinel One - Admin login from new location
description: |
  'Detects admin user login from new location (IP address).'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SentinelOne
    dataTypes:
      - SentinelOne
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - PrivilegeEscalation
relevantTechniques:
  - T1078
query: | 
  let lback_period = 14d;
  let lback_time = 1h;
  SentinelOne
  | where TimeGenerated between(ago(lback_period)..ago(lback_time))
  | where ActivityType == 27
  | where DataRole =~ 'Admin'
  | extend SrcIpAddr = extract(@'Address\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', 1, EventOriginalMessage)
  | where isnotempty(SrcIpAddr)
  | summarize ip_lst = makeset(SrcIpAddr) by SrcUserName
  | join (SentinelOne
          | where ActivityType == 27
          | where DataRole =~ 'Admin'
          | extend SrcIpAddr = extract(@'Address\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', 1, EventOriginalMessage)
          | where isnotempty(SrcIpAddr)) on SrcUserName
  | where ip_lst !has SrcIpAddr
  | extend AccountCustomEntity = SrcUserName, IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.1
kind: Scheduled

Stages and Predicates

Parameters

let lback_period = 14d;
let lback_time = 1h;

Stage 1: source

SentinelOne

Stage 2: where

| where TimeGenerated between(ago(lback_period)..ago(lback_time))

Stage 3: where

| where ActivityType == 27

Stage 4: where

| where DataRole =~ 'Admin'

Stage 5: extend

| extend SrcIpAddr = extract(@'Address\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', 1, EventOriginalMessage)

Stage 6: where

| where isnotempty(SrcIpAddr)

Stage 7: summarize

| summarize ip_lst = makeset(SrcIpAddr) by SrcUserName

Stage 8: join

| join (SentinelOne
        | where ActivityType == 27
        | where DataRole =~ 'Admin'
        | extend SrcIpAddr = extract(@'Address\s(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', 1, EventOriginalMessage)
        | where isnotempty(SrcIpAddr)) on SrcUserName

Stage 9: where

where ip_lst =~ SrcIpAddr

Stage 10: extend

extend AccountCustomEntity, IPCustomEntity

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
SrcUserNamesummarize
ip_lstsummarize
AccountCustomEntityextend
IPCustomEntityextend

SpyCloud infostealer malware credential exposure

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

'Identifies credentials harvested by infostealer malware on a compromised device. This exposure may indicate an actively compromised endpoint with associated command-and-control risk.'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access
Command & Control

Rule Body

id: ead4deed-9d48-4646-aee0-6b46c2dd1ae6
name: SpyCloud infostealer malware credential exposure
description: |
  'Identifies credentials harvested by infostealer malware on a compromised device. This
  exposure may indicate an actively compromised endpoint with associated command-and-control
  risk.'
severity: High
requiredDataConnectors:
  - connectorId: SpyCloudEnterpriseProtectionCCF
    dataTypes:
      - SpyCloudBreachWatchlistV2_CL
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - CommandAndControl
relevantTechniques:
  - T1555
  - T1071
query: |
  // Human-readable constant for the severity threshold used below
  let severityThreshold = 25;
  SpyCloudBreachWatchlistV2_CL
  | where Severity == severityThreshold
  | mv-expand IPAddress = IPAddresses to typeof(string)
  | extend AccountName = Username, AccountEmail = Email, HostName = UserHostName
  | project TimeGenerated, DocumentId, SourceId, SpyCloudPublishDate, AccountEmail,
      AccountName, HostName, InfectedMachineId, InfectedTime, UserOS, IPAddress
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
version: 1.0.0
kind: scheduled

Stages and Predicates

Parameters

let severityThreshold = 25;

Stage 1: source

SpyCloudBreachWatchlistV2_CL

Stage 2: where

| where Severity == severityThreshold

Stage 3: mv-expand

| mv-expand IPAddress = IPAddresses to typeof(string)

Stage 4: extend

| extend AccountName = Username, AccountEmail = Email, HostName = UserHostName

Stage 5: project

| project TimeGenerated, DocumentId, SourceId, SpyCloudPublishDate, AccountEmail,
    AccountName, HostName, InfectedMachineId, InfectedTime, UserOS, IPAddress

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountEmailproject
AccountNameproject
DocumentIdproject
HostNameproject
IPAddressproject
InfectedMachineIdproject
InfectedTimeproject
SourceIdproject
SpyCloudPublishDateproject
TimeGeneratedproject
UserOSproject

Tailscale: User role elevated to admin or owner

#
Status
available
Severity
high
Time window
2d
Group by
UserId
Source
github.com/Azure/Azure-Sentinel

Identifies when a user's tailnet role changes from a lower-privilege role to admin, network-admin, or owner between consecutive snapshots. Privilege escalation is a high-value attacker objective and warrants prompt review.

MITRE ATT&CK Coverage

Rule Body

id: d3c4e5f6-3456-7890-12ab-cdef12345003
name: "Tailscale: User role elevated to admin or owner"
description: |
  Identifies when a user's tailnet role changes from a lower-privilege role to admin, network-admin, or owner between consecutive snapshots. Privilege escalation is a high-value attacker objective and warrants prompt review.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: TailscaleCCF
    dataTypes:
      - Tailscale_Users_CL
queryFrequency: 1h
queryPeriod: 2d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - PrivilegeEscalation
  - Persistence
relevantTechniques:
  - T1078
  - T1098
query: |
  let elevated = dynamic(["admin", "network-admin", "owner"]);
  let recent =
      Tailscale_Users_CL
      | where TimeGenerated > ago(1h)
      | summarize arg_max(TimeGenerated, *) by UserId
      | where Role in (elevated)
      | project UserId, LoginName, RoleNow = Role;
  let prior =
      Tailscale_Users_CL
      | where TimeGenerated between (ago(2d) .. ago(1h))
      | summarize arg_max(TimeGenerated, *) by UserId
      | project UserId, RolePrior = Role;
  recent
  | join kind=inner prior on UserId
  | where RoleNow != RolePrior
  | where RolePrior !in (elevated)
  | project TimeGenerated = now(), UserId, LoginName, RolePrior, RoleNow
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: LoginName
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: P1D
    matchingMethod: AllEntities
kind: Scheduled
version: 1.0.0

Stages and Predicates

Parameters

let elevated = dynamic(["admin", "network-admin", "owner"]);

let recent is inlined into the numbered stages below.

Let Binding: prior used in Stage 6

let prior = Tailscale_Users_CL
    | where TimeGenerated between (ago(2d) .. ago(1h))
    | summarize arg_max(TimeGenerated, *) by UserId
    | project UserId, RolePrior = Role;

Stages 1 to 5 define let recent as the rule's main pipeline source. Stages 6 to 9 use it.

Stage 1: source

Tailscale_Users_CL

Stage 2: where

| where TimeGenerated > ago(1h)

Stage 3: summarize

| summarize arg_max(TimeGenerated, *) by UserId

Stage 4: where

| where Role in (elevated)

Stage 5: project

| project UserId, LoginName, RoleNow = Role

Stage 6: join

recent
| join kind=inner prior on UserId

Stage 7: where

where RoleNow != RolePrior

Stage 8: where

where not (RolePrior in ("admin", "network-admin", "owner"))

Stage 9: project

project LoginName, RoleNow, RolePrior, TimeGenerated, UserId

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Rolein
  • admin
  • network-admin
  • owner
field:"Role" kind:in

Exclusions

The rule actively suppresses these predicates.

Output Fields

These fields are emitted when the rule matches.

FieldSource
LoginNameproject
RoleNowproject
RolePriorproject
TimeGeneratedproject
UserIdproject

Back to all Kusto rules