Detection rules › Kusto

Kusto rules: 0e

Accessed files shared by temporary external user

#
Status
available
Severity
low
Time window
1h
Group by
MemberAdded, OfficeObjectId
Source
github.com/Azure/Azure-Sentinel

This detection identifies when an external user is added to a Team or Teams chat and shares a file which is accessed by many users (>10) and the users is removed within short period of time. This might be an indicator of suspicious activity.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: bff058b2-500e-4ae5-bb49-a5b1423cbd5b
name: Accessed files shared by temporary external user
description: |
  'This detection identifies when an external user is added to a Team or Teams chat and shares a file which is accessed by many users (>10) and the users is removed within short period of time. This might be an indicator of suspicious activity.'
severity: Low
status: Available 
requiredDataConnectors:
  - connectorId: Office365
    dataTypes:
      - OfficeActivity (Teams)
  - connectorId: Office365
    dataTypes:
      - OfficeActivity (SharePoint)
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1566
query: |
  let fileAccessThrehold = 10;
  OfficeActivity
  | where OfficeWorkload =~ "MicrosoftTeams"
  | where Operation =~ "MemberAdded"
  | extend MemberAdded = tostring(parse_json(Members)[0].UPN)
  | where MemberAdded contains ("#EXT#")
  | project TimeAdded=TimeGenerated, Operation, MemberAdded, UserWhoAdded = UserId, TeamName
  | join kind = inner (
    OfficeActivity
    | where OfficeWorkload =~ "MicrosoftTeams"
    | where Operation =~ "MemberRemoved"
    | extend MemberAdded = tostring(parse_json(Members)[0].UPN)
    | where MemberAdded contains ("#EXT#")
    | project TimeDeleted=TimeGenerated, Operation, MemberAdded, UserWhoDeleted = UserId, TeamName
    ) on MemberAdded
  | where TimeDeleted > TimeAdded
  | join kind=inner (
    OfficeActivity
    | where RecordType == "SharePointFileOperation"
    | where SourceRelativeUrl has "Microsoft Teams Chat Files"
    | where Operation == "FileUploaded"
    | extend MemberAdded = UserId
    | join kind = inner (
        OfficeActivity
        | where RecordType == "SharePointFileOperation"
        | where Operation  == "FileAccessed"
        | where SourceRelativeUrl has "Microsoft Teams Chat Files"
        | summarize FileAccessCount = count() by OfficeObjectId
        | where FileAccessCount > fileAccessThrehold
        ) on $left.OfficeObjectId == $right.OfficeObjectId
    )on MemberAdded
  | project-away MemberAdded1, MemberAdded2, OfficeObjectId1, Operation1, Operation2, TeamName1, TeamName2
  | extend MemberAddedAccountName = tostring(split(MemberAdded, "@")[0]), MemberAddedAccountUPNSuffix = tostring(split(MemberAdded, "@")[1])
  | extend UserWhoAddedAccountName = tostring(split(UserWhoAdded, "@")[0]), UserWhoAddedAccountUPNSuffix = tostring(split(UserWhoAdded, "@")[1])
  | extend UserWhoDeletedAccountName = tostring(split(UserWhoDeleted, "@")[0]), UserWhoDeletedAccountUPNSuffix = tostring(split(UserWhoDeleted, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: MemberAdded
      - identifier: Name
        columnName: MemberAddedAccountName
      - identifier: UPNSuffix
        columnName: MemberAddedAccountUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserWhoAdded
      - identifier: Name
        columnName: UserWhoAddedAccountName
      - identifier: UPNSuffix
        columnName: UserWhoAddedAccountUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserWhoDeleted
      - identifier: Name
        columnName: UserWhoDeletedAccountName
      - identifier: UPNSuffix
        columnName: UserWhoDeletedAccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIP
version: 2.1.1
kind: Scheduled

Stages and Predicates

Parameters

let fileAccessThrehold = 10;

Stage 1: source

OfficeActivity

Stage 2: where

| where OfficeWorkload =~ "MicrosoftTeams"

Stage 3: where

| where Operation =~ "MemberAdded"

Stage 4: extend

| extend MemberAdded = tostring(parse_json(Members)[0].UPN)

Stage 5: where

| where MemberAdded contains ("#EXT#")

Stage 6: project

| project TimeAdded=TimeGenerated, Operation, MemberAdded, UserWhoAdded = UserId, TeamName

Stage 7: join

| join kind = inner (
  OfficeActivity
  | where OfficeWorkload =~ "MicrosoftTeams"
  | where Operation =~ "MemberRemoved"
  | extend MemberAdded = tostring(parse_json(Members)[0].UPN)
  | where MemberAdded contains ("#EXT#")
  | project TimeDeleted=TimeGenerated, Operation, MemberAdded, UserWhoDeleted = UserId, TeamName
  ) on MemberAdded

Stage 8: where

where TimeDeleted > TimeAdded

Stage 9: join

join kind=inner (OfficeActivity) on MemberAdded

Stage 10: project-away

project-away MemberAdded1, MemberAdded2, OfficeObjectId1, Operation1, Operation2, TeamName1, TeamName2

Stage 11: extend (3 consecutive steps)

extend MemberAddedAccountName, MemberAddedAccountUPNSuffix, UserWhoAddedAccountName, UserWhoAddedAccountUPNSuffix, UserWhoDeletedAccountName, UserWhoDeletedAccountUPNSuffix

Stage 12: summarize aggregation inside the join branch

summarize by OfficeObjectId

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
OfficeObjectIdsummarize

AWSCloudTrail - Created CRUD S3 policy and then privilege escalation

#
Status
available
Severity
medium
Time window
1d
Group by
AccountName, AccountUPNSuffix, EventName, EventSource, PolicyName, RecipientAccountId, SourceIpAddress, UserIdentityArn, UserIdentityType
Source
github.com/Azure/Azure-Sentinel

Identifies creation of IAM policies that grant broad Amazon S3 CRUD permissions, followed by policy attachment activity to users, roles, or groups. This sequence can indicate attempts to expand access and should be investigated as potential cloud privilege escalation.

MITRE ATT&CK Coverage

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 467cbe7e-e6d4-4f4e-8e44-84dd01932c32
name: AWSCloudTrail - Created CRUD S3 policy and then privilege escalation
description: |
  Identifies creation of IAM policies that grant broad Amazon S3 CRUD permissions, followed by policy attachment activity to users, roles, or groups. This sequence can indicate attempts to
  expand access and should be investigated as potential cloud privilege escalation.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
  - PrivilegeEscalation
  - Persistence
relevantTechniques:
  - T1484
  - T1098.003
query: |
  let EventNameList = dynamic(["AttachUserPolicy","AttachRolePolicy","AttachGroupPolicy"]);
  let createPolicy =  dynamic(["CreatePolicy", "CreatePolicyVersion"]);
  let timeframe = 1d;
  let lookback = 14d;
  // Creating Master table with all the events to use with materialize for better performance
  let EventInfo = AWSCloudTrail
  | where TimeGenerated >= ago(lookback)
  | where EventName in (EventNameList) or EventName in (createPolicy)
  | 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]), "");
  //Checking for Policy creation event with Full Admin Privileges since lookback period.
  let FullAdminPolicyEvents =  materialize(  EventInfo
  | where TimeGenerated >= ago(lookback)
  | where EventName in (createPolicy)
  | extend PolicyName = tostring(parse_json(RequestParameters).policyName)
  | extend Statement = parse_json(tostring((parse_json(RequestParameters).policyDocument))).Statement
  | mvexpand Statement
  | extend Action = parse_json(Statement).Action , Effect = tostring(parse_json(Statement).Effect), Resource = tostring(parse_json(Statement).Resource), Condition = tostring(parse_json(Statement).Condition)
  | extend Action = tostring(Action)
  | where Effect =~ "Allow" and (Action contains "s3:Create" and Action contains "s3:Get" and Action contains "s3:Put" and Action contains "s3:Delete") and Resource == "*" and Condition == ""
  | distinct TimeGenerated, EventName, PolicyName, SourceIpAddress, UserIdentityArn, RecipientAccountId, AccountName, AccountUPNSuffix
  | project-rename StartTime = TimeGenerated  );
  let PolicyAttach = materialize(  EventInfo
  | where TimeGenerated >= ago(timeframe)
  | where EventName in (EventNameList) and isempty(ErrorCode) and isempty(ErrorMessage)
  | extend PolicyName = tostring(split(tostring(parse_json(RequestParameters).policyArn),"/")[1])
  | summarize AttachEventCount=count(), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by EventSource, EventName, UserIdentityType , UserIdentityArn, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, PolicyName
  | extend AttachEvent = pack("StartTime", StartTime, "EndTime", EndTime, "EventName", EventName, "UserIdentityType",   UserIdentityType, "SourceIpAddress", SourceIpAddress, "AccountName", AccountName, "AccountUPNSuffix", AccountUPNSuffix, "RecipientAccountId", RecipientAccountId, "UserIdentityArn", UserIdentityArn)
  | project EventSource, PolicyName, AttachEvent, AttachEventCount, RecipientAccountId, AccountName, AccountUPNSuffix
  );
  // Joining the list of PolicyNames and checking if it has been attached to any Roles/Users/Groups.
  // These Roles/Users/Groups will be Privileged and can be used by adversaries as pivot point for privilege escalation via multiple ways.
  FullAdminPolicyEvents
  | join kind=leftouter
  (
      PolicyAttach
  )
  on PolicyName
  | project-away PolicyName1
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
      - identifier: CloudAppAccountId
        columnName: RecipientAccountId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIpAddress
customDetails:
  PolicyName: PolicyName
  EventName: EventName
  RecipientAccountId: RecipientAccountId
  UserIdentityArn: UserIdentityArn
alertDetailsOverride:
  alertDisplayNameFormat: 'AWS S3 privilege escalation policy activity: {{PolicyName}} by {{AccountName}}'
  alertDescriptionFormat: 'Detected {{EventName}} for policy {{PolicyName}} in account {{RecipientAccountId}}.'
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let EventNameList = dynamic(["AttachUserPolicy","AttachRolePolicy","AttachGroupPolicy"]);
let createPolicy = dynamic(["CreatePolicy", "CreatePolicyVersion"]);
let timeframe = 1d;
let lookback = 14d;

let EventInfo and let FullAdminPolicyEvents are inlined into the numbered stages below.

Let Binding: PolicyAttach used in Stage 15

let PolicyAttach = materialize(  EventInfo
| where TimeGenerated >= ago(timeframe)
| where EventName in (EventNameList) and isempty(ErrorCode) and isempty(ErrorMessage)
| extend PolicyName = tostring(split(tostring(parse_json(RequestParameters).policyArn),"/")[1])
| summarize AttachEventCount=count(), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by EventSource, EventName, UserIdentityType , UserIdentityArn, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, PolicyName
| extend AttachEvent = pack("StartTime", StartTime, "EndTime", EndTime, "EventName", EventName, "UserIdentityType",   UserIdentityType, "SourceIpAddress", SourceIpAddress, "AccountName", AccountName, "AccountUPNSuffix", AccountUPNSuffix, "RecipientAccountId", RecipientAccountId, "UserIdentityArn", UserIdentityArn)
| project EventSource, PolicyName, AttachEvent, AttachEventCount, RecipientAccountId, AccountName, AccountUPNSuffix
);

Stages 1 to 14 define let FullAdminPolicyEvents as the rule's main pipeline source. Stages 15 to 17 use it.

Stage 1: source

AWSCloudTrail

Stage 2: where

| where TimeGenerated >= ago(lookback)

Stage 3: where

| where EventName in (EventNameList) or EventName in (createPolicy)

Stage 4: 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 5: where

| where TimeGenerated >= ago(lookback)

Stage 6: where

| where EventName in (createPolicy)

Stage 7: extend

| extend PolicyName = tostring(parse_json(RequestParameters).policyName)

Stage 8: extend

| extend Statement = parse_json(tostring((parse_json(RequestParameters).policyDocument))).Statement

Stage 9: mv-expand

| mvexpand Statement

Stage 10: extend

| extend Action = parse_json(Statement).Action , Effect = tostring(parse_json(Statement).Effect), Resource = tostring(parse_json(Statement).Resource), Condition = tostring(parse_json(Statement).Condition)

Stage 11: extend

| extend Action = tostring(Action)

Stage 12: where

| where Effect =~ "Allow" and (Action contains "s3:Create" and Action contains "s3:Get" and Action contains "s3:Put" and Action contains "s3:Delete") and Resource == "*" and Condition == ""

Stage 13: distinct

| distinct TimeGenerated, EventName, PolicyName, SourceIpAddress, UserIdentityArn, RecipientAccountId, AccountName, AccountUPNSuffix

Stage 14: project-rename

| project-rename StartTime = TimeGenerated

Stage 15: join

FullAdminPolicyEvents
| join kind=leftouter
(
    PolicyAttach
)
on PolicyName

Stage 16: project-away

project-away PolicyName1

Stage 17: summarize aggregation inside the join branch

summarize by EventSource, EventName, UserIdentityType, UserIdentityArn, SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, PolicyName

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Actioncontains
  • s3:Create
  • s3:Delete
  • s3:Get
  • s3:Put
field:"Action" kind:contains
Effecteq
  • Allow
field:"Effect" kind:eq value:"Allow"
ErrorCodeis_null
  • (no value, null check)
field:"ErrorCode" kind:is_null
ErrorMessageis_null
  • (no value, null check)
field:"ErrorMessage" kind:is_null
EventNamein
  • AttachGroupPolicy corpus 17 (kusto 15, panther 2)
  • AttachGroupPolicy
  • AttachRolePolicy corpus 17 (kusto 15, panther 2)
  • AttachRolePolicy
  • AttachUserPolicy corpus 17 (kusto 15, panther 2)
  • AttachUserPolicy
  • CreatePolicy corpus 14 (kusto 12, panther 2)
  • CreatePolicy
  • CreatePolicyVersion corpus 14 (kusto 12, panther 2)
  • CreatePolicyVersion
field:"aws::eventName" kind:in
Resourceeq
  • *
field:"Resource" kind:eq value:"*"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNamesummarize
AccountUPNSuffixsummarize
EventNamesummarize
EventSourcesummarize
PolicyNamesummarize
RecipientAccountIdsummarize
SourceIpAddresssummarize
UserIdentityArnsummarize
UserIdentityTypesummarize

BloodHound Attack Path Finding - SyncLapsPassword 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 - SyncLapsPassword Privileges on Tier Zero Objects

Rule Body

id: 31d40f68-8bc5-44f0-bf8c-c9c17d9ef1c8
name: BloodHound Attack Path Finding - SyncLapsPassword Privileges on Tier Zero Objects
description: BloodHound Attack Path Finding - SyncLapsPassword 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 | where Finding == "T0SyncLAPSPassword"
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 == "T0SyncLAPSPassword"

Indicators

These rows show field, operator, and value matches.

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

Cisco Umbrella - Connection to Unpopular Website Detected

#
Severity
medium
Time window
14d
Source
github.com/Azure/Azure-Sentinel

'Detects first connection to an unpopular website (possible malicious payload delivery).'

MITRE ATT&CK Coverage

TacticTechniques
Command & ControlNo specific technique

Rule Body

id: 75297f62-10a8-4fc1-9b2a-12f25c6f05a7
name: Cisco Umbrella - Connection to Unpopular Website Detected
description: |
  'Detects first connection to an unpopular website (possible malicious payload delivery).'
severity: Medium
requiredDataConnectors:
  - connectorId: CiscoUmbrellaDataConnector
    dataTypes:
      - Cisco_Umbrella_proxy_CL
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
query: |
  let domain_lookBack= 14d;
  let timeframe = 1d;
  let top_million_list = Cisco_Umbrella
  | where EventType == "proxylogs"
  | where TimeGenerated > ago(domain_lookBack) and TimeGenerated < ago(timeframe)
  | extend Hostname = parse_url(UrlOriginal)["Host"]
  | summarize count() by tostring(Hostname)
  | top 1000000 by count_
  | summarize make_list(Hostname);
  Cisco_Umbrella
  | where EventType == "proxylogs"
  | where TimeGenerated > ago(timeframe)
  | extend Hostname = parse_url(UrlOriginal)["Host"]
  | where Hostname !in (top_million_list)
  | extend Message = "Connect to unpopular website (possible malicious payload delivery)"
  | project Message, SrcIpAddr, DstIpAddr,UrlOriginal, TimeGenerated
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: UrlOriginal
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
version: 1.1.2
kind: Scheduled

Stages and Predicates

Parameters

let domain_lookBack = 14d;
let timeframe = 1d;

Let Binding: top_million_list used in Stage 5

let top_million_list = Cisco_Umbrella
| where EventType == "proxylogs"
| where TimeGenerated > ago(domain_lookBack) and TimeGenerated < ago(timeframe)
| extend Hostname = parse_url(UrlOriginal)["Host"]
| summarize count() by tostring(Hostname)
| top 1000000 by count_
| summarize make_list(Hostname);

Stage 1: source

Cisco_Umbrella

Stage 2: where

| where EventType == "proxylogs"

Stage 3: where

| where TimeGenerated > ago(timeframe)

Stage 4: extend

| extend Hostname = parse_url(UrlOriginal)["Host"]

Stage 5: where

| where Hostname !in (top_million_list)

Stage 6: extend

| extend Message = "Connect to unpopular website (possible malicious payload delivery)"

Stage 7: project

| project Message, SrcIpAddr, DstIpAddr,UrlOriginal, TimeGenerated

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
Hostnameeqtop_million_listexcludes:Hostname field:"Hostname" value:"top_million_list"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstIpAddrproject
Messageproject
SrcIpAddrproject
TimeGeneratedproject
UrlOriginalproject

Common Event Format (CEF) via AMA - Critical Severity Detection

#
Status
available
Severity
high
Time window
1h
Group by
Activity, DstHostName, DstUserName, FileHash, FileName, FilePath, Message, SrcIpAddr, Technique
Source
github.com/Azure/Azure-Sentinel

'Creates an incident when a CrowdStrike Falcon sensor detection is triggered with Critical severity. The rule queries CrowdStrikeFalconEventStream for DetectionSummaryEvent records where Severity is Critical, summarizes detections by host, source IP, user, activity, technique, file details, hash, and message, and raises an incident for each matching result.'

MITRE ATT&CK Coverage

Rule Body

id: f7d298b2-726c-42a5-bbac-0d7f9950f527
name: Common Event Format (CEF) via AMA - Critical Severity Detection
description: |
  'Creates an incident when a CrowdStrike Falcon sensor detection is triggered with Critical severity. The rule queries CrowdStrikeFalconEventStream for DetectionSummaryEvent records where Severity is Critical, summarizes detections by host, source IP, user, activity, technique, file details, hash, and message, and raises an incident for each matching result.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
- Impact
relevantTechniques:
- T1204.002
- T1499
query: |
  let timeframe = 1h;
  CrowdStrikeFalconEventStream
  | where TimeGenerated > ago(timeframe)
  | where EventType == "DetectionSummaryEvent"
  | where Severity == "Critical"
  | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), Total = count() by DstHostName, SrcIpAddr, DstUserName, Activity, Technique, FileName, FilePath, FileHash, Message
  | extend timestamp = StartTimeUtc, AccountCustomEntity = DstUserName, HostCustomEntity = DstHostName, IPCustomEntity = SrcIpAddr, FileHashCustomEntity = FileHash, FileHashAlgo = "MD5"
  | project StartTimeUtc, EndTimeUtc, Total, DstHostName, SrcIpAddr, DstUserName, Activity, Technique, FileName, FilePath, FileHash, Message, timestamp, AccountCustomEntity, HostCustomEntity, IPCustomEntity, FileHashCustomEntity, FileHashAlgo
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: AccountCustomEntity
- entityType: Host
  fieldMappings:
  - identifier: FullName
    columnName: HostCustomEntity
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: IPCustomEntity
- entityType: FileHash
  fieldMappings:
  - identifier: Algorithm
    columnName: FileHashAlgo
  - identifier: Value
    columnName: FileHashCustomEntity
alertDetailsOverride:
  alertDisplayNameFormat: CrowdStrike critical detection on {{DstHostName}}
  alertDescriptionFormat: 'CrowdStrike reported {{Total}} critical detection(s) on {{DstHostName}} for {{DstUserName}}.'
customDetails:
  DetectionCount: Total
  DetectionMessage: Message
  DetectionTechnique: Technique
  DetectionActivity: Activity
  DetectionFileName: FileName
  DetectionFilePath: FilePath
  DetectionHost: DstHostName
  DetectionUser: DstUserName
  DetectionSourceIp: SrcIpAddr
version: 1.0.6
kind: Scheduled

Stages and Predicates

Parameters

let timeframe = 1h;

Stage 1: source

CrowdStrikeFalconEventStream

Stage 2: where

| where TimeGenerated > ago(timeframe)

Stage 3: where

| where EventType == "DetectionSummaryEvent"

Stage 4: where

| where Severity == "Critical"

Stage 5: summarize

| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), Total = count() by DstHostName, SrcIpAddr, DstUserName, Activity, Technique, FileName, FilePath, FileHash, Message

Stage 6: extend

| extend timestamp = StartTimeUtc, AccountCustomEntity = DstUserName, HostCustomEntity = DstHostName, IPCustomEntity = SrcIpAddr, FileHashCustomEntity = FileHash, FileHashAlgo = "MD5"

Stage 7: project

| project StartTimeUtc, EndTimeUtc, Total, DstHostName, SrcIpAddr, DstUserName, Activity, Technique, FileName, FilePath, FileHash, Message, timestamp, AccountCustomEntity, HostCustomEntity, IPCustomEntity, FileHashCustomEntity, FileHashAlgo

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityproject
Activityproject
DstHostNameproject
DstUserNameproject
EndTimeUtcproject
FileHashproject
FileHashAlgoproject
FileHashCustomEntityproject
FileNameproject
FilePathproject
HostCustomEntityproject
IPCustomEntityproject
Messageproject
SrcIpAddrproject
StartTimeUtcproject
Techniqueproject
Totalproject
timestampproject

CyberArkEPM - Unexpected executable location

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

'Detects program run from unexpected location.'

MITRE ATT&CK Coverage

TacticTechniques
Execution
Stealth

Rule Body

id: c1fcbbd7-74f8-4f32-8116-0a533ebd3878
name: CyberArkEPM - Unexpected executable location
description: |
  'Detects program run from unexpected location.'
severity: Medium
requiredDataConnectors:
  - connectorId: CyberArkEPM
    dataTypes:
      - CyberArkEPM
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
  - DefenseEvasion
relevantTechniques:
  - T1204
  - T1036
query: | 
  let susp_exe_folders = dynamic([@'\tmp\', @'\TEMP\', @'/tmp/', @'\Users\Public\', @'\$Recycle.bin', @'\Windows\Fonts\', @'$']);
  CyberArkEPM
  | where EventSubType != 'AttackAttempt'
  | where ActingProcessName has_any (susp_exe_folders)
  | project EventEndTime, EventMessage, ActorUsername, ActingProcessFileInternalName
  | extend AccountCustomEntity = ActorUsername
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let susp_exe_folders = dynamic([@'\tmp\', @'\TEMP\', @'/tmp/', @'\Users\Public\', @'\$Recycle.bin', @'\Windows\Fonts\', @'$']);

Stage 1: source

CyberArkEPM

Stage 2: where

| where EventSubType != 'AttackAttempt'

Stage 3: where

| where ActingProcessName has_any (susp_exe_folders)

Stage 4: project

| project EventEndTime, EventMessage, ActorUsername, ActingProcessFileInternalName

Stage 5: extend

| extend AccountCustomEntity = ActorUsername

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActingProcessNamematch
  • $ transforms: term
  • /tmp/ transforms: term
  • \$Recycle.bin transforms: term
  • \TEMP\ transforms: term
  • \Users\Public\ transforms: term
  • \Windows\Fonts\ transforms: term
  • \tmp\ transforms: term
field:"ParentImage" kind:match
EventSubTypene
  • AttackAttempt
field:"EventSubType" kind:ne value:"AttackAttempt"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActingProcessFileInternalNameproject
ActorUsernameproject
EventEndTimeproject
EventMessageproject
AccountCustomEntityextend

Dataverse - Suspicious use of Web API

#
Status
available
Severity
medium
Time window
14d
Group by
$left.IPAddress, $left.UserPrincipalName, $right.ClientIp, $right.UserId, AppDisplayName, AppId, ClientIp, IPAddress, InstanceUrl, UserId, UserPrincipalName
Source
github.com/Azure/Azure-Sentinel

Identifies sign-in across multiple Dataverse environments, breaching a predefined threshold, originating from a user with IP address that was used to sign-into the well known Microsoft Entra app registration.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: 8a6ecba2-ccfe-4c8c-b086-fa3e6ff7fa86
kind: Scheduled
name: Dataverse - Suspicious use of Web API
description: Identifies sign-in across multiple Dataverse environments, breaching
  a predefined threshold, originating from a user with IP address that was used to
  sign-into the well known Microsoft Entra app registration.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Dataverse
    dataTypes:
      - DataverseActivity
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
  - Exfiltration
  - Reconnaissance
  - Discovery
relevantTechniques:
  - T1106
  - T1567
  - T1595
  - T1526
  - T1580
query: |
  let query_frequency = 1h;
  let query_lookback = 24h;
  // AppID of the multi-tenant Dynamics 365 Example Client Application
  let well_known_app_id = "51f81489-12ee-4a9e-aaae-a2591f45987d";
  let environment_count_threshold = 10;
  SigninLogs
  | where TimeGenerated >= ago(query_lookback)
  // Comment out the line below to monitor activity from all Azure AD apps
  | where AppId == well_known_app_id
  | where ResourceIdentity == '00000007-0000-0000-c000-000000000000'
  | summarize FirstSeen = min(TimeGenerated) by AppId, UserPrincipalName, IPAddress, AppDisplayName
  | join kind=inner (
      DataverseActivity
      | where TimeGenerated >= ago(query_frequency)
      | where Message == "UserSignIn")
      on $left.UserPrincipalName == $right.UserId, $left.IPAddress == $right.ClientIp
  | where TimeGenerated between (FirstSeen .. (FirstSeen + 2h))
  | summarize InstanceCount = dcount(InstanceUrl, 4), FirstSeen = min(FirstSeen) by UserId, ClientIp, InstanceUrl, AppDisplayName, AppId
  | where InstanceCount > environment_count_threshold
  | extend
      CloudAppId = int(32780),
      AccountName = tostring(split(UserId, '@')[0]),
      UPNSuffix = tostring(split(UserId, '@')[1])
  | project
      FirstSeen,
      UserId,
      ClientIp,
      AppDisplayName,
      AppId,
      InstanceUrl,
      CloudAppId,
      AccountName,
      UPNSuffix
eventGroupingSettings:
  aggregationKind: AlertPerResult
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIp
  - entityType: CloudApplication
    fieldMappings:
      - identifier: AppId
        columnName: CloudAppId
      - identifier: InstanceName
        columnName: InstanceUrl
alertDetailsOverride:
  alertDisplayNameFormat: Dataverse - Suspicious Web API sign-in activity
  alertDescriptionFormat: '{{UserId}} sign-in activity generated in {{InstanceUrl}}.
    The app used was a well known multi-tenant app not owned or registered by the
    organization.'
version: 3.2.0

Stages and Predicates

Parameters

let query_frequency = 1h;
let query_lookback = 24h;
let well_known_app_id = "51f81489-12ee-4a9e-aaae-a2591f45987d";
let environment_count_threshold = 10;

Stage 1: source

SigninLogs

Stage 2: where

| where TimeGenerated >= ago(query_lookback)

Stage 3: where

| where AppId == well_known_app_id

Stage 4: where

| where ResourceIdentity == '00000007-0000-0000-c000-000000000000'

Stage 5: summarize

| summarize FirstSeen = min(TimeGenerated) by AppId, UserPrincipalName, IPAddress, AppDisplayName

Stage 6: join

| join kind=inner (
    DataverseActivity
    | where TimeGenerated >= ago(query_frequency)
    | where Message == "UserSignIn")
    on $left.UserPrincipalName == $right.UserId, $left.IPAddress == $right.ClientIp

Stage 7: where TimeGenerated - FirstSeen >= 0s AND TimeGenerated - FirstSeen <= 2h

where TimeGenerated - FirstSeen >= 0s AND TimeGenerated - FirstSeen <= 2h

Stage 8: summarize

summarize FirstSeen, InstanceCount by UserId, ClientIp, InstanceUrl, AppDisplayName, AppId

Stage 9: where

where InstanceCount > 10

Stage 10: extend

extend AccountName, CloudAppId, UPNSuffix

Stage 11: project

project AccountName, AppDisplayName, AppId, ClientIp, CloudAppId, FirstSeen, InstanceUrl, UPNSuffix, UserId

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNameproject
AppDisplayNameproject
AppIdproject
ClientIpproject
CloudAppIdproject
FirstSeenproject
InstanceUrlproject
UPNSuffixproject
UserIdproject

Fortiweb - WAF Allowed threat

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

'Detects WAF "Allowed" action on threat events.'

MITRE ATT&CK Coverage

Rule Body

id: 86e9409f-b9ea-4e9a-8b72-5132ba43bcae
name: Fortiweb - WAF Allowed threat
description: |
  'Detects WAF "Allowed" action on threat events.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: FortinetFortiWebAma
    dataTypes:
      - Fortiweb
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  Fortiweb
  | where EventType =~ 'Attack'
  | where DvcAction =~ 'Allow'
  | summarize count() by SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

Fortiweb

Stage 2: where

| where EventType =~ 'Attack'

Stage 3: where

| where DvcAction =~ 'Allow'

Stage 4: summarize

| summarize count() by SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
SrcIpAddrsummarize

GCP Security Command Center - Detect Firewall rules allowing unrestricted high-risk ports

#
Status
available
Severity
high
Time window
1h
Group by
Description, ExternalUri, FindingName, FirewallName, ProjectName, ResourceName, Severity
Source
github.com/Azure/Azure-Sentinel

This query detects GCP Firewall rules that allow unrestricted (0.0.0.0/0) ingress to high-risk ports using Google Cloud Security Command Center OPEN_FIREWALL findings. Publicly exposed management, database, and service ports (e.g., RDP 3389, SSH 22, SQL 1433/3306) significantly increase the risk of brute-force attacks, exploitation, and lateral movement.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Discovery
Lateral Movement

Rule Body

id: f4f92ca4-6ebe-4f2a-90e5-b0d04b709651
name: GCP Security Command Center - Detect Firewall rules allowing unrestricted high-risk ports
description: |
  This query detects GCP Firewall rules that allow unrestricted (0.0.0.0/0) ingress to high-risk ports using Google Cloud Security Command Center OPEN_FIREWALL findings.
  Publicly exposed management, database, and service ports (e.g., RDP 3389, SSH 22, SQL 1433/3306) significantly increase the risk of brute-force attacks, exploitation, and lateral movement.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: GoogleSCCDefinition
    dataTypes:
      - GoogleCloudSCC
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - LateralMovement
  - Discovery
relevantTechniques:
  - T1133
  - T1021
  - T1046
tags:
  - PCI-DSS v3.2.1 1.2.1
query: |
  let HighRiskPorts = dynamic([3389,20,23,110,143,3306,8080,1433,9200,9300,25,445,135,21,1434,4333,5432,5500,5601,22,3000,5000,8088,8888]);
  GoogleCloudSCC
  | where tostring(Findings.state) == "ACTIVE"
  | extend FindingCategory = tostring(Findings.category)
  | where FindingCategory == "OPEN_FIREWALL"
  | extend FindingsJson = parse_json(Findings)
  | extend SourcePropertiesJson = parse_json(tostring(FindingsJson.sourceProperties))
  | extend IpRulesJson = parse_json(tostring(FindingsJson.ipRules))
  | where tostring(FindingsJson.state) == "ACTIVE"
  | where tostring(SourcePropertiesJson.ExternalSourceRanges) contains "0.0.0.0/0"
  | extend AllowedIpRules = parse_json(tostring(IpRulesJson.allowed.ipRules))
  | mv-expand IpRule = AllowedIpRules
  | extend PortRanges = parse_json(tostring(IpRule.portRanges))
  | mv-expand PortRange = PortRanges
  | extend MinPort = toint(PortRange.min), MaxPort = toint(PortRange.max)
  | where MinPort in (HighRiskPorts) or MaxPort in (HighRiskPorts)
  | extend 
      ResourceName = tostring(FindingsJson.resourceName),
      FindingName = tostring(FindingsJson.name),
      Protocol = tostring(IpRule.protocol),
      Severity = tostring(FindingsJson.severity),
      Description = tostring(FindingsJson.description),
      ExternalUri = tostring(FindingsJson.externalUri),
      AttackExposureScore = todouble(FindingsJson.attackExposure.score),
      ProjectName = extract(@"projects/([^/]+)", 1, tostring(FindingsJson.resourceName)),
      FirewallName = extract(@"firewalls/([^/]+)", 1, tostring(FindingsJson.resourceName))
  | extend PortInfo = case(
      MinPort == MaxPort, tostring(MinPort),
      strcat(tostring(MinPort), "-", tostring(MaxPort))
  )
  | summarize 
      TimeGenerated = max(TimeGenerated), 
      OpenHighRiskPorts = make_set(PortInfo),
      Protocols = make_set(Protocol),
      AttackExposureScore = max(AttackExposureScore)
      by ProjectName, FirewallName, ResourceName, FindingName, Severity, Description, ExternalUri
  | extend OpenHighRiskPorts = strcat_array(OpenHighRiskPorts, ", ")
  | extend Protocols = strcat_array(Protocols, ", ")
entityMappings:
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: ResourceName
customDetails:
  ProjectName: ProjectName
  FirewallName: FirewallName
  FindingId: FindingName
  OpenHighRiskPorts: OpenHighRiskPorts
  Protocols: Protocols
  AttackExposureScore: AttackExposureScore
  ExternalUri: ExternalUri
alertDetailsOverride:
  alertDisplayNameFormat: "GCP Firewall {{FirewallName}} allows unrestricted high-risk ports"
  alertDescriptionFormat: |-
    GCP Firewall {{FirewallName}} in project {{ProjectName}} allows unrestricted (0.0.0.0/0) ingress to high-risk ports: {{OpenHighRiskPorts}}.
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let HighRiskPorts = dynamic([3389,20,23,110,143,3306,8080,1433,9200,9300,25,445,135,21,1434,4333,5432,5500,5601,22,3000,5000,8088,8888]);

Stage 1: source

GoogleCloudSCC

Stage 2: where

| where tostring(Findings.state) == "ACTIVE"

Stage 3: extend

| extend FindingCategory = tostring(Findings.category)

Stage 4: where

| where FindingCategory == "OPEN_FIREWALL"

Stage 5: extend (3 consecutive steps)

| extend FindingsJson = parse_json(Findings)
| extend SourcePropertiesJson = parse_json(tostring(FindingsJson.sourceProperties))
| extend IpRulesJson = parse_json(tostring(FindingsJson.ipRules))

Stage 6: where

| where tostring(FindingsJson.state) == "ACTIVE"

Stage 7: where

| where tostring(SourcePropertiesJson.ExternalSourceRanges) contains "0.0.0.0/0"

Stage 8: extend

| extend AllowedIpRules = parse_json(tostring(IpRulesJson.allowed.ipRules))

Stage 9: mv-expand

| mv-expand IpRule = AllowedIpRules

Stage 10: extend

| extend PortRanges = parse_json(tostring(IpRule.portRanges))

Stage 11: mv-expand

| mv-expand PortRange = PortRanges

Stage 12: extend

| extend MinPort = toint(PortRange.min), MaxPort = toint(PortRange.max)

Stage 13: where

| where MinPort in (HighRiskPorts) or MaxPort in (HighRiskPorts)

Stage 14: extend

| extend 
    ResourceName = tostring(FindingsJson.resourceName),
    FindingName = tostring(FindingsJson.name),
    Protocol = tostring(IpRule.protocol),
    Severity = tostring(FindingsJson.severity),
    Description = tostring(FindingsJson.description),
    ExternalUri = tostring(FindingsJson.externalUri),
    AttackExposureScore = todouble(FindingsJson.attackExposure.score),
    ProjectName = extract(@"projects/([^/]+)", 1, tostring(FindingsJson.resourceName)),
    FirewallName = extract(@"firewalls/([^/]+)", 1, tostring(FindingsJson.resourceName))

Stage 15: extend

| extend PortInfo = case(
    MinPort == MaxPort, tostring(MinPort),
    strcat(tostring(MinPort), "-", tostring(MaxPort))
)
PortInfo =
ifMinPort =~ MaxPorttostring(MinPort)
elsestrcat(tostring(MinPort), "-", tostring(MaxPort))

Stage 16: summarize

| summarize 
    TimeGenerated = max(TimeGenerated), 
    OpenHighRiskPorts = make_set(PortInfo),
    Protocols = make_set(Protocol),
    AttackExposureScore = max(AttackExposureScore)
    by ProjectName, FirewallName, ResourceName, FindingName, Severity, Description, ExternalUri

Stage 17: extend

| extend OpenHighRiskPorts = strcat_array(OpenHighRiskPorts, ", ")

Stage 18: extend

| extend Protocols = strcat_array(Protocols, ", ")

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
FindingCategoryeq
  • OPEN_FIREWALL
field:"FindingCategory" kind:eq value:"OPEN_FIREWALL"
Findings.stateeq
  • ACTIVE transforms: tostring, kusto_dynamic_path
field:"Findings.state" kind:eq value:"ACTIVE"
FindingsJson.stateeq
  • ACTIVE transforms: tostring, kusto_dynamic_path
field:"FindingsJson.state" kind:eq value:"ACTIVE"
MaxPortin
  • 110
  • 135
  • 143
  • 1433
  • 1434
  • 20
  • 21
  • 22
  • 23
  • 25
  • 3000
  • 3306
  • 3389
  • 4333
  • 445
  • 5000
  • 5432
  • 5500
  • 5601
  • 8080
  • 8088
  • 8888
  • 9200
  • 9300
field:"MaxPort" kind:in
MinPortin
  • 110
  • 135
  • 143
  • 1433
  • 1434
  • 20
  • 21
  • 22
  • 23
  • 25
  • 3000
  • 3306
  • 3389
  • 4333
  • 445
  • 5000
  • 5432
  • 5500
  • 5601
  • 8080
  • 8088
  • 8888
  • 9200
  • 9300
field:"MinPort" kind:in
SourcePropertiesJson.ExternalSourceRangescontains
  • 0.0.0.0/0 transforms: tostring, kusto_dynamic_path
field:"SourcePropertiesJson.ExternalSourceRanges" kind:contains value:"0.0.0.0/0"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AttackExposureScoresummarize
Descriptionsummarize
ExternalUrisummarize
FindingNamesummarize
FirewallNamesummarize
OpenHighRiskPortsextend
ProjectNamesummarize
Protocolsextend
ResourceNamesummarize
Severitysummarize
TimeGeneratedsummarize

GTI - Initial Access Broker Alert Detected

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

Triggers an incident when a GTI Relevance System Alert of type initial_access_broker is ingested. Initial Access Broker alerts indicate that a threat actor is advertising or has sold access (credentials, VPN, RDP, admin panels, etc.) to an organisation that matches your profile. These are high-urgency signals that may indicate imminent compromise or an ongoing breach. Each unique Alert ID is grouped into a single incident.

MITRE ATT&CK Coverage

Rule Body

id: c3d4e5f6-a7b8-9012-cdef-123456789013
name: GTI - Initial Access Broker Alert Detected
description: |
  Triggers an incident when a GTI Relevance System Alert of type initial_access_broker is ingested.
  Initial Access Broker alerts indicate that a threat actor is advertising or has sold access
  (credentials, VPN, RDP, admin panels, etc.) to an organisation that matches your profile.
  These are high-urgency signals that may indicate imminent compromise or an ongoing breach.
  Each unique Alert ID is grouped into a single incident.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: GoogleThreatIntelligenceRelevanceSystemAlertsAPI
    dataTypes:
      - RelevanceSystemAlerts_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - CredentialAccess
relevantTechniques:
  - T1078
  - T1133
  - T1190
  - T1552
query: |
  GTIRelevanceSystemAlerts
  | where DetailType == "initial_access_broker"
  | project
      TimeGenerated,
      AlertId,
      AlertName                       = DisplayName,
      AlertState                      = State,
      BrokerSeverity                  = InitialAccessBrokerSeverity,
      DiscoveryDocuments              = InitialAccessBrokerDiscoveryDocumentIds,
      PriorityLevel,
      PriorityReasoning,
      SeverityLevel,
      SeverityConfidence,
      SeverityReasoning,
      RelevanceLevel,
      RelevanceConfidence,
      RelevanceReasoning,
      FindingCount,
      AiSummary,
      CommonThemes                    = RelevanceEvidenceCommonThemes,
      DistinctThemes                  = RelevanceEvidenceDistinctThemes,
      AuditCreateTime,
      AuditUpdateTime
alertDetailsOverride:
  alertDisplayNameFormat: 'GTI Initial Access Broker: {{AlertName}}'
  alertDescriptionFormat: |
    Broker Severity: {{BrokerSeverity}} | Priority: {{PriorityLevel}}

    Summary: {{AiSummary}}
customDetails:
  AlertId: AlertId
  AlertState: AlertState
  BrokerSeverity: BrokerSeverity
  PriorityLevel: PriorityLevel
  SeverityLevel: SeverityLevel
  RelevanceLevel: RelevanceLevel
  FindingCount: FindingCount
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: P5D
    matchingMethod: Selected
    groupByCustomDetails:
      - AlertId
eventGroupingSettings:
  aggregationKind: AlertPerResult
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

GTIRelevanceSystemAlerts

Stage 2: where

| where DetailType == "initial_access_broker"

Stage 3: project

| project
    TimeGenerated,
    AlertId,
    AlertName                       = DisplayName,
    AlertState                      = State,
    BrokerSeverity                  = InitialAccessBrokerSeverity,
    DiscoveryDocuments              = InitialAccessBrokerDiscoveryDocumentIds,
    PriorityLevel,
    PriorityReasoning,
    SeverityLevel,
    SeverityConfidence,
    SeverityReasoning,
    RelevanceLevel,
    RelevanceConfidence,
    RelevanceReasoning,
    FindingCount,
    AiSummary,
    CommonThemes                    = RelevanceEvidenceCommonThemes,
    DistinctThemes                  = RelevanceEvidenceDistinctThemes,
    AuditCreateTime,
    AuditUpdateTime

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
DetailTypeeq
  • initial_access_broker
field:"DetailType" kind:eq value:"initial_access_broker"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AiSummaryproject
AlertIdproject
AlertNameproject
AlertStateproject
AuditCreateTimeproject
AuditUpdateTimeproject
BrokerSeverityproject
CommonThemesproject
DiscoveryDocumentsproject
DistinctThemesproject
FindingCountproject
PriorityLevelproject
PriorityReasoningproject
RelevanceConfidenceproject
RelevanceLevelproject
RelevanceReasoningproject
SeverityConfidenceproject
SeverityLevelproject
SeverityReasoningproject
TimeGeneratedproject

Guardian- No LLM Output Policy Violation Detection

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

'This alert creates an incident when No LLM Output Policy Violation detected from the Guardian.'

Rule Body

id: c0598d69-221e-4235-a1e4-bfd57ada8ce5
name: Guardian- No LLM Output Policy Violation Detection
description: |
  'This alert creates an incident when No LLM Output Policy Violation detected from the Guardian.'
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: BoschAIShield
    dataTypes:
      - Guardian
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
  Guardian
  | where PolicyViolatedControlFeature =~ 'No LLM Output'
  | where Severity =~ 'Low'
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: NTDomain
        columnName: NTDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIP
eventGroupingSettings:
  aggregationKind: SingleAlert
alertDetailsOverride:
  alertDisplayNameFormat: Guardian- No LLM Output Policy Violation detection
  alertDescriptionFormat: |
    This query detects No LLM Output Policy Violation detected from the Guardian generated at {{TimeGenerated}}.\n\nPlease check the source for more information and investigate further.
  alertTacticsColumnName: null
  alertSeverityColumnName: Severity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

Guardian

Stage 2: where

| where PolicyViolatedControlFeature =~ 'No LLM Output'

Stage 3: where

| where Severity =~ 'Low'

Indicators

These rows show field, operator, and value matches.

GWorkspace - Alert events

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

'Detects alert events.'

MITRE ATT&CK Coverage

Rule Body

id: e369d246-5da8-11ec-bf63-0242ac130002
name: GWorkspace - Alert events
description: |
  'Detects alert events.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: GoogleWorkspaceReportsAPI
    dataTypes:
      - GWorkspaceActivityReports
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  GWorkspaceActivityReports
  | where EventType has "ALERT_CENTER"
  | extend AccountCustomEntity = ActorEmail
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

GWorkspaceActivityReports

Stage 2: where

| where EventType has "ALERT_CENTER"

Stage 3: extend

| extend AccountCustomEntity = ActorEmail

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventTypematch
  • ALERT_CENTER transforms: term
field:"EventType" kind:match value:"ALERT_CENTER"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend

KMS Server Deleted

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

Detects when a KMS server is deleted from Veeam Backup & Replication. This might indicate unauthorized removal of critical components.

Rule Body

id: 9b5ab4b6-7cca-4779-9784-761b48a58e78
name: KMS Server Deleted
description: Detects when a KMS server is deleted from Veeam Backup & Replication.
  This might indicate unauthorized removal of critical components.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 42301\n| extend Name = extract(\"\
  Name=\\\"([^\\\"]*)\\\"\", 1, SyslogMessage)\n| project\n    Date = format_datetime(TimeGenerated,\
  \ 'dd.MM.yyyy HH:mm'),\n    DataSource = original_host,\n    EventId = instanceId,\n\
  \    [\"User Name\"] = user,\n    [\"Object Name\"] = Name,\n    MessageDetails\
  \ = Description,\n   Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
  Date: Date
  VbrHostName: DataSource
  EventId: EventId
  MessageDetails: MessageDetails
  Severity: Severity

Stages and Predicates

Stage 1: source

Veeam_GetSecurityEvents

Stage 2: where

| where instanceId == 42301

Stage 3: extend

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

Stage 4: project

| project
    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
    DataSource = original_host,
    EventId = instanceId,
    ["User Name"] = user,
    ["Object Name"] = Name,
    MessageDetails = Description,
   Severity = SeverityDescription

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MessageDetailsproject
Object Nameproject
Severityproject
User Nameproject

NGINX - Put file and get file from same IP address

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

'Detects put or get files from one source in short timeframe'

MITRE ATT&CK Coverage

Rule Body

id: e04fa38e-9fb7-438d-887a-381d5dd235e6
name: NGINX - Put file and get file from same IP address
description: |
  'Detects put or get files 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:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  let p = NGINXHTTPServer
  | where HttpRequestMethod in~ ('POST', 'PUT')
  | sort by EventStartTime asc
  | summarize post_time=min(EventStartTime) by SrcIpAddr, tostring(UrlOriginal);
  NGINXHTTPServer
  | where HttpRequestMethod =~ 'GET'
  | sort by EventStartTime asc
  | summarize get_time=min(EventStartTime) by SrcIpAddr, tostring(UrlOriginal)
  | join kind=innerunique (p) on UrlOriginal, SrcIpAddr
  | extend second = datetime_diff('second',get_time,post_time)
  | where second between (1 .. 300)
  | project second, post_time, get_time, SrcIpAddr, UrlOriginal
  | extend IPCustomEntity = SrcIpAddr, UrlCustomEntity = tostring(UrlOriginal)
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: UrlCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Let Binding: p used in Stage 5

let p = NGINXHTTPServer
| where HttpRequestMethod in~ ('POST', 'PUT')
| sort by EventStartTime asc
| summarize post_time=min(EventStartTime) by SrcIpAddr, tostring(UrlOriginal);

Stage 1: source

NGINXHTTPServer

Stage 2: where

| where HttpRequestMethod =~ 'GET'

Stage 3: sort

| sort by EventStartTime asc

Stage 4: summarize

| summarize get_time=min(EventStartTime) by SrcIpAddr, tostring(UrlOriginal)

Stage 5: join

| join kind=innerunique (p) on UrlOriginal, SrcIpAddr

Stage 6: extend

extend second

Stage 7: where

where second >= 1 and second <= 300

Stage 8: project

project SrcIpAddr, UrlOriginal, get_time, post_time, second

Stage 9: extend

extend IPCustomEntity, UrlCustomEntity

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
SrcIpAddrproject
UrlOriginalproject
get_timeproject
post_timeproject
secondproject
IPCustomEntityextend
UrlCustomEntityextend

Process Creation with Suspicious CommandLine Arguments

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

This analytic rule detects process creation events with base64 encoded command line arguments. This could be an indication of a malicious process being executed.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: fdbcc0eb-44fb-467e-a51d-a91df0780a81
name: Process Creation with Suspicious CommandLine Arguments 
description: |
  This analytic rule detects process creation events with base64 encoded command line arguments. This could be an indication of a malicious process being executed.
severity: Medium
status: Available 
tags:
  - Schema: _ASim_ProcessEvent
    SchemaVersion: 0.1.4
requiredDataConnectors:
  - connectorId: CrowdStrikeFalconEndpointProtection
    dataTypes: 
      - CommonSecurityLog
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - SecurityAlert
  - connectorId: SentinelOne
    dataTypes:
      - SentinelOne_CL
  - connectorId: VMwareCarbonBlack
    dataTypes:
      - CarbonBlackEvents_CL
  - connectorId: CiscoSecureEndpoint
    dataTypes:
      - CiscoSecureEndpoint_CL
  - connectorId: TrendMicroApexOne
    dataTypes:
      - TMApexOneEvent
  - connectorId: TrendMicroApexOneAma
    dataTypes:
      - TMApexOneEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
  - DefenseEvasion
relevantTechniques:
  - T1059
  - T1027
query: |
  _ASim_ProcessEvent
  | where EventType == 'ProcessCreated'
  | extend CommandLineArgs = strcat_array(array_slice(split(CommandLine, " "), 1, -1), " ")
  | where strlen(CommandLineArgs) > 0
  | where CommandLineArgs contains "base64"
  | project
  TimeGenerated,
  DvcHostname,
  DvcIpAddr,
  DvcDomain,
  TargetUsername,
  TargetUsernameType,
  TargetProcessName,
  TargetProcessId,
  CommandLine
  | extend Username = iff(tostring(TargetUsernameType) == 'Windows', tostring(split(TargetUsername, '\\')[1]), TargetUsername)
  | extend NTDomain = iff(tostring(TargetUsernameType) == 'Windows', tostring(split(TargetUsername, '\\')[0]), TargetUsername)
  | extend Username = iff(tostring(TargetUsernameType) == 'UPN', tostring(split(TargetUsername, '@')[0]), Username)
  | extend UPNSuffix = iff(tostring(TargetUsernameType) == 'UPN', tostring(split(TargetUsername, '@')[1]), '')
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DvcHostname
      - identifier: DnsDomain
        columnName: DvcDomain
      - identifier: NTDomain
        columnName: NTDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: DvcIpAddr
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Username
      - identifier: UPNSuffix
        columnName: UPNSuffix
      - identifier: NTDomain
        columnName: NTDomain
  - entityType: Process
    fieldMappings:
      - identifier: ProcessId
        columnName: TargetProcessId
      - identifier: CommandLine
        columnName: CommandLine
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: "Process with suspicious command line arguments was created on {{DvcHostname}} ({{DvcIpAddr}}) by ({{TargetUsername}})"
  alertDescriptionFormat: "Process '{{TargetProcessName}}' ProcessId: '{{TargetProcessId}}' with commandline {{CommandLine}} was created."
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

_ASim_ProcessEvent

Stage 2: where

| where EventType == 'ProcessCreated'

Stage 3: extend

| extend CommandLineArgs = strcat_array(array_slice(split(CommandLine, " "), 1, -1), " ")

Stage 4: where

| where strlen(CommandLineArgs) > 0

Stage 5: where

| where CommandLineArgs contains "base64"

Stage 6: project

| project
TimeGenerated,
DvcHostname,
DvcIpAddr,
DvcDomain,
TargetUsername,
TargetUsernameType,
TargetProcessName,
TargetProcessId,
CommandLine

Stage 7: extend (4 consecutive steps)

| extend Username = iff(tostring(TargetUsernameType) == 'Windows', tostring(split(TargetUsername, '\\')[1]), TargetUsername)
| extend NTDomain = iff(tostring(TargetUsernameType) == 'Windows', tostring(split(TargetUsername, '\\')[0]), TargetUsername)
| extend Username = iff(tostring(TargetUsernameType) == 'UPN', tostring(split(TargetUsername, '@')[0]), Username)
| extend UPNSuffix = iff(tostring(TargetUsernameType) == 'UPN', tostring(split(TargetUsername, '@')[1]), '')
Username =
ifTargetUsernameType == "Windows"tostring(split(TargetUsername, '\\')[1])
elseTargetUsername

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
CommandLineproject
DvcDomainproject
DvcHostnameproject
DvcIpAddrproject
TargetProcessIdproject
TargetProcessNameproject
TargetUsernameproject
TargetUsernameTypeproject
TimeGeneratedproject
Usernameextend
NTDomainextend
UPNSuffixextend

PROD (TM016.1) - GROUP - Added to Well-Known or Tier Model Group

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

Well-Known Groups are classified as Tier 0, and Tier Model groups that provide permission over Tier Model objects.

MITRE ATT&CK Coverage

TacticTechniques
Privilege Escalation

Telemetry Coverage

Rule Body

id: 763fc00b-4971-43c2-9cd1-f12b58a542bd
name: 'PROD (TM016.1) - GROUP - Added to Well-Known or Tier Model Group'
description: |
  Well-Known Groups are classified as Tier 0, and Tier Model groups that provide permission over Tier Model objects.
severity: High
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - PrivilegeEscalation
relevantTechniques:
  - T1078.002
query: |
  let adminGroups = dynamic([
      "CN=Domain Admins,CN=Users",
      "CN=Enterprise Admins,CN=Users",
      "CN=Administrators,CN=Builtin",
      "CN=Cert Publishers,CN=Users",
      "CN=Cloneable Domain Controllers,CN=Users",
      "CN=Denied RODC Password Replication Group,CN=Users",
      "CN=DnsAdmins,CN=Users",
      "CN=DnsUpdateProxy,CN=Users",
      "CN=Domain Controllers,CN=Users",
      "CN=Domain Guests,CN=Users",
      "CN=Enterprise Key Admins,CN=Users",
      "CN=Enterprise Read-only Domain Controllers,CN=Users",
      "CN=External Trust Accounts,CN=Users",
      "CN=Forest Trust Accounts,CN=Users",
      "CN=Group Policy Creator Owners,CN=Users",
      "CN=Key Admins,CN=Users",
      "CN=RAS and IAS Servers,CN=Users",
      "CN=Read-only Domain Controllers,CN=Users",
      "CN=Schema Admins,CN=Users",
      "CN=Access Control Assistance Operators,CN=Builtin",
      "CN=Account Operators,CN=Builtin",
      "CN=Backup Operators,CN=Builtin",
      "CN=Certificate Service DCOM Access,CN=Builtin",
      "CN=Cryptographic Operators,CN=Builtin",
      "CN=Distributed COM Users,CN=Builtin",
      "CN=Event Log Readers,CN=Builtin",
      "CN=Guests,CN=Builtin",
      "CN=Hyper-V Administrators,CN=Builtin",
      "CN=IIS_IUSRS,CN=Builtin",
      "CN=Incoming Forest Trust Builders,CN=Builtin",
      "CN=Network Configuration Operators,CN=Builtin",
      "CN=OpenSSH Users,CN=Builtin",
      "CN=Performance Log Users,CN=Builtin",
      "CN=Performance Monitor Users,CN=Builtin",
      "CN=Pre-Windows 2000 Compatible Access,CN=Builtin",
      "CN=Print Operators,CN=Builtin",
      "CN=RDS Endpoint Servers,CN=Builtin",
      "CN=RDS Management Servers,CN=Builtin",
      "CN=RDS Remote Access Servers,CN=Builtin",
      "CN=Remote Desktop Users,CN=Builtin",
      "CN=Remote Management Users,CN=Builtin",
      "CN=Replicator,CN=Builtin",
      "CN=Server Operators,CN=Builtin",
      "CN=Storage Replica Administrators,CN=Builtin",
      "CN=Terminal Server License Servers,CN=Builtin",
      "CN=Users,CN=Builtin",
      "CN=Windows Authorization Access Group,CN=Builtin",
      "CN=Tier 0 Admins,OU=Tier 0 Groups,OU=Tier 0,OU=Tier Model Administration",
      "CN=Tier 0 Server Operators,OU=Tier 0 Groups,OU=Tier 0,OU=Tier Model Administration",
      "CN=Tier 1 Admins,OU=Tier 1 Groups,OU=Tier 1,OU=Tier Model Administration",
      "CN=Tier 1 Server Operators,OU=Tier 1 Groups,OU=Tier 1,OU=Tier Model Administration",
      "CN=Tier 2 Admins,OU=Tier 2 Groups,OU=Tier 2,OU=Tier Model Administration"
  ]);
  SecurityEvent
  | where EventID == 5136
  | extend EventData = tostring(EventData)
  | where EventData has_any(adminGroups)
      and EventData has '"AttributeLDAPDisplayName">member'
      and EventData has 'OperationType">%%14674'
  | extend GroupName_e = extract("<Data Name=\"ObjectDN\">([^<]+)</Data>", 1, EventData)
  | extend GroupName = extract("(?i)CN=([^,]+)", 1, GroupName_e)
  | extend GroupTier = case(
      GroupName has "Tier 0", "Tier 0",
      GroupName has "Tier 1", "Tier 1",
      GroupName has "Tier 2", "Tier 2",
      "Tier 0")
  | extend ObjectName_e = extract("<Data Name=\"AttributeValue\">([^<]+)</Data>", 1, EventData)
  | extend ObjectName = extract("(?i)CN=([^,]+)", 1, ObjectName_e)
  | extend ObjectTier = case(
      ObjectName_e has "Tier 0", "Tier 0",
      ObjectName_e has "Tier 1", "Tier 1",
      ObjectName_e has "Tier 2", "Tier 2",
      "Outside Tier Model")
  | extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)
  | extend ActivityType = extract("([a-zA-Z]+)\\.$", 1, Activity)
  | extend SubjectUserName = extract("<Data Name=\"SubjectUserName\">([^<]+)</Data>", 1, EventData)
  | extend SubjectDomainName = extract("<Data Name=\"SubjectDomainName\">([^<]+)</Data>", 1, EventData)
  | extend Account = strcat(SubjectDomainName, "\\", SubjectUserName)
  | extend OperationType = iff(EventData has 'OperationType">%%14674', "Added", tostring(OperationType))
  | project TimeGenerated 
          , ObjectName
          , ObjectTier
          , ObjectType = AccountType
          , GroupName
          , GroupTier
          , ActivityType
          , OperationType
          , 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
  ObjectType: ObjectType
  GroupName: GroupName
  GroupTier: GroupTier
  ActivityType: ActivityType
  OperationType: OperationType
  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: '(TM016.1) A member was added to {{GroupName}} which is a Well-Known or Tier Model Group'
  alertDescriptionFormat: 'The following object {{ObjectName}} was added to a {{ObjectTier}}  Well-Known or Tier Model high privilege group.'
version: 1.0.0
kind: Scheduled

Stages and Predicates

Let Binding: adminGroups used in Stage 4

let adminGroups = dynamic([
    "CN=Domain Admins,CN=Users",
    "CN=Enterprise Admins,CN=Users",
    "CN=Administrators,CN=Builtin",
    "CN=Cert Publishers,CN=Users",
    "CN=Cloneable Domain Controllers,CN=Users",
    "CN=Denied RODC Password Replication Group,CN=Users",
    "CN=DnsAdmins,CN=Users",
    "CN=DnsUpdateProxy,CN=Users",
    "CN=Domain Controllers,CN=Users",
    "CN=Domain Guests,CN=Users",
    "CN=Enterprise Key Admins,CN=Users",
    "CN=Enterprise Read-only Domain Controllers,CN=Users",
    "CN=External Trust Accounts,CN=Users",
    "CN=Forest Trust Accounts,CN=Users",
    "CN=Group Policy Creator Owners,CN=Users",
    "CN=Key Admins,CN=Users",
    "CN=RAS and IAS Servers,CN=Users",
    "CN=Read-only Domain Controllers,CN=Users",
    "CN=Schema Admins,CN=Users",
    "CN=Access Control Assistance Operators,CN=Builtin",
    "CN=Account Operators,CN=Builtin",
    "CN=Backup Operators,CN=Builtin",
    "CN=Certificate Service DCOM Access,CN=Builtin",
    "CN=Cryptographic Operators,CN=Builtin",
    "CN=Distributed COM Users,CN=Builtin",
    "CN=Event Log Readers,CN=Builtin",
    "CN=Guests,CN=Builtin",
    "CN=Hyper-V Administrators,CN=Builtin",
    "CN=IIS_IUSRS,CN=Builtin",
    "CN=Incoming Forest Trust Builders,CN=Builtin",
    "CN=Network Configuration Operators,CN=Builtin",
    "CN=OpenSSH Users,CN=Builtin",
    "CN=Performance Log Users,CN=Builtin",
    "CN=Performance Monitor Users,CN=Builtin",
    "CN=Pre-Windows 2000 Compatible Access,CN=Builtin",
    "CN=Print Operators,CN=Builtin",
    "CN=RDS Endpoint Servers,CN=Builtin",
    "CN=RDS Management Servers,CN=Builtin",
    "CN=RDS Remote Access Servers,CN=Builtin",
    "CN=Remote Desktop Users,CN=Builtin",
    "CN=Remote Management Users,CN=Builtin",
    "CN=Replicator,CN=Builtin",
    "CN=Server Operators,CN=Builtin",
    "CN=Storage Replica Administrators,CN=Builtin",
    "CN=Terminal Server License Servers,CN=Builtin",
    "CN=Users,CN=Builtin",
    "CN=Windows Authorization Access Group,CN=Builtin",
    "CN=Tier 0 Admins,OU=Tier 0 Groups,OU=Tier 0,OU=Tier Model Administration",
    "CN=Tier 0 Server Operators,OU=Tier 0 Groups,OU=Tier 0,OU=Tier Model Administration",
    "CN=Tier 1 Admins,OU=Tier 1 Groups,OU=Tier 1,OU=Tier Model Administration",
    "CN=Tier 1 Server Operators,OU=Tier 1 Groups,OU=Tier 1,OU=Tier Model Administration",
    "CN=Tier 2 Admins,OU=Tier 2 Groups,OU=Tier 2,OU=Tier Model Administration"
]);

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID == 5136

Stage 3: extend

| extend EventData = tostring(EventData)

Stage 4: where

| where EventData has_any(adminGroups)
    and EventData has '"AttributeLDAPDisplayName">member'
    and EventData has 'OperationType">%%14674'

Stage 5: extend (12 consecutive steps)

| extend GroupName_e = extract("<Data Name=\"ObjectDN\">([^<]+)</Data>", 1, EventData)
| extend GroupName = extract("(?i)CN=([^,]+)", 1, GroupName_e)
| extend GroupTier = case(
    GroupName has "Tier 0", "Tier 0",
    GroupName has "Tier 1", "Tier 1",
    GroupName has "Tier 2", "Tier 2",
    "Tier 0")
| extend ObjectName_e = extract("<Data Name=\"AttributeValue\">([^<]+)</Data>", 1, EventData)
| extend ObjectName = extract("(?i)CN=([^,]+)", 1, ObjectName_e)
| extend ObjectTier = case(
    ObjectName_e has "Tier 0", "Tier 0",
    ObjectName_e has "Tier 1", "Tier 1",
    ObjectName_e has "Tier 2", "Tier 2",
    "Outside Tier Model")
| extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)
| extend ActivityType = extract("([a-zA-Z]+)\\.$", 1, Activity)
| extend SubjectUserName = extract("<Data Name=\"SubjectUserName\">([^<]+)</Data>", 1, EventData)
| extend SubjectDomainName = extract("<Data Name=\"SubjectDomainName\">([^<]+)</Data>", 1, EventData)
| extend Account = strcat(SubjectDomainName, "\\", SubjectUserName)
| extend OperationType = iff(EventData has 'OperationType">%%14674', "Added", tostring(OperationType))

Stage 6: project

| project TimeGenerated 
        , ObjectName
        , ObjectTier
        , ObjectType = AccountType
        , GroupName
        , GroupTier
        , ActivityType
        , OperationType
        , Account
        , Domain
        , Computer
        , Channel
        , EventID
        , EventData

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventDatamatch
  • "AttributeLDAPDisplayName">member transforms: term
  • CN=Access Control Assistance Operators,CN=Builtin transforms: term
  • CN=Account Operators,CN=Builtin transforms: term
  • CN=Administrators,CN=Builtin transforms: term
  • CN=Backup Operators,CN=Builtin transforms: term
  • CN=Cert Publishers,CN=Users transforms: term
  • CN=Certificate Service DCOM Access,CN=Builtin transforms: term
  • CN=Cloneable Domain Controllers,CN=Users transforms: term
  • CN=Cryptographic Operators,CN=Builtin transforms: term
  • CN=Denied RODC Password Replication Group,CN=Users transforms: term
  • CN=Distributed COM Users,CN=Builtin transforms: term
  • CN=DnsAdmins,CN=Users transforms: term
  • CN=DnsUpdateProxy,CN=Users transforms: term
  • CN=Domain Admins,CN=Users transforms: term
  • CN=Domain Controllers,CN=Users transforms: term
  • CN=Domain Guests,CN=Users transforms: term
  • CN=Enterprise Admins,CN=Users transforms: term
  • CN=Enterprise Key Admins,CN=Users transforms: term
  • CN=Enterprise Read-only Domain Controllers,CN=Users transforms: term
  • CN=Event Log Readers,CN=Builtin transforms: term
  • CN=External Trust Accounts,CN=Users transforms: term
  • CN=Forest Trust Accounts,CN=Users transforms: term
  • CN=Group Policy Creator Owners,CN=Users transforms: term
  • CN=Guests,CN=Builtin transforms: term
  • CN=Hyper-V Administrators,CN=Builtin transforms: term
  • CN=IIS_IUSRS,CN=Builtin transforms: term
  • CN=Incoming Forest Trust Builders,CN=Builtin transforms: term
  • CN=Key Admins,CN=Users transforms: term
  • CN=Network Configuration Operators,CN=Builtin transforms: term
  • CN=OpenSSH Users,CN=Builtin transforms: term
  • CN=Performance Log Users,CN=Builtin transforms: term
  • CN=Performance Monitor Users,CN=Builtin transforms: term
  • CN=Pre-Windows 2000 Compatible Access,CN=Builtin transforms: term
  • CN=Print Operators,CN=Builtin transforms: term
  • CN=RAS and IAS Servers,CN=Users transforms: term
  • CN=RDS Endpoint Servers,CN=Builtin transforms: term
  • CN=RDS Management Servers,CN=Builtin transforms: term
  • CN=RDS Remote Access Servers,CN=Builtin transforms: term
  • CN=Read-only Domain Controllers,CN=Users transforms: term
  • CN=Remote Desktop Users,CN=Builtin transforms: term
  • CN=Remote Management Users,CN=Builtin transforms: term
  • CN=Replicator,CN=Builtin transforms: term
  • CN=Schema Admins,CN=Users transforms: term
  • CN=Server Operators,CN=Builtin transforms: term
  • CN=Storage Replica Administrators,CN=Builtin transforms: term
  • CN=Terminal Server License Servers,CN=Builtin transforms: term
  • CN=Tier 0 Admins,OU=Tier 0 Groups,OU=Tier 0,OU=Tier Model Administration transforms: term
  • CN=Tier 0 Server Operators,OU=Tier 0 Groups,OU=Tier 0,OU=Tier Model Administration transforms: term
  • CN=Tier 1 Admins,OU=Tier 1 Groups,OU=Tier 1,OU=Tier Model Administration transforms: term
  • CN=Tier 1 Server Operators,OU=Tier 1 Groups,OU=Tier 1,OU=Tier Model Administration transforms: term
  • CN=Tier 2 Admins,OU=Tier 2 Groups,OU=Tier 2,OU=Tier Model Administration transforms: term
  • CN=Users,CN=Builtin transforms: term
  • CN=Windows Authorization Access Group,CN=Builtin transforms: term
  • OperationType">%%14674 transforms: term
field:"EventData" kind: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
Channelproject
Computerproject
Domainproject
EventDataproject
EventIDproject
GroupNameproject
GroupTierproject
ObjectNameproject
ObjectTierproject
ObjectTypeproject
OperationTypeproject
TimeGeneratedproject

Radiflow - Platform Alert

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

Generates an incident when an alert raised by Radiflow's iSID is not contemplated by any of the other analytic rules

MITRE ATT&CK Coverage

TacticTechniques
Initial AccessNo specific technique
ExecutionNo specific technique
Privilege EscalationNo specific technique
Lateral MovementNo specific technique
Command & ControlNo specific technique
ExfiltrationNo specific technique

Rule Body

id: ff0c781a-b30f-4acf-9cf1-75d7383d66d1
name: Radiflow - Platform Alert
description: Generates an incident when an alert raised by Radiflow's iSID is not contemplated by any of the other analytic rules
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: RadiflowIsid
    dataTypes:
      - RadiflowEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
- Execution
- CommandAndControl
- Exfiltration
- LateralMovement
- ImpairProcessControl
- InhibitResponseFunction
- InitialAccess
relevantTechniques:
query: |
  RadiflowEvent
  | where DeviceProduct =~ 'iSID'
  | where EventClassID !in ('User Activity', 100, 200, 300, 15500, 500, 15600, 16100, 16900, 17100, 18200, 18300, 18400, 18500, 5200, 10500, 15000, 15100, 15200, 15300, 15800, 16600, 17500, 11400, 11500, 11600, 11700, 11800, 11900, 16200, 5500, 14700, 19300, 19400, 2200, 2300, 2400, 2500, 2600, 2700, 14500, 3400, 5300, 6700, 6800, 6900, 7000, 7100, 17900, 5700, 5800, 5800, 6000, 17600, 17800, 18100)
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: Radiflow Platform Alert
  alertDescriptionFormat: |
   Suspicious activity has been detected by Radiflow's iSID. Please check the following details for more information.\n\nMessage: {{EventMessage}} \nSourceIP: {{SourceIP}} \nDestination IP (if any): {{DestinationIP}}
  alertSeverityColumnName: EventSeverity
  alertDynamicProperties: []
suppressionEnabled: false
suppressionDuration: 5h
customDetails:
  SourceMAC: SourceMACAddress
  SourceType: SourceType
  SourceVendor: SourceVendor
  SourceVLAN: SourceVLAN
  DestinationMAC: DestinationMACAddress
  DestinationType: DestinationType
  DestinationVendor: DestinationVendor
  Port: Port
  Protocol: Protocol
  SourceHostName: SourceHostName
  SourceIP: SourceIP
  DestinationHostName: DestinationHostName
  DestinationIP: DestinationIP
entityMappings:
- entityType: Host
  fieldMappings:
  - identifier: HostName
    columnName: SourceHostName
  - identifier: NetBiosName
    columnName: SourceHostName
- entityType: Host
  fieldMappings:
  - identifier: HostName
    columnName: DestinationHostName
  - identifier: NetBiosName
    columnName: DestinationHostName
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: SourceIP
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: DestinationIP
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

RadiflowEvent

Stage 2: where

| where DeviceProduct =~ 'iSID'

Stage 3: where

| where EventClassID !in ('User Activity', 100, 200, 300, 15500, 500, 15600, 16100, 16900, 17100, 18200, 18300, 18400, 18500, 5200, 10500, 15000, 15100, 15200, 15300, 15800, 16600, 17500, 11400, 11500, 11600, 11700, 11800, 11900, 16200, 5500, 14700, 19300, 19400, 2200, 2300, 2400, 2500, 2600, 2700, 14500, 3400, 5300, 6700, 6800, 6900, 7000, 7100, 17900, 5700, 5800, 5800, 6000, 17600, 17800, 18100)

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
EventClassIDin100, 10500, 11400, 11500, 11600, 11700, 11800, 11900, 14500, 14700, 15000, 15100, 15200, 15300, 15500, 15600, 15800, 16100, 16200, 16600, 16900, 17100, 17500, 17600, 17800, 17900, 18100, 18200, 18300, 18400, 18500, 19300, 19400, 200, 2200, 2300, 2400, 2500, 2600, 2700, 300, 3400, 500, 5200, 5300, 5500, 5700, 5800, 6000, 6700, 6800, 6900, 7000, 7100, User Activityexcludes:EventClassID

RecordedFuture Threat Hunting Domain All Actors

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

Recorded Future Threat Hunting domain correlation for all actors.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Command & Control

Telemetry Coverage

ProviderRecord / event type
SysmonEvent ID 22: DNSEvent (DNS query)

Rule Body

id: acbf7ef6-f964-44c3-9031-7834ec68175f
name: RecordedFuture Threat Hunting Domain All Actors
description: |
  'Recorded Future Threat Hunting domain correlation for all actors.'
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligenceUploadIndicatorsAPI
    dataTypes:
      - ThreatIntelIndicators
queryFrequency: 15m
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - CommandAndControl
relevantTechniques:
  - T1566
  - T1568
customDetails:
  ActorInformation: RecordedFuturePortalLink
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: '{{Description}}'
  alertDescriptionFormat: '**{{Description}}**\n\nCorrelation found on {{Domain}} from the {{Type}} table.\n'
  alertDynamicProperties:
    - alertProperty: AlertLink
      value: RecordedFuturePortalLink
query: |
  let ioc_lookBack = 1d;
  // The source table (_Im_Dns) is an ASIM parser table, but can be replaced by any infrastructure table containing domain/dns data.
  // The following workbook: Recorded Future - Domain Correlation will help researching available data and selecting tables and columns
  _Im_Dns
  | where isnotempty(Domain)
  | extend lowerDomain=tolower(Domain)
  | join kind=inner (
  ThreatIntelIndicators
  // Only look at Domain IOCs
  | where ObservableKey == 'domain-name:value'
  | where isnotempty(ObservableValue)
  // Only look at Recorded Future Threat Hunt Indicators.
  | where Data.description startswith "Recorded Future - Threat Hunt"
  // Only work with the latest indicators
  | where TimeGenerated >= ago(ioc_lookBack)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
  | where IsActive == true and ValidUntil > now()
  | extend lowerDomain=tolower(ObservableValue)
  ) on lowerDomain
  // select column from the source table to match with Recorded Future $left.Domain
  | mv-expand Label=Data.labels
  | extend RecordedFuturePortalLink = parse_json(tostring(Label)).RecordedFuturePortalLink
  | project Domain=ObservableValue, Description=Data.description, Type, TimeGenerated, RecordedFuturePortalLink
entityMappings:
  - entityType: DNS
    fieldMappings:
      - identifier: DomainName
        columnName: Domain
version: 1.1.1
kind: Scheduled

Stages and Predicates

Parameters

let ioc_lookBack = 1d;

Stage 1: source

_Im_Dns

Stage 2: where

| where isnotempty(Domain)

Stage 3: extend

| extend lowerDomain=tolower(Domain)

Stage 4: join

| join kind=inner (
ThreatIntelIndicators
| where ObservableKey == 'domain-name:value'
| where isnotempty(ObservableValue)
| where Data.description startswith "Recorded Future - Threat Hunt"
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
| where IsActive == true and ValidUntil > now()
| extend lowerDomain=tolower(ObservableValue)
) on lowerDomain

Stage 5: mv-expand

mv-expand Label

Stage 6: extend

extend RecordedFuturePortalLink

Stage 7: project

project Description, Domain, RecordedFuturePortalLink, TimeGenerated, Type

Stage 8: 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

TI Map IP Entity to AzureActivity

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

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

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: 7a0c9989-1618-4126-9290-fb77b976d181
name: TI Map IP Entity to AzureActivity
description: |
  This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in AzureActivity.
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: AzureActivity
    dataTypes:
      - AzureActivity
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  let dt_lookBack = 1h; // Look back 1 hour for AzureActivity logs
  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)
    // Filter out indicators without relevant IP address fields
    | where TimeGenerated >= ago(ioc_lookBack)
    // Select the IP entity based on availability of different IP fields
    | 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, "")
    // Exclude local addresses using the ipv4_is_private operator and filtering out specific address prefixes
    | 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 AzureActivity logs to identify potential malicious activity
  IP_Indicators
     | project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
  // using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated
  | join kind=innerunique (
      AzureActivity | where TimeGenerated >= ago(dt_lookBack)
      // renaming time column so it is clear the log this came from
      | extend AzureActivity_TimeGenerated = TimeGenerated
  )
  on $left.TI_ipEntity == $right.CallerIpAddress
  | where AzureActivity_TimeGenerated < ValidUntil
  | summarize AzureActivity_TimeGenerated = arg_max(AzureActivity_TimeGenerated, *) by Id, CallerIpAddress
  | extend Description = tostring(parse_json(Data).description)
  | extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
  | project AzureActivity_TimeGenerated, Description, ActivityGroupNames, Id, ValidUntil, Confidence, TI_ipEntity, CallerIpAddress, 
  Caller, OperationNameValue, ActivityStatusValue, CategoryValue, ResourceId, NetworkSourceIP, Type, Url
  | extend timestamp = AzureActivity_TimeGenerated
  | extend Name = iif(Caller has '@', tostring(split(Caller,'@',0)[0]), "")
  | extend UPNSuffix = iif(Caller has '@', tostring(split(Caller,'@',1)[0]), "")
  | extend AadUserId = iif(Caller !has '@', tostring(Caller), "")
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Caller
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: AadUserId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: CallerIpAddress
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
  - entityType: AzureResource
    fieldMappings:
      - identifier: ResourceId
        columnName: ResourceId
version: 1.4.7
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 (
    AzureActivity | where TimeGenerated >= ago(dt_lookBack)
    | extend AzureActivity_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.CallerIpAddress

Stage 13: where

where AzureActivity_TimeGenerated < ValidUntil

Stage 14: summarize

summarize AzureActivity_TimeGenerated by Id, CallerIpAddress

Stage 15: extend

extend Description

Stage 16: extend

extend ActivityGroupNames

Stage 17: project

project ActivityGroupNames, ActivityStatusValue, AzureActivity_TimeGenerated, Caller, CallerIpAddress, CategoryValue, Confidence, Description, Id, NetworkSourceIP, OperationNameValue, ResourceId, TI_ipEntity, Type, Url, ValidUntil

Stage 18: extend (4 consecutive steps)

extend AadUserId, Name, UPNSuffix, 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
ActivityStatusValueproject
AzureActivity_TimeGeneratedproject
Callerproject
CallerIpAddressproject
CategoryValueproject
Confidenceproject
Descriptionproject
Idproject
NetworkSourceIPproject
OperationNameValueproject
ResourceIdproject
TI_ipEntityproject
Typeproject
Urlproject
ValidUntilproject
timestampextend
Nameextend
UPNSuffixextend
AadUserIdextend

User Session Impersonation(Okta)

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

This query detects instances of user session impersonation, where an attacker successfully initiates a session impersonation event. The query extracts detailed information about the target user and the actor involved in the impersonation, providing insights into potential privilege escalation activities.

MITRE ATT&CK Coverage

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 35846296-4052-4de2-8098-beb6bb5f2203
name: User Session Impersonation(Okta)
description: |
  This query detects instances of user session impersonation, where an attacker successfully initiates a session impersonation event. The query extracts detailed information about the target user and the actor involved in the impersonation, providing insights into potential privilege escalation activities.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: OktaSSO
    dataTypes:
      - Okta_CL
  - connectorId: OktaSSOv2
    dataTypes:
      - OktaSSO
queryFrequency: 6h
queryPeriod: 6h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - PrivilegeEscalation
relevantTechniques:
  - T1134
  - T1134.003
query: |
  OktaSSO
  | where eventType_s == "user.session.impersonation.initiate" and outcome_result_s == "SUCCESS"
  // Expand the JSON array in 'target_s' field to extract detailed information about the event
  | mv-expand parsed_json = todynamic(target_s) // Unpack and understand the details from the 'target_s' JSON array
  // Enhance visibility by extending columns with extracted details for better analysis
  | extend TargetUser_id = tostring(parsed_json.id), 
           TargetUser_type = tostring(parsed_json.type), 
           TargetUser_alternateId = tostring(parsed_json.alternateId), 
           TargetUser_displayName = tostring(parsed_json.displayName), 
           Target_detailEntry = tostring(parsed_json.detailEntry)
  // Filter out events with unknown target user IDs to focus on valid impersonation attempts
  | where isnotempty(TargetUser_alternateId) and TargetUser_alternateId != "unknown"
  // Project event details to gain insights into the security context, including actor and target user information
  | project TimeGenerated, actor_alternateId_s, actor_displayName_s, TargetUser_alternateId, TargetUser_displayName, TargetUser_type, TargetUser_id, eventType_s, outcome_result_s
  | extend AccountName = tostring(split(actor_alternateId_s, "@")[0]), AccountUPNSuffix = tostring(split(actor_alternateId_s, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
customDetails:
  ActorDisplayName: actor_displayName_s
  TargetUserAltId: TargetUser_alternateId
  TargetUserName: TargetUser_displayName
  TargetUserType: TargetUser_type
  EventType: eventType_s
alertDetailsOverride:
  alertDisplayNameFormat: 'Okta session impersonation by {{actor_displayName_s}} for {{TargetUser_displayName}}'
  alertDescriptionFormat: 'Okta user session impersonation succeeded. Actor {{actor_alternateId_s}} impersonated target {{TargetUser_alternateId}} during {{eventType_s}}.'
version: 1.1.1
kind: Scheduled

Stages and Predicates

Stage 1: source

OktaSSO

Stage 2: where

| where eventType_s == "user.session.impersonation.initiate" and outcome_result_s == "SUCCESS"

Stage 3: mv-expand

| mv-expand parsed_json = todynamic(target_s)

Stage 4: extend

| extend TargetUser_id = tostring(parsed_json.id), 
         TargetUser_type = tostring(parsed_json.type), 
         TargetUser_alternateId = tostring(parsed_json.alternateId), 
         TargetUser_displayName = tostring(parsed_json.displayName), 
         Target_detailEntry = tostring(parsed_json.detailEntry)

Stage 5: where

| where isnotempty(TargetUser_alternateId) and TargetUser_alternateId != "unknown"

Stage 6: project

| project TimeGenerated, actor_alternateId_s, actor_displayName_s, TargetUser_alternateId, TargetUser_displayName, TargetUser_type, TargetUser_id, eventType_s, outcome_result_s

Stage 7: extend

| extend AccountName = tostring(split(actor_alternateId_s, "@")[0]), AccountUPNSuffix = tostring(split(actor_alternateId_s, "@")[1])

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
TargetUser_alternateIdproject
TargetUser_displayNameproject
TargetUser_idproject
TargetUser_typeproject
TimeGeneratedproject
actor_alternateId_sproject
actor_displayName_sproject
eventType_sproject
outcome_result_sproject
AccountNameextend
AccountUPNSuffixextend

Utimaco ESKM - PERMISSION_DENIED burst for a KMIP user

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

Identifies a single KMIP user receiving 10 or more PERMISSION_DENIED responses within 30 minutes on Utimaco ESKM. May indicate privilege probing, key access reconnaissance, or a misconfigured application.

MITRE ATT&CK Coverage

TacticTechniques
Discovery

Rule Body

id: 9b1a3b3e-7e16-4a3b-8a8f-7f1f2b1c0a02
name: Utimaco ESKM - PERMISSION_DENIED burst for a KMIP user
description: |
  Identifies a single KMIP user receiving 10 or more PERMISSION_DENIED responses within 30 minutes on Utimaco ESKM. May indicate privilege probing, key access reconnaissance, or a misconfigured application.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: UtimacoESKMConnector
    dataTypes:
      - UtimacoESKMKmipServerLogs_CL
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
relevantTechniques:
  - T1087
query: |
  UtimacoESKMKmipServerLogs_CL
  | where TimeGenerated > ago(30m)
  | where Reason == "PERMISSION_DENIED"
  | where isnotempty(User)
  | summarize DeniedCount = count(), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Operations = make_set(Operation, 50), ObjectTypes = make_set(ObjectType, 25), SourceIPs = make_set(IP, 25) by User
  | where DeniedCount >= 10
  | extend AccountCustomEntity = User
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

UtimacoESKMKmipServerLogs_CL

Stage 2: where

| where TimeGenerated > ago(30m)

Stage 3: where

| where Reason == "PERMISSION_DENIED"

Stage 4: where

| where isnotempty(User)

Stage 5: summarize

| summarize DeniedCount = count(), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Operations = make_set(Operation, 50), ObjectTypes = make_set(ObjectType, 25), SourceIPs = make_set(IP, 25) by User

Stage 6: where

| where DeniedCount >= 10

Stage 7: extend

| extend AccountCustomEntity = User

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
DeniedCountge
  • 10 transforms: number
field:"DeniedCount" kind:ge value:"10"
Reasoneq
  • PERMISSION_DENIED
field:"Reason" kind:eq value:"PERMISSION_DENIED"
Useris_not_null
  • (no value, null check)
field:"user" kind:is_not_null

Output Fields

These fields are emitted when the rule matches.

FieldSource
DeniedCountsummarize
EndTimesummarize
ObjectTypessummarize
Operationssummarize
SourceIPssummarize
StartTimesummarize
Usersummarize
AccountCustomEntityextend

Veeam ONE Possible Ransomware Activity (Hyper-V)

#

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

Detects Veeam ONE possible ransomware activity alerts for Microsoft Hyper-V.

Rule Body

id: d48f9671-adc0-45e5-a477-670ef38bc132
name: Veeam ONE Possible Ransomware Activity (Hyper-V)
description: Detects Veeam ONE possible ransomware activity alerts for Microsoft Hyper-V.
severity: High
status: Available
requiredDataConnectors:
- connectorId: VeeamCustomTablesDataConnector
  dataTypes:
  - VeeamOneTriggeredAlarms_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: VeeamOneTriggeredAlarms_CL | where PredefinedAlarmId == 344
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 == 344

Indicators

These rows show field, operator, and value matches.

Whisper Security - ASN Reputation Degradation

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

Detects autonomous systems whose reputation score has increased by more than 20 points in the last 24 hours. Higher scores indicate worse reputation, so a score increase signals degradation that may indicate adversary use of the network.

MITRE ATT&CK Coverage

TacticTechniques
Resource Development

Rule Body

id: 32e7bcab-4424-516e-9c7c-dbe868144494
name: Whisper Security - ASN Reputation Degradation
description: |
  Detects autonomous systems whose reputation score has increased by more than 20 points in the last 24 hours. Higher scores indicate worse reputation, so a score increase signals degradation that may indicate adversary use of the network.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: WhisperSecurityConnector
  dataTypes:
  - WhisperASNReputation_CL
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- ResourceDevelopment
relevantTechniques:
- T1583
query: |
  // MITRE ATT&CK: T1583.002 - Acquire Infrastructure: DNS Server
  // Tactic: Resource Development
  // Detects ASN reputation degradation (score increase > 20 points in 24h)
  // Note: Higher reputationScore = worse reputation
  let degradationThreshold = 20;
  let currentScores = WhisperASNReputation_CL
      | where TimeGenerated > ago(1h)
      | summarize (LatestTime, CurrentScore, CurrentLevel, AsnName, Country, PrefixCount, PeerCount) = arg_max(TimeGenerated, reputationScore, reputationLevel, asnName, country, prefixCount, peerCount) by asn
      | project asn, CurrentScore, CurrentLevel, AsnName, Country, PrefixCount, PeerCount;
  let previousScores = WhisperASNReputation_CL
      | where TimeGenerated between (ago(1d) .. ago(1h))
      | summarize (PreviousTime, PreviousScore) = arg_max(TimeGenerated, reputationScore) by asn
      | project asn, PreviousScore;
  currentScores
      | join kind=inner (previousScores) on asn
      | extend ScoreDelta = CurrentScore - PreviousScore
      | where ScoreDelta > degradationThreshold
      | project TimeGenerated = now(), ASN = asn, AsnName, OldScore = PreviousScore, NewScore = CurrentScore, ScoreDelta, CurrentLevel, Country, PrefixCount, PeerCount
customDetails:
  ASN: ASN
  AsnName: AsnName
  OldScore: OldScore
  NewScore: NewScore
  ScoreDelta: ScoreDelta
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let degradationThreshold = 20;

let currentScores is inlined into the numbered stages below.

Let Binding: previousScores used in Stage 5

let previousScores = WhisperASNReputation_CL
    | where TimeGenerated between (ago(1d) .. ago(1h))
    | summarize (PreviousTime, PreviousScore) = arg_max(TimeGenerated, reputationScore) by asn
    | project asn, PreviousScore;

Stages 1 to 4 define let currentScores as the rule's main pipeline source. Stages 5 to 8 use it.

Stage 1: source

WhisperASNReputation_CL

Stage 2: where

| where TimeGenerated > ago(1h)

Stage 3: summarize

| summarize (LatestTime, CurrentScore, CurrentLevel, AsnName, Country, PrefixCount, PeerCount) = arg_max(TimeGenerated, reputationScore, reputationLevel, asnName, country, prefixCount, peerCount) by asn

Stage 4: project

| project asn, CurrentScore, CurrentLevel, AsnName, Country, PrefixCount, PeerCount

Stage 5: join

currentScores
| join kind=inner (previousScores) on asn

Stage 6: extend

extend ScoreDelta

Stage 7: where

where ScoreDelta > 20

Stage 8: project

project ASN, AsnName, Country, CurrentLevel, NewScore, OldScore, PeerCount, PrefixCount, ScoreDelta, TimeGenerated

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ScoreDeltagt
  • 20 transforms: number
field:"ScoreDelta" kind:gt value:"20"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ASNproject
AsnNameproject
Countryproject
CurrentLevelproject
NewScoreproject
OldScoreproject
PeerCountproject
PrefixCountproject
ScoreDeltaproject
TimeGeneratedproject

Back to all Kusto rules