Detection rules › Kusto
Kusto rules: 0e
Accessed files shared by temporary external user
#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
| Tactic | Techniques |
|---|---|
| Initial Access |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft 365 | FileAccessed |
| Microsoft 365 | FileUploaded |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
FileAccessCount | gt |
| field:"FileAccessCount" kind:gt value:"10" |
MemberAdded | contains |
| field:"MemberAdded" kind:contains value:"#EXT#" |
OfficeWorkload | eq |
| field:"m365::Workload" kind:eq |
Operation | eq |
| field:"Operation" kind:eq |
RecordType | eq |
| field:"RecordType" kind:eq value:"SharePointFileOperation" |
SourceRelativeUrl | match |
| field:"SourceRelativeUrl" kind:match value:"Microsoft Teams Chat Files" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
OfficeObjectId | summarize |
AWSCloudTrail - Created CRUD S3 policy and then privilege escalation
#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
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
- ASL AWS Create Policy Version to allow all resources (Splunk)
- AWS IAM CompromisedKeyQuarantine Policy Attached to User (Elastic)
- AWS Lateral Movement from Kubernetes SA via AssumeRoleWithWebIdentity (Elastic)
- AWSCloudTrail - CloudFormation policy created then used for privilege escalation (Kusto)
- AWSCloudTrail - Creation of CRUD DynamoDB policy and then privilege escalation (Kusto)
- AWSCloudTrail - Creation of CRUD KMS policy and then privilege escalation (Kusto)
- AWSCloudTrail - Creation of CRUD Lambda policy and then privilege escalation (Kusto)
- AWSCloudTrail - Creation of DataPipeline policy and then privilege escalation (Kusto)
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
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.
| Field | Kind | Values | Search |
|---|---|---|---|
Action | contains |
| field:"Action" kind:contains |
Effect | eq |
| field:"Effect" kind:eq value:"Allow" |
ErrorCode | is_null | field:"ErrorCode" kind:is_null | |
ErrorMessage | is_null | field:"ErrorMessage" kind:is_null | |
EventName | in |
| field:"aws::eventName" kind:in |
Resource | eq |
| field:"Resource" kind:eq value:"*" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | summarize |
AccountUPNSuffix | summarize |
EventName | summarize |
EventSource | summarize |
PolicyName | summarize |
RecipientAccountId | summarize |
SourceIpAddress | summarize |
UserIdentityArn | summarize |
UserIdentityType | summarize |
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.
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.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"T0SyncLAPSPassword" |
Cisco Umbrella - Connection to Unpopular Website Detected
#'Detects first connection to an unpopular website (possible malicious payload delivery).'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control | No 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
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.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | eq |
| field:"EventType" kind:eq value:"proxylogs" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
Hostname | eq | top_million_list | excludes:Hostname field:"Hostname" value:"top_million_list" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstIpAddr | project |
Message | project |
SrcIpAddr | project |
TimeGenerated | project |
UrlOriginal | project |
Common Event Format (CEF) via AMA - Critical Severity Detection
#'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
| Tactic | Techniques |
|---|---|
| Execution | |
| Impact |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | eq |
| field:"EventType" kind:eq value:"DetectionSummaryEvent" |
Severity | eq |
| field:"Severity" kind:eq value:"Critical" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | project |
Activity | project |
DstHostName | project |
DstUserName | project |
EndTimeUtc | project |
FileHash | project |
FileHashAlgo | project |
FileHashCustomEntity | project |
FileName | project |
FilePath | project |
HostCustomEntity | project |
IPCustomEntity | project |
Message | project |
SrcIpAddr | project |
StartTimeUtc | project |
Technique | project |
Total | project |
timestamp | project |
CyberArkEPM - Unexpected executable location
#'Detects program run from unexpected location.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
ActingProcessName | match |
| field:"ParentImage" kind:match |
EventSubType | ne |
| field:"EventSubType" kind:ne value:"AttackAttempt" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActingProcessFileInternalName | project |
ActorUsername | project |
EventEndTime | project |
EventMessage | project |
AccountCustomEntity | extend |
Dataverse - Suspicious use of Web API
#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
| Tactic | Techniques |
|---|---|
| Reconnaissance | |
| Execution | |
| Discovery | |
| Exfiltration |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
AppId | eq |
| field:"AppId" kind:eq value:"51f81489-12ee-4a9e-aaae-a2591f45987d" |
InstanceCount | gt |
| field:"InstanceCount" kind:gt value:"10" |
Message | eq |
| field:"Message" kind:eq value:"UserSignIn" |
ResourceIdentity | eq |
| field:"ResourceIdentity" kind:eq value:"00000007-0000-0000-c000-000000000000" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | project |
AppDisplayName | project |
AppId | project |
ClientIp | project |
CloudAppId | project |
FirstSeen | project |
InstanceUrl | project |
UPNSuffix | project |
UserId | project |
Fortiweb - WAF Allowed threat
#'Detects WAF "Allowed" action on threat events.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
DvcAction | eq |
| field:"DvcAction" kind:eq value:"Allow" |
EventType | eq |
| field:"EventType" kind:eq value:"Attack" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SrcIpAddr | summarize |
GCP Security Command Center - Detect Firewall rules allowing unrestricted high-risk ports
#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
| Tactic | Techniques |
|---|---|
| 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 =MinPort =~ MaxPorttostring(MinPort)strcat(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.
| Field | Kind | Values | Search |
|---|---|---|---|
FindingCategory | eq |
| field:"FindingCategory" kind:eq value:"OPEN_FIREWALL" |
Findings.state | eq |
| field:"Findings.state" kind:eq value:"ACTIVE" |
FindingsJson.state | eq |
| field:"FindingsJson.state" kind:eq value:"ACTIVE" |
MaxPort | in |
| field:"MaxPort" kind:in |
MinPort | in |
| field:"MinPort" kind:in |
SourcePropertiesJson.ExternalSourceRanges | contains |
| field:"SourcePropertiesJson.ExternalSourceRanges" kind:contains value:"0.0.0.0/0" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AttackExposureScore | summarize |
Description | summarize |
ExternalUri | summarize |
FindingName | summarize |
FirewallName | summarize |
OpenHighRiskPorts | extend |
ProjectName | summarize |
Protocols | extend |
ResourceName | summarize |
Severity | summarize |
TimeGenerated | summarize |
GTI - Initial Access Broker Alert Detected
#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
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Credential Access |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
DetailType | eq |
| field:"DetailType" kind:eq value:"initial_access_broker" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AiSummary | project |
AlertId | project |
AlertName | project |
AlertState | project |
AuditCreateTime | project |
AuditUpdateTime | project |
BrokerSeverity | project |
CommonThemes | project |
DiscoveryDocuments | project |
DistinctThemes | project |
FindingCount | project |
PriorityLevel | project |
PriorityReasoning | project |
RelevanceConfidence | project |
RelevanceLevel | project |
RelevanceReasoning | project |
SeverityConfidence | project |
SeverityLevel | project |
SeverityReasoning | project |
TimeGenerated | project |
Guardian- No LLM Output Policy Violation Detection
#'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.
| Field | Kind | Values | Search |
|---|---|---|---|
PolicyViolatedControlFeature | eq |
| field:"PolicyViolatedControlFeature" kind:eq value:"No LLM Output" |
Severity | eq |
| field:"Severity" kind:eq value:"Low" |
GWorkspace - Alert events
#'Detects alert events.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | match |
| field:"EventType" kind:match value:"ALERT_CENTER" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
KMS Server Deleted
#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.
| Field | Kind | Values | Search |
|---|---|---|---|
instanceId | eq |
| field:"instanceId" kind:eq value:"42301" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DataSource | project |
Date | project |
EventId | project |
MessageDetails | project |
Object Name | project |
Severity | project |
User Name | project |
NGINX - Put file and get file from same IP address
#'Detects put or get files from one source in short timeframe'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
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
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.
| Field | Source |
|---|---|
SrcIpAddr | project |
UrlOriginal | project |
get_time | project |
post_time | project |
second | project |
IPCustomEntity | extend |
UrlCustomEntity | extend |
Process Creation with Suspicious CommandLine Arguments
#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
| Tactic | Techniques |
|---|---|
| Execution | |
| Stealth |
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 =TargetUsernameType == "Windows"tostring(split(TargetUsername, '\\')[1])TargetUsernameIndicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CommandLineArgs | contains |
| field:"CommandLineArgs" kind:contains value:"base64" |
CommandLineArgs | is_not_null | field:"CommandLineArgs" kind:is_not_null | |
EventType | eq |
| field:"EventType" kind:eq value:"ProcessCreated" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
CommandLine | project |
DvcDomain | project |
DvcHostname | project |
DvcIpAddr | project |
TargetProcessId | project |
TargetProcessName | project |
TargetUsername | project |
TargetUsernameType | project |
TimeGenerated | project |
Username | extend |
NTDomain | extend |
UPNSuffix | extend |
PROD (TM016.1) - GROUP - Added to Well-Known or Tier Model Group
#Well-Known Groups are classified as Tier 0, and Tier Model groups that provide permission over Tier Model objects.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 5136: A directory service object was modified. |
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
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.
| Field | Kind | Values | Search |
|---|---|---|---|
EventData | match |
| field:"EventData" kind:match |
EventID | eq |
| field:"EventID" kind:eq value:"5136" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account | project |
ActivityType | project |
Channel | project |
Computer | project |
Domain | project |
EventData | project |
EventID | project |
GroupName | project |
GroupTier | project |
ObjectName | project |
ObjectTier | project |
ObjectType | project |
OperationType | project |
TimeGenerated | project |
Radiflow - Platform Alert
#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
| Tactic | Techniques |
|---|---|
| Initial Access | No specific technique |
| Execution | No specific technique |
| Privilege Escalation | No specific technique |
| Lateral Movement | No specific technique |
| Command & Control | No specific technique |
| Exfiltration | No 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.
| Field | Kind | Values | Search |
|---|---|---|---|
DeviceProduct | eq |
| field:"DeviceProduct" kind:eq value:"iSID" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
EventClassID | in | 100, 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 Activity | excludes:EventClassID |
RecordedFuture Threat Hunting Domain All Actors
#Recorded Future Threat Hunting domain correlation for all actors.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Command & Control |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Sysmon | Event 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.
| Field | Kind | Values | Search |
|---|---|---|---|
Data.description | starts_with |
| field:"Data.description" kind:starts_with value:"Recorded Future - Threat Hunt" |
Domain | is_not_null | field:"Domain" kind:is_not_null | |
IsActive | eq |
| field:"IsActive" kind:eq value:"true" |
ObservableKey | eq |
| field:"ObservableKey" kind:eq value:"domain-name:value" |
ObservableValue | is_not_null | field:"ObservableValue" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Id | summarize |
TI Map IP Entity to AzureActivity
#This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in AzureActivity.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| 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 =isnotempty(NetworkSourceIP)NetworkSourceIPNetworkSourceIPStage 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.
| Field | Kind | Values | Search |
|---|---|---|---|
IndicatorType | in |
| field:"IndicatorType" kind:in |
IsActive | eq |
| field:"IsActive" kind:eq value:"true" |
ValidUntil | is_null | field:"ValidUntil" kind:is_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
TI_ipEntity | cidr_match | 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8 | excludes:TI_ipEntity |
TI_ipEntity | starts_with | 127. | excludes:TI_ipEntity field:"TI_ipEntity" value:"127." |
TI_ipEntity | starts_with | :: | excludes:TI_ipEntity field:"TI_ipEntity" value:"::" |
TI_ipEntity | starts_with | fe80 | excludes:TI_ipEntity field:"TI_ipEntity" value:"fe80" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActivityGroupNames | project |
ActivityStatusValue | project |
AzureActivity_TimeGenerated | project |
Caller | project |
CallerIpAddress | project |
CategoryValue | project |
Confidence | project |
Description | project |
Id | project |
NetworkSourceIP | project |
OperationNameValue | project |
ResourceId | project |
TI_ipEntity | project |
Type | project |
Url | project |
ValidUntil | project |
timestamp | extend |
Name | extend |
UPNSuffix | extend |
AadUserId | extend |
User Session Impersonation(Okta)
#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
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Okta | System Log event type user.session.impersonation.initiate: Impersonation session initiated |
Rules Detecting the Same Action
These rules filter on the same operation.
- Okta Support Access (Panther)
- Okta Support Access Granted (Panther)
- Okta User Session Impersonation (Elastic)
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.
| Field | Kind | Values | Search |
|---|---|---|---|
TargetUser_alternateId | is_not_null | field:"TargetUser_alternateId" kind:is_not_null | |
TargetUser_alternateId | ne |
| field:"TargetUser_alternateId" kind:ne value:"unknown" |
eventType_s | eq |
| field:"okta::eventType" kind:eq value:"user.session.impersonation.initiate" |
outcome_result_s | eq |
| field:"okta::outcome.result" kind:eq value:"SUCCESS" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
TargetUser_alternateId | project |
TargetUser_displayName | project |
TargetUser_id | project |
TargetUser_type | project |
TimeGenerated | project |
actor_alternateId_s | project |
actor_displayName_s | project |
eventType_s | project |
outcome_result_s | project |
AccountName | extend |
AccountUPNSuffix | extend |
Utimaco ESKM - PERMISSION_DENIED burst for a KMIP user
#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
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
DeniedCount | ge |
| field:"DeniedCount" kind:ge value:"10" |
Reason | eq |
| field:"Reason" kind:eq value:"PERMISSION_DENIED" |
User | is_not_null | field:"user" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DeniedCount | summarize |
EndTime | summarize |
ObjectTypes | summarize |
Operations | summarize |
SourceIPs | summarize |
StartTime | summarize |
User | summarize |
AccountCustomEntity | extend |
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.
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.
| Field | Kind | Values | Search |
|---|---|---|---|
PredefinedAlarmId | eq |
| field:"PredefinedAlarmId" kind:eq value:"344" |
Whisper Security - ASN Reputation Degradation
#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
| Tactic | Techniques |
|---|---|
| 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
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.
| Field | Kind | Values | Search |
|---|---|---|---|
ScoreDelta | gt |
| field:"ScoreDelta" kind:gt value:"20" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ASN | project |
AsnName | project |
Country | project |
CurrentLevel | project |
NewScore | project |
OldScore | project |
PeerCount | project |
PrefixCount | project |
ScoreDelta | project |
TimeGenerated | project |