Detection rules › Kusto
Kusto rules: 3c
1Password - Vault export post account creation
#This will alert when a successful vault export has occurred within 14 days of a new account being created within 1Password. Ref: https://1password.com/ Ref: https://github.com/securehats/
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Credential Access |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| 1Password | vault-export: Export Vault |
Rule Body
id: 969e2e5c-9cc6-423c-a3de-514f7ad75fe7
name: 1Password - Vault export post account creation
version: 1.0.0
kind: Scheduled
description: |-
This will alert when a successful vault export has occurred within 14 days of a new account being created within 1Password.
Ref: https://1password.com/
Ref: https://github.com/securehats/
severity: Medium
requiredDataConnectors:
- connectorId: 1Password
dataTypes:
- OnePasswordEventLogs_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
- Persistence
relevantTechniques:
- T1555
- T1136
query: |-
let ruleFrequency = 1h;
let lookback = 14d;
let onePasswordUserCreationCheck =
OnePasswordEventLogs_CL
| where TimeGenerated between (ago(lookback) .. ago(now() - ago(ruleFrequency)))
| extend creationActivityFound = iff(action == "activate" and object_type == "user", bool(1), bool(0))
;
OnePasswordEventLogs_CL
| where action == "export" and object_type == "vault"
| extend userUuid = tostring(actor_details.uuid)
| join (
onePasswordUserCreationCheck
| where creationActivityFound == true
| extend userUuid = tostring(object_details.uuid)
)
on $left.userUuid == $right.userUuid
| extend
TargetUsername = object_details1.email
, SrcIpAddr = session1.ip_address
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetUsername
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1h
matchingMethod: AllEntities
suppressionDuration: 5h
eventGroupingSettings:
aggregationKind: SingleAlert
Stages and Predicates
Parameters
let ruleFrequency = 1h;
let lookback = 14d;
Let Binding: onePasswordUserCreationCheck
let onePasswordUserCreationCheck = OnePasswordEventLogs_CL
| where TimeGenerated between (ago(lookback) .. ago(now() - ago(ruleFrequency)))
| extend creationActivityFound = iff(action == "activate" and object_type == "user", bool(1), bool(0));
Stage 1: source
OnePasswordEventLogs_CL
Stage 2: where
| where action == "export" and object_type == "vault"
Stage 3: extend
| extend userUuid = tostring(actor_details.uuid)
Stage 4: join
| join (
onePasswordUserCreationCheck
| where creationActivityFound == true
| extend userUuid = tostring(object_details.uuid)
)
on $left.userUuid == $right.userUuid
Stage 5: extend
extend SrcIpAddr, TargetUsername
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"export" |
creationActivityFound | eq |
| field:"creationActivityFound" kind:eq value:"true" |
object_type | eq |
| field:"object_type" kind:eq value:"vault" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
userUuid | extend |
SrcIpAddr | extend |
TargetUsername | extend |
[Entra ID] Application Assigned Administrator Permissions Immediately After Obtaining Role Management Permissions
#Detects an application receiving administrator permissions and then using them to assign a role. This pattern can indicate rapid privilege escalation through app consent abuse.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Persistence | |
| Privilege Escalation | |
| Stealth |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Azure | Add app role assignment to service principal |
Rules Detecting the Same Action
These rules filter on the same operation.
- [Entra ID] Application Granted Administrative Permission to Assign Microsoft Entra ID Roles (Kusto)
- [Entra ID] Mass Privileged Role Change Activity Detected (Kusto)
- [Entra ID] Privilege Elevation Request Denied (Kusto)
- [Entra ID] Privileged Role Assigned to a New User (Kusto)
- [Entra ID] Privileged Role Assigned to User (Kusto)
- Account Elevated to New Role (Kusto)
- Added Credentials to Existing Application (Sigma)
- Added Owner To Application (Sigma)
Rule Body
id: 90bacdf4-e35b-47cb-92d1-29d8397eb4a0
name: "[Entra ID] Application Assigned Administrator Permissions Immediately After Obtaining Role Management Permissions"
version: 1.0.0
kind: Scheduled
description: |
Detects an application receiving administrator permissions and then using them to assign a role. This pattern can indicate rapid privilege escalation through app consent abuse.
severity: High
status: Available
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 1h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- PrivilegeEscalation
- DefenseEvasion
- InitialAccess
relevantTechniques:
- T1078.004
- T1098.003
query: |
let query_frequency = 1h;
let query_period = 2h;
AuditLogs
| where TimeGenerated > ago(query_period)
| where Category =~ "ApplicationManagement" and LoggedByService =~ "Core Directory"
| where OperationName =~ "Add app role assignment to service principal"
| mv-expand TargetResource = TargetResources
| mv-expand modifiedProperty = TargetResource["modifiedProperties"]
| where tostring(modifiedProperty["displayName"]) == "AppRole.Value"
| extend PermissionGrant = tostring(modifiedProperty["newValue"])
| where PermissionGrant has "RoleManagement.ReadWrite.Directory"
| mv-apply modifiedProperty = TargetResource["modifiedProperties"] on (
summarize modifiedProperties = make_bag(
bag_pack(
tostring(modifiedProperty["displayName"]),
bag_pack(
"oldValue",
trim(@'[\"\s]+', tostring(modifiedProperty["oldValue"])),
"newValue",
trim(@'[\"\s]+', tostring(modifiedProperty["newValue"]))
)
),
100
)
)
| project
PermissionGrant_TimeGenerated = TimeGenerated,
PermissionGrant_OperationName = OperationName,
PermissionGrant_Result = Result,
PermissionGrant,
AppDisplayName = tostring(modifiedProperties["ServicePrincipal.DisplayName"]["newValue"]),
AppServicePrincipalId = tostring(modifiedProperties["ServicePrincipal.ObjectID"]["newValue"]),
PermissionGrant_InitiatedBy = InitiatedBy,
PermissionGrant_TargetResources = TargetResources,
PermissionGrant_AdditionalDetails = AdditionalDetails,
PermissionGrant_CorrelationId = CorrelationId
| join kind=inner (
AuditLogs
| where TimeGenerated > ago(query_frequency)
| where Category =~ "RoleManagement"
and LoggedByService =~ "Core Directory"
and AADOperationType =~ "Assign"
| where isnotempty(InitiatedBy["app"])
| mv-expand TargetResource = TargetResources
| mv-expand modifiedProperty = TargetResource["modifiedProperties"]
| where tostring(modifiedProperty["displayName"]) in ("Role.DisplayName", "RoleDefinition.DisplayName")
| extend RoleAssignment = tostring(modifiedProperty["newValue"])
| where RoleAssignment contains "Admin"
| project
RoleAssignment_TimeGenerated = TimeGenerated,
RoleAssignment_OperationName = OperationName,
RoleAssignment_Result = Result,
RoleAssignment,
TargetType = tostring(TargetResources[0]["type"]),
Target = iff(isnotempty(TargetResources[0]["displayName"]), tostring(TargetResources[0]["displayName"]), tolower(TargetResources[0]["userPrincipalName"])),
TargetId = tostring(TargetResources[0]["id"]),
RoleAssignment_InitiatedBy = InitiatedBy,
RoleAssignment_TargetResources = TargetResources,
RoleAssignment_AdditionalDetails = AdditionalDetails,
RoleAssignment_CorrelationId = CorrelationId,
AppServicePrincipalId = tostring(InitiatedBy["app"]["servicePrincipalId"])
)
on AppServicePrincipalId
| where PermissionGrant_TimeGenerated < RoleAssignment_TimeGenerated
| extend
TargetName = tostring(split(Target, "@")[0]),
TargetUPNSuffix = tostring(split(Target, "@")[1])
| project
Alert_Category_en = "Entra ID",
Alert_SubCategory_en = "Anomaly Identity Privilege Modification",
Alert_Name_en = "Application Assigned Administrator Permissions Immediately After Obtaining Role Management Permissions",
Alert_Description_en = strcat(
"At Taiwan time",
format_datetime(datetime_utc_to_local(RoleAssignment_TimeGenerated, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
". ",
"EntraID:",
"",
". ",
"detected initiator: ",
tostring(PermissionGrant_InitiatedBy.user.userPrincipalName),
" granted application: ",
AppDisplayName,
" permission: ",
PermissionGrant,
"afterward, the application immediately assigned a role to target: ",
Target,
"role assignment: ",
RoleAssignment
),
Alert_TriageStep_en = strcat(
"1. Confirm initiator: ",
tostring(PermissionGrant_InitiatedBy.user.userPrincipalName),
"IP:",
tostring(PermissionGrant_InitiatedBy.ipAddress),
", and review the authorized application: ",
AppDisplayName,
", obtained permission: ",
PermissionGrant,
", to determine whether it is expected behavior/automation. ",
"2. Review application: ",
AppDisplayName,
" for target: ",
Target,
" role assignment content: ",
RoleAssignment,
", to determine whether it is expected behavior/automation. "
),
Alert_Containment_en = strcat(
"1. If determined to be unauthorized behavior, immediately disable initiator: ",
tostring(PermissionGrant_InitiatedBy.user.userPrincipalName),
" and application: ",
AppDisplayName,
", and revoke its Directory/RoleManagement-related permissions. ",
"2. Immediately revert/remove the improper administrator role assignment: ",
Target,
", and review whether other role/group/application assignments were changed."
),
Alert_Remediation_en =
"1. Strengthen Conditional Access and account governance: enforce MFA/managed device and named location restrictions for administrator accounts, and block abnormal IPs. "
"2. Implement least privilege and PIM: use Just-In-Time (JIT) activation with approval for administrator roles to reduce the risk of immediate abuse after account compromise. "
"3. Strengthen detection and automation: establish automated response SOAR (notification/revoke authorization/disable application/revert role/block source).",
Alert_Time_TW = datetime_utc_to_local(RoleAssignment_TimeGenerated, "Asia/Taipei"),
Alert_Time_UTC0 = RoleAssignment_TimeGenerated,
Event_Action = RoleAssignment_OperationName,
Event_Status = RoleAssignment_Result,
Event_Description = strcat("initiator: ", tostring(PermissionGrant_InitiatedBy.user.userPrincipalName), " to application: ", AppDisplayName, ", ", PermissionGrant_OperationName, ". Subsequently, application: ", AppDisplayName, " for target ", Target, ", ", RoleAssignment_OperationName),
Source_Identity_FullName = AppDisplayName,
Source_Identity_ID = AppServicePrincipalId,
Source_Identity_Type = "Service",
Target_Identity_FullName = Target,
Target_Identity_ID = TargetId,
Target_Identity_Name = TargetName,
Target_Identity_Type = iff(TargetType has "User", "User", "Service"),
Target_Resource_ID = "",
Target_Resource_Name = "Microsoft Entra ID",
Target_Resource_Type = "Microsoft Entra ID"
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Source_Identity_FullName
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: Target_Identity_FullName
alertDetailsOverride:
alertDynamicProperties: []
Stages and Predicates
Parameters
let query_frequency = 1h;
let query_period = 2h;
Stage 1: source
AuditLogs
Stage 2: where
| where TimeGenerated > ago(query_period)
Stage 3: where
| where Category =~ "ApplicationManagement" and LoggedByService =~ "Core Directory"
Stage 4: where
| where OperationName =~ "Add app role assignment to service principal"
Stage 5: mv-expand
| mv-expand TargetResource = TargetResources
Stage 6: mv-expand
| mv-expand modifiedProperty = TargetResource["modifiedProperties"]
Stage 7: where
| where tostring(modifiedProperty["displayName"]) == "AppRole.Value"
Stage 8: extend
| extend PermissionGrant = tostring(modifiedProperty["newValue"])
Stage 9: where
| where PermissionGrant has "RoleManagement.ReadWrite.Directory"
Stage 10: kusto:mv-apply
| mv-apply modifiedProperty = TargetResource["modifiedProperties"] on (
summarize modifiedProperties = make_bag(
bag_pack(
tostring(modifiedProperty["displayName"]),
bag_pack(
"oldValue",
trim(@'[\"\s]+', tostring(modifiedProperty["oldValue"])),
"newValue",
trim(@'[\"\s]+', tostring(modifiedProperty["newValue"]))
)
),
100
)
)
Stage 11: project
| project
PermissionGrant_TimeGenerated = TimeGenerated,
PermissionGrant_OperationName = OperationName,
PermissionGrant_Result = Result,
PermissionGrant,
AppDisplayName = tostring(modifiedProperties["ServicePrincipal.DisplayName"]["newValue"]),
AppServicePrincipalId = tostring(modifiedProperties["ServicePrincipal.ObjectID"]["newValue"]),
PermissionGrant_InitiatedBy = InitiatedBy,
PermissionGrant_TargetResources = TargetResources,
PermissionGrant_AdditionalDetails = AdditionalDetails,
PermissionGrant_CorrelationId = CorrelationId
Stage 12: join
| join kind=inner (
AuditLogs
| where TimeGenerated > ago(query_frequency)
| where Category =~ "RoleManagement"
and LoggedByService =~ "Core Directory"
and AADOperationType =~ "Assign"
| where isnotempty(InitiatedBy["app"])
| mv-expand TargetResource = TargetResources
| mv-expand modifiedProperty = TargetResource["modifiedProperties"]
| where tostring(modifiedProperty["displayName"]) in ("Role.DisplayName", "RoleDefinition.DisplayName")
| extend RoleAssignment = tostring(modifiedProperty["newValue"])
| where RoleAssignment contains "Admin"
| project
RoleAssignment_TimeGenerated = TimeGenerated,
RoleAssignment_OperationName = OperationName,
RoleAssignment_Result = Result,
RoleAssignment,
TargetType = tostring(TargetResources[0]["type"]),
Target = iff(isnotempty(TargetResources[0]["displayName"]), tostring(TargetResources[0]["displayName"]), tolower(TargetResources[0]["userPrincipalName"])),
TargetId = tostring(TargetResources[0]["id"]),
RoleAssignment_InitiatedBy = InitiatedBy,
RoleAssignment_TargetResources = TargetResources,
RoleAssignment_AdditionalDetails = AdditionalDetails,
RoleAssignment_CorrelationId = CorrelationId,
AppServicePrincipalId = tostring(InitiatedBy["app"]["servicePrincipalId"])
)
on AppServicePrincipalId
Stage 13: where
where PermissionGrant_TimeGenerated < RoleAssignment_TimeGenerated
Stage 14: extend
extend TargetName, TargetUPNSuffix
Stage 15: project
project Alert_Category_en, Alert_Containment_en, Alert_Description_en, Alert_Name_en, Alert_Remediation_en, Alert_SubCategory_en, Alert_Time_TW, Alert_Time_UTC0, Alert_TriageStep_en, Event_Action, Event_Description, Event_Status, Source_Identity_FullName, Source_Identity_ID, Source_Identity_Type, Target_Identity_FullName, Target_Identity_ID, Target_Identity_Name, Target_Identity_Type, Target_Resource_ID, Target_Resource_Name, Target_Resource_Type
Stage 16: summarize aggregation inside the mv-apply branch
summarize
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AADOperationType | eq |
| field:"AADOperationType" kind:eq value:"Assign" |
Category | eq |
| field:"Category" kind:eq |
InitiatedBy["app"] | is_not_null | ||
LoggedByService | eq |
| field:"azure_ad::logged_by_service" kind:eq |
OperationName | eq |
| field:"OperationName" kind:eq value:"Add app role assignment to service principal" |
PermissionGrant | match |
| field:"PermissionGrant" kind:match value:"RoleManagement.ReadWrite.Directory" |
RoleAssignment | contains |
| field:"RoleAssignment" kind:contains value:"Admin" |
modifiedProperty["displayName"] | eq |
| |
modifiedProperty["displayName"] | in |
|
BitSight - diligence risk category detected
#'Rule helps to detect whenever there is a diligence risk category found in BitSight.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance | |
| Execution |
Rule Body
id: 161ed3ac-b242-4b13-8c6b-58716e5e9972
name: BitSight - diligence risk category detected
description: |
'Rule helps to detect whenever there is a diligence risk category found in BitSight.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BitSight
dataTypes:
- BitSightFindingsData
queryFrequency: 1d
queryPeriod: 24h
triggerOperator: GreaterThan
triggerThreshold: 0
tactics:
- Execution
- Reconnaissance
relevantTechniques:
- T1203
- T1595.002
query: |
let timeframe = 24h;
BitSightFindingsData
| where ingestion_time() > ago(timeframe)
| where RiskCategory == "Diligence"
| extend Severity = toreal(Severity)
| extend Severity = case( Severity <= 6.9 and Severity >= 4.0, "Low",
Severity <= 8.9 and Severity >= 7.0, "Medium",
Severity <= 10.0 and Severity >= 9.0, "High",
"Informational")
| project FirstSeen, CompanyName, Severity, RiskCategory, TemporaryId, RiskVector
incidentConfiguration:
createIncident: false
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: 'BitSight: Alert for {{RiskVector}} in {{CompanyName}} from BitSight'
alertDescriptionFormat: 'Alert is generated for {{CompanyName}}.\n\nRisk Vector: {{RiskVector}}\nTemporaryId: {{TemporaryId}}\nRisk Category: Diligence'
alertSeverityColumnName: Severity
entityMappings:
- entityType: Malware
fieldMappings:
- identifier: Name
columnName: RiskVector
- identifier: Category
columnName: RiskCategory
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let timeframe = 24h;
Stage 1: source
BitSightFindingsData
Stage 2: where
| where ingestion_time() > ago(timeframe)
Stage 3: where
| where RiskCategory == "Diligence"
Stage 4: extend
| extend Severity = toreal(Severity)
Stage 5: extend
| extend Severity = case( Severity <= 6.9 and Severity >= 4.0, "Low",
Severity <= 8.9 and Severity >= 7.0, "Medium",
Severity <= 10.0 and Severity >= 9.0, "High",
"Informational")
Severity =Severity <= 6.9 and Severity >= 4.0"Low"Severity <= 8.9 and Severity >= 7.0"Medium"Severity <= 10.0 and Severity >= 9.0"High""Informational"Stage 6: project
| project FirstSeen, CompanyName, Severity, RiskCategory, TemporaryId, RiskVector
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
RiskCategory | eq |
| field:"RiskCategory" kind:eq value:"Diligence" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
CompanyName | project |
FirstSeen | project |
RiskCategory | project |
RiskVector | project |
Severity | project |
TemporaryId | project |
Cisco WSA - Internet access from public IP
#'Detects internet access from public IP.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 4250b050-e1c6-4926-af04-9484bbd7e94f
name: Cisco WSA - Internet access from public IP
description: |
'Detects internet access from public IP.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1189
query: |
let ip_except = dynamic(['127.0.0.2']); //Add exceptions to this list
CiscoWSAEvent
| where ipv4_is_private(SrcIpAddr) == false
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let ip_except = dynamic(['127.0.0.2']);
Stage 1: source
CiscoWSAEvent
Stage 2: where
| where ipv4_is_private(SrcIpAddr) == false
Stage 3: extend
| extend IPCustomEntity = SrcIpAddr
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
SrcIpAddr | 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:SrcIpAddr |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
IPCustomEntity | extend |
Cognni Incidents for Low Sensitivity Legal Information
#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.
'Display incidents in which low sensitivity legal information was placed at risk by user sharing.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Collection |
Rule Body
id: 8374ec0f-d857-4c17-b1e7-93d11800f8fb
name: Cognni Incidents for Low Sensitivity Legal Information
description: |
'Display incidents in which low sensitivity legal information was placed at risk by user sharing.'
severity: Low
requiredDataConnectors:
- connectorId: CognniSentinelDataConnector
dataTypes:
- CognniIncidents_CL
queryFrequency: 5h
queryPeriod: 5h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- Collection
relevantTechniques:
- T1530
query: |
let lowRisk = 1;
let legal = 'Legal Information';
CognniIncidents_CL
| where Severity == lowRisk
| where informationType_s == legal
| where TimeGenerated >= ago(5h)
| extend AccountCustomEntity = userId_s
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let lowRisk = 1;
let legal = 'Legal Information';
Stage 1: source
CognniIncidents_CL
Stage 2: where
| where Severity == lowRisk
Stage 3: where
| where informationType_s == legal
Stage 4: where
| where TimeGenerated >= ago(5h)
Stage 5: extend
| extend AccountCustomEntity = userId_s
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Severity | eq |
| field:"Severity" kind:eq value:"1" |
informationType_s | eq |
| field:"informationType_s" kind:eq value:"Legal Information" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
Mimecast Secure Email Gateway - Attachment Protect
#'Detect threat for mail attachment under the targeted threat protection.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Collection | |
| Initial Access |
Rule Body
id: 72bd7b0c-493c-4fa5-8a95-7f6376b6cfb2
name: Mimecast Secure Email Gateway - Attachment Protect
description: |
'Detect threat for mail attachment under the targeted threat protection.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: MimecastSEGAPI
dataTypes:
- MimecastCG
enabled: true
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Collection
- Exfiltration
- Discovery
- InitialAccess
- Execution
relevantTechniques:
- T1114
- T1566
- T0865
query: |
MimecastCG
| where Type == "email_ttp_ap"
| extend SenderEnvelope = ['Sender Envelope'] , SenderIp = ['Sender IP']
suppressionDuration: 5h
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: P7D
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: AlertPerResult
entityMappings:
- entityType: MailMessage
fieldMappings:
- identifier: Sender
columnName: SenderEnvelope
- identifier: Recipient
columnName: Recipients
- identifier: Subject
columnName: Subject
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SenderIp
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
MimecastCG
Stage 2: where
| where Type == "email_ttp_ap"
Stage 3: extend
| extend SenderEnvelope = ['Sender Envelope'] , SenderIp = ['Sender IP']
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Type | eq |
| field:"Type" kind:eq value:"email_ttp_ap" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SenderEnvelope | extend |
SenderIp | extend |
Mimecast Secure Email Gateway - Attachment Protect
#Detect threat for mail attachment under the targeted threat protection
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Collection | |
| Initial Access |
Rule Body
id: 72264f4f-61fb-4f4f-96c4-635571a376c2
name: Mimecast Secure Email Gateway - Attachment Protect
description: Detect threat for mail attachment under the targeted threat protection
severity: High
requiredDataConnectors:
- connectorId: MimecastSIEMAPI
dataTypes:
- MimecastSIEM_CL
enabled: true
query: MimecastSIEM_CL| where mimecastEventId_s=="mail_ttp_attachment"
queryFrequency: 5m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
suppressionDuration: 5h
suppressionEnabled: false
tactics:
- Collection
- Exfiltration
- Discovery
- InitialAccess
- Execution
relevantTechniques:
- T1114
- T1566
- T0865
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1d
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: SingleAlert
customDetails:
sha256: sha256_s
fileName: fileName_s
MsgId_s: MsgId_s
entityMappings:
- entityType: MailMessage
fieldMappings:
- identifier: Sender
columnName: Sender_s
- identifier: Recipient
columnName: Recipient_s
- identifier: Subject
columnName: Subject_s
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IP_s
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
MimecastSIEM_CL
Stage 2: where
| where mimecastEventId_s=="mail_ttp_attachment"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
mimecastEventId_s | eq |
| field:"mimecastEventId_s" kind:eq value:"mail_ttp_attachment" |
Multiple Sources Affected by the Same TI Destination
#'Identifies multiple machines trying to reach out to the same destination blocked by TI in Azure Firewall. This can indicate attack on the organization by the same attack group. Configurable Parameters: - Minimum affected threshold - alert only if more than this number of hosts affected. Default is set to 5. - Recommendation is to use the new resource specific logs. If you are using both, the TiTraffic Count will be duplicated.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control | |
| Exfiltration |
Rule Body
id: 4644baf7-3464-45dd-bd9d-e07687e25f81
name: Multiple Sources Affected by the Same TI Destination
description: |
'Identifies multiple machines trying to reach out to the same destination blocked by TI in Azure Firewall. This can indicate attack on the organization by the same attack group.
Configurable Parameters:
- Minimum affected threshold - alert only if more than this number of hosts affected. Default is set to 5.
- Recommendation is to use the new resource specific logs. If you are using both, the TiTraffic Count will be duplicated.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AzureFirewall
dataTypes:
- AzureDiagnostics
- AZFWThreatIntel
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
- CommandAndControl
relevantTechniques:
- T1041
- T1071
query: |
let Timeframe = 1d;
let StartTime = ago(Timeframe);
let EndTime = now();
let MinAffectedThreshold = 5;
union isfuzzy=true
(AzureDiagnostics
| where TimeGenerated between (StartTime .. EndTime)
| where OperationName == "AzureFirewallThreatIntelLog"
| parse msg_s with * "from " SourceIp ":" SourcePort:int " to " Fqdn ":" DestinationPort:int "." * "Action: Deny. " ThreatDescription),
(AZFWThreatIntel
| where TimeGenerated between (StartTime .. EndTime))
| extend Fqdn = DestinationIp
| summarize TiTrafficCount = count(), dCountSourceIps = dcount(SourceIp), AffectedIps = make_set(SourceIp, 10000) by Fqdn, ThreatDescription
| where array_length(AffectedIps) > MinAffectedThreshold
| mv-expand SourceIp = AffectedIps
| order by TiTrafficCount desc, Fqdn asc, parse_ipv4(tostring(SourceIp)) asc
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIp
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Fqdn
customDetails:
ThreatDescription: ThreatDescription
version: 1.1.5
kind: Scheduled
Stages and Predicates
Parameters
let Timeframe = 1d;
let StartTime = ago(Timeframe);
let EndTime = now();
let MinAffectedThreshold = 5;
Union isfuzzy=true (2 Sources)
Each leg below queries one source; the rule matches if any leg does. Sources: AzureDiagnostics, AZFWThreatIntel
Leg 1: AzureDiagnostics
AzureDiagnostics
| where TimeGenerated between (StartTime .. EndTime)
| where OperationName == "AzureFirewallThreatIntelLog"
| parse msg_s with * "from " SourceIp ":" SourcePort:int " to " Fqdn ":" DestinationPort:int "." * "Action: Deny. " ThreatDescription
Leg 2: AZFWThreatIntel
AZFWThreatIntel
| where TimeGenerated between (StartTime .. EndTime)
Applied to the Combined Result
| extend Fqdn = DestinationIp
| summarize TiTrafficCount = count(), dCountSourceIps = dcount(SourceIp), AffectedIps = make_set(SourceIp, 10000) by Fqdn, ThreatDescription
| where array_length(AffectedIps) > MinAffectedThreshold
| mv-expand SourceIp = AffectedIps
| order by TiTrafficCount desc, Fqdn asc, parse_ipv4(tostring(SourceIp)) asc
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
OperationName | eq |
| field:"OperationName" kind:eq value:"AzureFirewallThreatIntelLog" |
TimeGenerated | ge |
| field:"TimeGenerated" kind:ge value:"StartTime" |
TimeGenerated | le |
| field:"TimeGenerated" kind:le value:"EndTime" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AffectedIps | summarize |
Fqdn | summarize |
ThreatDescription | summarize |
TiTrafficCount | summarize |
dCountSourceIps | summarize |
Network endpoint to host executable correlation
#'Correlates blocked URLs hosting [malicious] executables with host endpoint data to identify potential instances of executables of the same name having been recently run.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 4688: A new process has been created. |
Rule Body
id: 01f64465-b1ef-41ea-a7f5-31553a11ad43
name: Network endpoint to host executable correlation
description: |
'Correlates blocked URLs hosting [malicious] executables with host endpoint data to identify potential instances of executables of the same name having been recently run.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: TrendMicro
dataTypes:
- CommonSecurityLog
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvents
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
relevantTechniques:
- T1204
query: |
let endpointData =
(union isfuzzy=true
(SecurityEvent
| where EventID == 4688
| extend shortFileName = tolower(tostring(split(NewProcessName, '\\')[-1]))
),
(WindowsEvent
| where EventID == 4688
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend shortFileName = tolower(tostring(split(NewProcessName, '\\')[-1]))
| extend TargetUserName = tostring(EventData.TargetUserName)
));
// Correlate suspect executables seen in TrendMicro rule updates with similar activity on endpoints
CommonSecurityLog
| where DeviceVendor =~ "Trend Micro"
| where Activity =~ "Deny List updated"
| where RequestURL endswith ".exe"
| project TimeGenerated, Activity , RequestURL , SourceIP, DestinationIP
| extend suspectExeName = tolower(tostring(split(RequestURL, '/')[-1]))
| join kind=innerunique (endpointData) on $left.suspectExeName == $right.shortFileName
| extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: TargetUserName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: DestinationIP
- entityType: URL
fieldMappings:
- identifier: Url
columnName: RequestURL
version: 1.1.6
kind: Scheduled
Stages and Predicates
Let Binding: endpointData
let endpointData = (union isfuzzy=true
(SecurityEvent
| where EventID == 4688
| extend shortFileName = tolower(tostring(split(NewProcessName, '\\')[-1]))
),
(WindowsEvent
| where EventID == 4688
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend shortFileName = tolower(tostring(split(NewProcessName, '\\')[-1]))
| extend TargetUserName = tostring(EventData.TargetUserName)
));
Stage 1: source
let endpointData
Stage 2: source
CommonSecurityLog
Stage 3: where
| where DeviceVendor =~ "Trend Micro"
Stage 4: where
| where Activity =~ "Deny List updated"
Stage 5: where
| where RequestURL endswith ".exe"
Stage 6: project
| project TimeGenerated, Activity , RequestURL , SourceIP, DestinationIP
Stage 7: extend
| extend suspectExeName = tolower(tostring(split(RequestURL, '/')[-1]))
Stage 8: join
join kind=innerunique (endpointData) on $left.suspectExeName, $right.shortFileName
Stage 9: extend
extend DnsDomain, HostName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Activity | eq |
| field:"Activity" kind:eq value:"Deny List updated" |
DeviceVendor | eq |
| field:"DeviceVendor" kind:eq value:"Trend Micro" |
EventID | eq |
| field:"EventID" kind:eq value:"4688" |
RequestURL | ends_with |
| field:"RequestURL" kind:ends_with value:".exe" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Activity | project |
DestinationIP | project |
RequestURL | project |
SourceIP | project |
TimeGenerated | project |
suspectExeName | extend |
DnsDomain | extend |
HostName | extend |
Process Injection Initiated By MMC
#This query searches for suspicious behavior initiated by MMC. This is done by looking at a number of actions that are commonly associated with process injection.
Known False Positives
- Some legitimate MMC snap-ins may perform actions that are similar to process injection. These will require additional filtering.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth |
References
Telemetry Coverage
Rule Body
let timeframe = 2*1h;
DeviceEvents
| where ingestion_time() >= ago(timeframe)
| where InitiatingProcessFileName =~ "mmc.exe"
// Look for actions associated with process injection.
| where ActionType in~ ("CreateRemoteThreadApiCall", "MemoryRemoteProtect", "NtAllocateVirtualMemoryRemoteApiCall", "NtMapViewOfSectionRemoteApiCall","ReadProcessMemoryApiCall", "SetThreadContextRemoteApiCall", "QueueUserApcRemoteApiCall")
| extend ParsedCommandLine=parse_command_line(InitiatingProcessCommandLine, "windows")
// When a .msc file is opened in MMC, the file path is passed as an argument to MMC.
// Based on testing this is the first argument in the command line. In some cases a command-line switch /32 is passed as the first argument
// and the file path is the second argument. This is handled by the iif statement below.
| extend MscFile=ParsedCommandLine[1]
| extend MscFile=iif(MscFile startswith "/", ParsedCommandLine[2], MscFile)
// Begin environment-specific filter.
// End environment-specific filter.
Stages and Predicates
Parameters
let timeframe = 2*1h;
Stage 1: source
DeviceEvents
Stage 2: where
| where ingestion_time() >= ago(timeframe)
Stage 3: where
| where InitiatingProcessFileName =~ "mmc.exe"
Stage 4: where
| where ActionType in~ ("CreateRemoteThreadApiCall", "MemoryRemoteProtect", "NtAllocateVirtualMemoryRemoteApiCall", "NtMapViewOfSectionRemoteApiCall","ReadProcessMemoryApiCall", "SetThreadContextRemoteApiCall", "QueueUserApcRemoteApiCall")
Stage 5: extend (3 consecutive steps)
| extend ParsedCommandLine=parse_command_line(InitiatingProcessCommandLine, "windows")
| extend MscFile=ParsedCommandLine[1]
| extend MscFile=iif(MscFile startswith "/", ParsedCommandLine[2], MscFile)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | in |
| field:"ActionType" kind:in |
InitiatingProcessFileName | eq |
| field:"InitiatingProcessFileName" kind:eq value:"mmc.exe" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ParsedCommandLine | extend |
MscFile | extend |
ProofpointPOD - Weak ciphers
#'Detects when weak TLS ciphers are used.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: 56b0a0cd-894e-4b38-a0a1-c41d9f96649a
name: ProofpointPOD - Weak ciphers
description: |
'Detects when weak TLS ciphers are used.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: ProofpointPOD
dataTypes:
- ProofpointPOD_message_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1573
query: |
let lbtime = 1h;
let tls_ciphers = dynamic(['RC4-SHA', 'DES-CBC3-SHA']);
ProofpointPOD
| where EventType == 'message'
| where TlsCipher in (tls_ciphers)
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.3
kind: Scheduled
Stages and Predicates
Parameters
let lbtime = 1h;
let tls_ciphers = dynamic(['RC4-SHA', 'DES-CBC3-SHA']);
Stage 1: source
ProofpointPOD
Stage 2: where
| where EventType == 'message'
Stage 3: where
| where TlsCipher in (tls_ciphers)
Stage 4: extend
| extend IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | eq |
| field:"EventType" kind:eq value:"message" |
TlsCipher | in |
| field:"TlsCipher" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
IPCustomEntity | extend |
Subnets Alerts for Prancer
#'High severity subnet alerts found by Prancer.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance |
Rule Body
id: 10be8f37-d83c-4b7e-81c2-1271c51ac09f
name: Subnets Alerts for Prancer
description: |
'High severity subnet alerts found by Prancer.'
severity: High
requiredDataConnectors:
- connectorId: PrancerLogData
dataTypes:
- prancer_CL
queryFrequency: 5h
queryPeriod: 5h
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: SingleAlert
status: Available
tactics:
- Reconnaissance
relevantTechniques:
- T1595
query: |
union prancer_CL
| where deviceProduct_s == 'azure'
| where parse_json(data_data_snapshots_s)[0].type == 'Microsoft.Network/virtualNetworks/subnets'
| where data_data_severity_s == 'High' and data_data_result_s == 'failed'
| extend snapshot = parse_json(data_data_snapshots_s)
| mv-expand snapshot
| extend
id = tostring(snapshot.id),
structure = tostring(snapshot.structure),
reference = tostring(snapshot.reference),
source = tostring(snapshot.source),
collection = tostring(snapshot.collection),
type = tostring(snapshot.type),
region = tostring(snapshot.region),
resourceTypes = tostring(snapshot.resourceTypes),
path = tostring(snapshot.path)
customDetails:
entityMappings:
- entityType: AzureResource
fieldMappings:
- identifier: ResourceId
columnName: path
alertDetailsOverride:
alertDisplayNameFormat: "{{data_data_message_s}}"
alertDescriptionFormat: "{{data_data_description_s}}"
alertSeverityColumnName: "{{data_data_severity_s}}"
alertDynamicProperties:
- alertProperty: RemediationSteps
value: data_data_remediation_description_s
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: union
union of 1 branches
Stage 2: source
prancer_CL
Stage 3: where
where deviceProduct_s == "azure"
Stage 4: where
where parse_json(data_data_snapshots_s)[0].type == "Microsoft.Network/virtualNetworks/subnets"
Stage 5: where
where data_data_result_s == "failed" and data_data_severity_s == "High"
Stage 6: extend
extend snapshot
Stage 7: mv-expand
mv-expand snapshot
Stage 8: extend
extend collection, id, path, reference, region, resourceTypes, source, structure, type
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
data_data_result_s | eq |
| field:"data_data_result_s" kind:eq value:"failed" |
data_data_severity_s | eq |
| field:"data_data_severity_s" kind:eq value:"High" |
data_data_snapshots_s[0].type | eq |
| field:"data_data_snapshots_s[0].type" kind:eq value:"Microsoft.Network/virtualNetworks/subnets" |
deviceProduct_s | eq |
| field:"deviceProduct_s" kind:eq value:"azure" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
snapshot | extend |
collection | extend |
id | extend |
path | extend |
reference | extend |
region | extend |
resourceTypes | extend |
source | extend |
structure | extend |
type | extend |
Subscription moved to another tenant
#'This detection uses AzureActivity logs (Security category) to identify when a subscription is moved to another tenant. A threat actor may move a subscription into their own tenant to circumvent local resource deployment and logging policies. Once moved, threat actors may deploy resources and perform malicious activities such as crypto mining. This is a technique known as "subscription hijacking". More information can be found here: https://techcommunity.microsoft.com/t5/microsoft-365-defender-blog/hunt-for-compromised-azure-subscriptions-using-microsoft/ba-p/3607121'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 48c026d8-7f36-4a95-9568-6f1420d66e37
kind: Scheduled
name: Subscription moved to another tenant
description: |
'This detection uses AzureActivity logs (Security category) to identify when a subscription is moved to another tenant.
A threat actor may move a subscription into their own tenant to circumvent local resource deployment and logging policies.
Once moved, threat actors may deploy resources and perform malicious activities such as crypto mining.
This is a technique known as "subscription hijacking". More information can be found here: https://techcommunity.microsoft.com/t5/microsoft-365-defender-blog/hunt-for-compromised-azure-subscriptions-using-microsoft/ba-p/3607121'
severity: Low
requiredDataConnectors:
- connectorId: AzureActivity
dataTypes:
- AzureActivity
queryPeriod: 20m
queryFrequency: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1496
query: |
let queryFrequency = 5m;
let eventCapture = "moved from tenant ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}) to tenant ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})";
AzureActivity
| where ingestion_time() > ago(queryFrequency)
| where CategoryValue =~ "Security"
| where OperationNameValue =~ "Microsoft.Subscription/updateTenant/action"
| extend Properties_d = coalesce(parse_json(Properties), Properties_d)
| where isnotempty(Properties_d)
| extend Summary = tostring(Properties_d.message)
| extend EventCapture = extract_all(eventCapture, Summary)
| extend SourceTenantId = iff(isnotempty(EventCapture), EventCapture[0][0], "")
| extend DestinationTenantId = iff(isnotempty(EventCapture), EventCapture[0][1], "")
| extend
Name = split(Caller, "@", 0)[0],
UPNSuffix = split(Caller, "@", 1)[0]
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: AzureResource
fieldMappings:
- identifier: ResourceId
columnName: _ResourceId
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Caller
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
customDetails:
DestinationTenantId: DestinationTenantId
SourceTenantId: SourceTenantId
alertDetailsOverride:
alertDescriptionFormat: |
The user {{Caller}} moved a subscription:
{{Summary}}
If this was not expected, it may indicate a subscription hijacking event.
alertDisplayNameFormat: |
Subscription {{SubscriptionId}} changed tenants
version: 1.0.1
Stages and Predicates
Parameters
let queryFrequency = 5m;
Let Binding: eventCapture
let eventCapture = "moved from tenant ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}) to tenant ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})";
Stage 1: source
AzureActivity
Stage 2: where
| where ingestion_time() > ago(queryFrequency)
Stage 3: where
| where CategoryValue =~ "Security"
Stage 4: where
| where OperationNameValue =~ "Microsoft.Subscription/updateTenant/action"
Stage 5: extend
| extend Properties_d = coalesce(parse_json(Properties), Properties_d)
Stage 6: where
| where isnotempty(Properties_d)
Stage 7: extend (5 consecutive steps)
| extend Summary = tostring(Properties_d.message)
| extend EventCapture = extract_all(eventCapture, Summary)
| extend SourceTenantId = iff(isnotempty(EventCapture), EventCapture[0][0], "")
| extend DestinationTenantId = iff(isnotempty(EventCapture), EventCapture[0][1], "")
| extend
Name = split(Caller, "@", 0)[0],
UPNSuffix = split(Caller, "@", 1)[0]
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CategoryValue | eq |
| field:"CategoryValue" kind:eq value:"Security" |
OperationNameValue | eq |
| field:"azure_ad::operation_name_value" kind:eq value:"Microsoft.Subscription/updateTenant/action" |
Properties_d | is_not_null | field:"Properties_d" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Properties_d | extend |
Summary | extend |
EventCapture | extend |
SourceTenantId | extend |
DestinationTenantId | extend |
Name | extend |
UPNSuffix | extend |
Theom - Critical data in API headers or body
#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.
"Creates Sentinel incidents for critical/high Theom risks, associated with ruleId's TRIS0007 to TRIS0010 and TRIS0014"
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Collection |
Rule Body
id: 2ef36aaa-ec4a-473a-9734-f364ce8868f8
name: Theom - Critical data in API headers or body
description: |
"Creates Sentinel incidents for critical/high Theom risks, associated with ruleId's TRIS0007 to TRIS0010 and TRIS0014"
severity: High
status : Available
requiredDataConnectors:
- connectorId: Theom
dataTypes:
- TheomAlerts_CL
queryPeriod: 5m
queryFrequency: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Collection
relevantTechniques:
- T1119
query: |
TheomAlerts_CL
| where ( customProps_RuleId_s == "TRIS0007" or customProps_RuleId_s == "TRIS0008" or
customProps_RuleId_s == "TRIS0009" or customProps_RuleId_s == "TRIS0010" or customProps_RuleId_s == "TRIS0014" )
and (priority_s == "P1" or priority_s == "P2")
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: "Theom Alert ID: {{id_s}} "
alertDescriptionFormat: "\nSummary: {{summary_s}} \nAdditional info: {{details_s}}\nPlease investigate further on Theom UI at {{deepLink_s}}\n"
entityMappings:
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: customProps_AssetName_s
- entityType: URL
fieldMappings:
- identifier: Url
columnName: deepLink_s
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
TheomAlerts_CL
Stage 2: where
| where ( customProps_RuleId_s == "TRIS0007" or customProps_RuleId_s == "TRIS0008" or
customProps_RuleId_s == "TRIS0009" or customProps_RuleId_s == "TRIS0010" or customProps_RuleId_s == "TRIS0014" )
and (priority_s == "P1" or priority_s == "P2")
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
customProps_RuleId_s | eq |
| field:"customProps_RuleId_s" kind:eq |
priority_s | eq |
| field:"priority_s" kind:eq |
TI Map File Hash to DeviceFileEvents Event
#Identifies a match in DeviceFileEvents Event data from any FileHash IOC from TI
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceFileEvents action any: File activity |
Rule Body
id: d6f04915-4471-4cb3-b163-a8b72997cf72
name: TI Map File Hash to DeviceFileEvents Event
description: |
'Identifies a match in DeviceFileEvents Event data from any FileHash IOC from TI'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceFileEvents
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelIndicators
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let DeviceFileEvents_ = (union
(DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA1) | extend FileHashValue = SHA1),
(DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA256) | extend FileHashValue = SHA256));
let Hashes = DeviceFileEvents_ | distinct FileHashValue;
ThreatIntelIndicators
//extract key part of kv pair
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType == "file"
| extend FileHashType = replace("'", "", substring(ObservableKey, indexof(ObservableKey, "hashes.") + 7, strlen(ObservableKey) - indexof(ObservableKey, "hashes.") - 7))
| extend FileHashValue = ObservableValue
| extend IndicatorId = tostring(split(Id, "--")[2])
| where isnotempty(FileHashValue)
| where TimeGenerated > ago(ioc_lookBack)
// | where FileHashValue in (Hashes)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
| extend Description = tostring(parse_json(Data).description)
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
| project-reorder *, FileHashType, FileHashValue, Type
| join kind=innerunique (DeviceFileEvents_) on $left.FileHashValue == $right.FileHashValue
| where TimeGenerated < ValidUntil
| summarize TimeGenerated = arg_max(TimeGenerated, *) by IndicatorId, DeviceId
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| project TimeGenerated, TrafficLightProtocolLevel, Description, ActivityGroupNames, IndicatorId, FileHashValue, FileHashType, ValidUntil, Confidence, ActionType, DeviceId, DeviceName, FolderPath, RequestAccountDomain, RequestAccountName, RequestAccountSid, MachineGroup
| extend timestamp = TimeGenerated
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: RequestAccountName
- identifier: Sid
columnName: RequestAccountSid
- identifier: NTDomain
columnName: RequestAccountDomain
- entityType: FileHash
fieldMappings:
- identifier: Value
columnName: FileHashValue
- identifier: Algorithm
columnName: FileHashType
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: DeviceName
version: 1.0.4
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
Let Binding: DeviceFileEvents_
let DeviceFileEvents_ = (union
(DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA1) | extend FileHashValue = SHA1),
(DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA256) | extend FileHashValue = SHA256));
Let Binding: Hashes
let Hashes = DeviceFileEvents_ | distinct FileHashValue;
Stage 1: source
let DeviceFileEvents_
Stage 2: source
ThreatIntelIndicators
Stage 3: extend
extend IndicatorType
Stage 4: where
where IndicatorType == "file"
Stage 5: extend (3 consecutive steps)
extend FileHashType, FileHashValue, IndicatorId
Stage 6: where
where isnotempty(FileHashValue)
Stage 7: where
where TimeGenerated > ago(1209600s)
Stage 8: summarize
summarize LatestIndicatorTime by Id, ObservableValue
Stage 9: where
where (isempty(ValidUntil) or ValidUntil > now()) and IsActive == true
Stage 10: extend
extend Description
Stage 11: where
where not (Description contains_cs "State: falsepos;") and not (Description contains_cs "State: inactive;")
Stage 12: project-reorder
project-reorder
Stage 13: join
join kind=innerunique (DeviceFileEvents_) on $left.FileHashValue, $right.FileHashValue
Stage 14: where
where TimeGenerated < ValidUntil
Stage 15: summarize
summarize TimeGenerated by IndicatorId, DeviceId
Stage 16: extend (3 consecutive steps)
extend ActivityGroupNames, Description, TrafficLightProtocolLevel
Stage 17: project
project ActionType, ActivityGroupNames, Confidence, Description, DeviceId, DeviceName, FileHashType, FileHashValue, FolderPath, IndicatorId, MachineGroup, RequestAccountDomain, RequestAccountName, RequestAccountSid, TimeGenerated, TrafficLightProtocolLevel, ValidUntil
Stage 18: extend
extend timestamp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
FileHashValue | is_not_null | field:"FileHashValue" kind:is_not_null | |
IndicatorType | eq |
| field:"IndicatorType" kind:eq value:"file" |
IsActive | eq |
| field:"IsActive" kind:eq value:"true" |
SHA1 | is_not_null | field:"SHA1" kind:is_not_null | |
SHA256 | is_not_null | field:"SHA256" kind:is_not_null | |
ValidUntil | is_null | field:"ValidUntil" kind:is_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
Description | contains | State: falsepos; | excludes:Description field:"Description" value:"State: falsepos;" |
Description | contains | State: inactive; | excludes:Description field:"Description" value:"State: inactive;" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActionType | project |
ActivityGroupNames | project |
Confidence | project |
Description | project |
DeviceId | project |
DeviceName | project |
FileHashType | project |
FileHashValue | project |
FolderPath | project |
IndicatorId | project |
MachineGroup | project |
RequestAccountDomain | project |
RequestAccountName | project |
RequestAccountSid | project |
TimeGenerated | project |
TrafficLightProtocolLevel | project |
ValidUntil | project |
timestamp | extend |
TI Map IP entity to Web Session Events (ASIM Web Session schema)
#This rule identifies Web Sessions for which the source IP address is a known IoC. This rule uses the Advanced Security Information Model (ASIM) and supports any web session source that complies with ASIM.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: 0548be6c-135e-4eb6-b9ff-14a09df62c77
name: TI Map IP entity to Web Session Events (ASIM Web Session schema)
description: |
This rule identifies Web Sessions for which the source IP address is a known IoC. This rule uses the [Advanced Security Information Model (ASIM)](https://aka.ms/AboutASIM) and supports any web session source that complies with ASIM.
severity: Medium
requiredDataConnectors:
- connectorId: SquidProxy
dataTypes:
- SquidProxy_CL
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelIndicators
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let HAS_ANY_MAX = 10000;
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_TI = ThreatIntelIndicators
//extract key part of kv pair
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType in ("ipv4-addr", "ipv6-addr", "network-traffic")
| extend NetworkSourceIP = toupper(ObservableValue)
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| where TimeGenerated >= ago(ioc_lookBack)
// As there is potentially more than 1 indicator type for matching IP, taking NetworkIP first, then others if that is empty.
// Taking the first non-empty value based on potential IOC match availability
| extend TI_ipEntity = NetworkSourceIP
// Picking up only IOC's that contain the entities we want
| where TI_ipEntity != "NO_IP"
// Exclude local addresses, using the ipv4_is_private operator
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
| extend ThreatType = tostring(Data.indicator_types[0])
| extend IndicatorId = tostring(split(Id, "--")[2])
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil));
let IP_TI_list = toscalar(IP_TI
| summarize NIoCs = dcount(TI_ipEntity), IoCs = make_set(TI_ipEntity)
| project IoCs = iff(NIoCs > HAS_ANY_MAX, dynamic([]), IoCs));
IP_TI
| 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 (
_Im_WebSession (starttime = ago(dt_lookBack), srcipaddr_has_any_prefix = IP_TI_list)
| where isnotempty(SrcIpAddr)
// renaming time column so it is clear the log this came from
| extend imNWS_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.SrcIpAddr
| where imNWS_TimeGenerated < ValidUntil
| summarize imNWS_TimeGenerated = arg_max(imNWS_TimeGenerated, *) by IndicatorId, DstIpAddr
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| project imNWS_TimeGenerated, Description, ActivityGroupNames, IndicatorId, ThreatType, ValidUntil, Confidence, TI_ipEntity, Dvc, SrcIpAddr, DstIpAddr, Url
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: DstIpAddr
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
customDetails:
EventTime: imNWS_TimeGenerated
IoCDescription: Description
ActivityGroupNames: ActivityGroupNames
IndicatorId: IndicatorId
ThreatType: ThreatType
IoCExpirationTime: ValidUntil
IoCConfidenceScore: Confidence
alertDetailsOverride:
alertDisplayNameFormat: The IP {{SrcIpAddr}} of the web request matches an IP IoC
alertDescriptionFormat: The source address {{SrcIpAddr}} of the web request for the URL {{Url}} matches a known indicator of compromise of {{ThreatType}}. Consult the threat intelligence feed for more information about the indicator.
version: 1.2.11
kind: Scheduled
Stages and Predicates
Parameters
let HAS_ANY_MAX = 10000;
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_TI is inlined into the numbered stages below.
Let Binding: IP_TI_list
let IP_TI_list = toscalar(IP_TI
| summarize NIoCs = dcount(TI_ipEntity), IoCs = make_set(TI_ipEntity)
| project IoCs = iff(NIoCs > HAS_ANY_MAX, dynamic([]), IoCs));
Stages 1 to 13 define let IP_TI as the rule's main pipeline source. Stages 14 to 20 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
| extend TI_ipEntity = NetworkSourceIP
Stage 8: where
| where TI_ipEntity != "NO_IP"
Stage 9: where
| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
Stage 10: extend
| extend ThreatType = tostring(Data.indicator_types[0])
Stage 11: extend
| extend IndicatorId = tostring(split(Id, "--")[2])
Stage 12: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId, ObservableValue
Stage 13: where
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
Stage 14: project-reorder
IP_TI
| project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
Stage 15: join
| join kind = innerunique (
_Im_WebSession (starttime = ago(dt_lookBack), srcipaddr_has_any_prefix = IP_TI_list)
| where isnotempty(SrcIpAddr)
| extend imNWS_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.SrcIpAddr
Stage 16: where
where imNWS_TimeGenerated < ValidUntil
Stage 17: summarize
summarize imNWS_TimeGenerated by IndicatorId, DstIpAddr
Stage 18: extend
extend Description
Stage 19: extend
extend ActivityGroupNames
Stage 20: project
project ActivityGroupNames, Confidence, Description, DstIpAddr, Dvc, IndicatorId, SrcIpAddr, TI_ipEntity, ThreatType, Url, ValidUntil, imNWS_TimeGenerated
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" |
SrcIpAddr | is_not_null | field:"SrcIpAddr" kind:is_not_null | |
TI_ipEntity | ne |
| field:"TI_ipEntity" kind:ne value:"NO_IP" |
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 |
Confidence | project |
Description | project |
DstIpAddr | project |
Dvc | project |
IndicatorId | project |
SrcIpAddr | project |
TI_ipEntity | project |
ThreatType | project |
Url | project |
ValidUntil | project |
imNWS_TimeGenerated | project |
Vectra Create Detection Alert for Accounts
#This analytic rule is looking for new attacker behaviors observed by the Vectra Platform. The intent is to create entries in the SecurityAlert table for every new detection attached to an entity monitored by the Vectra Platform
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Rule Body
id: e796701f-6b39-4c54-bf8a-1d543a990784
name: Vectra Create Detection Alert for Accounts
version: 1.0.1
kind: Scheduled
description: This analytic rule is looking for new attacker behaviors observed by the Vectra Platform. The intent is to create entries in the SecurityAlert table for every new detection attached to an entity monitored by the Vectra Platform
status: Available
requiredDataConnectors:
- connectorId: VectraXDR
dataTypes:
- Detections_Data_CL
severity: Medium
queryFrequency: 10m
queryPeriod: 10m
suppressionDuration: PT1H
suppressionEnabled: false
triggerOperator: GreaterThan
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1546
query: |
VectraDetections
| where Type == "account"
| extend
entity_uid = ['Entity UID'],
entity_id = ['Entity ID'],
entity_type = ['Entity Type'],
detection_id = ['Detection ID'],
detection = ['Detection Name'],
category = ['Detection Category'],
detection_url = ['Vectra Pivot'],
mitre = Mitre,
tags = Tags
| summarize arg_max(TimeGenerated, *) by ['Detection ID'], entity_uid
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: entity_uid
incidentConfiguration:
createIncident: false
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
customDetails:
entity_id: entity_id
tags: tags
entity_type: entity_type
detection_id: detection_id
mitre_techniques: mitre
alertDetailsOverride:
alertDisplayNameFormat: Vectra AI Detection- {{detection}}
alertDescriptionFormat: Vectra AI has detected {{category}} - {{detection} on entity {{entity_uid}}.
alertDynamicProperties:
- alertProperty: AlertLink
value: detection_url
eventGroupingSettings:
aggregationKind: AlertPerResult
Stages and Predicates
Stage 1: source
VectraDetections
Stage 2: where
| where Type == "account"
Stage 3: extend
| extend
entity_uid = ['Entity UID'],
entity_id = ['Entity ID'],
entity_type = ['Entity Type'],
detection_id = ['Detection ID'],
detection = ['Detection Name'],
category = ['Detection Category'],
detection_url = ['Vectra Pivot'],
mitre = Mitre,
tags = Tags
Stage 4: summarize
| summarize arg_max(TimeGenerated, *) by ['Detection ID'], entity_uid
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Type | eq |
| field:"Type" kind:eq value:"account" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
entity_uid | summarize |
Vulnerable Machines related to OMIGOD CVE-2021-38647
#'This query uses the Azure Defender Security Nested Recommendations data to find machines vulnerable to OMIGOD CVE-2021-38647. OMI is the Linux equivalent of Windows WMI and helps users manage configurations across remote and local environments. The query aims to find machines that have this OMI vulnerability (CVE-2021-38647). Security Nested Recommendations data is sent to Microsoft Sentinel using the continuous export feature of Azure Defender(refrence link below). Reference: https://www.wiz.io/blog/omigod-critical-vulnerabilities-in-omi-azure Reference: https://docs.microsoft.com/azure/security-center/continuous-export?tabs=azure-portal'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution |
Rule Body
id: 4d94d4a9-dc96-450a-9dea-4d4d4594199b
name: Vulnerable Machines related to OMIGOD CVE-2021-38647
description: |
'This query uses the Azure Defender Security Nested Recommendations data to find machines vulnerable to OMIGOD CVE-2021-38647.
OMI is the Linux equivalent of Windows WMI and helps users manage configurations across remote and local environments. The query aims to find machines that have this OMI vulnerability (CVE-2021-38647).
Security Nested Recommendations data is sent to Microsoft Sentinel using the continuous export feature of Azure Defender(refrence link below).
Reference: https://www.wiz.io/blog/omigod-critical-vulnerabilities-in-omi-azure
Reference: https://docs.microsoft.com/azure/security-center/continuous-export?tabs=azure-portal'
severity: High
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Execution
relevantTechniques:
- T1190
- T1203
tags:
- OMIGOD
- CVE-2021-38647
query: |
SecurityNestedRecommendation
| where RemediationDescription has 'CVE-2021-38647'
| parse ResourceDetails with * 'virtualMachines/' VirtualMAchine '"' *
| summarize arg_min(TimeGenerated, *) by TenantId, RecommendationSubscriptionId, VirtualMAchine, RecommendationName,Description,RemediationDescription, tostring(AdditionalData),VulnerabilityId
| extend HostName = tostring(split(VirtualMAchine, ".")[0]), DomainIndex = toint(indexof(VirtualMAchine, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(VirtualMAchine, DomainIndex + 1), VirtualMAchine)
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: VirtualMAchine
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: HostNameDomain
version: 1.0.5
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Threat Protection" ]
Stages and Predicates
Stage 1: source
SecurityNestedRecommendation
Stage 2: where
| where RemediationDescription has 'CVE-2021-38647'
Stage 3: parse
| parse ResourceDetails with * 'virtualMachines/' VirtualMAchine '"' *
Stage 4: summarize
| summarize arg_min(TimeGenerated, *) by TenantId, RecommendationSubscriptionId, VirtualMAchine, RecommendationName,Description,RemediationDescription, tostring(AdditionalData),VulnerabilityId
Stage 5: extend
| extend HostName = tostring(split(VirtualMAchine, ".")[0]), DomainIndex = toint(indexof(VirtualMAchine, '.'))
Stage 6: extend
| extend HostNameDomain = iff(DomainIndex != -1, substring(VirtualMAchine, DomainIndex + 1), VirtualMAchine)
HostNameDomain =DomainIndex != -1substring(VirtualMAchine, (DomainIndex + 1))VirtualMAchineIndicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
RemediationDescription | match |
| field:"RemediationDescription" kind:match value:"CVE-2021-38647" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Description | summarize |
RecommendationName | summarize |
RecommendationSubscriptionId | summarize |
RemediationDescription | summarize |
TenantId | summarize |
VirtualMAchine | summarize |
VulnerabilityId | summarize |
DomainIndex | extend |
HostName | extend |
HostNameDomain | extend |
Zscaler - Connections by dormant user
#'Detects ZPA connections by dormant user.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Rule Body
id: 66bc77ee-3e45-11ec-9bbc-0242ac130002
name: Zscaler - Connections by dormant user
description: |
'Detects ZPA connections by dormant user.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: CustomLogsAma
datatypes:
- ZPA_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1078
query: |
let dt_lastlogintime = 14d;
let activeUsers =
ZPAEvent
| where TimeGenerated > ago(dt_lastlogintime)
| where DvcAction == 'open'
| summarize lastLoginTime = max(TimeGenerated) by DstUserName
| summarize makelist(DstUserName);
ZPAEvent
| where DstUserName !in~ (activeUsers)
| summarize EventCount = count() by DstUserName
| project-away EventCount
| extend AccountCustomEntity = DstUserName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let dt_lastlogintime = 14d;
Let Binding: activeUsers
let activeUsers = ZPAEvent
| where TimeGenerated > ago(dt_lastlogintime)
| where DvcAction == 'open'
| summarize lastLoginTime = max(TimeGenerated) by DstUserName
| summarize makelist(DstUserName);
Stage 1: source
ZPAEvent
Stage 2: where
| where DstUserName !in~ (activeUsers)
Stage 3: summarize
| summarize EventCount = count() by DstUserName
Stage 4: project-away
| project-away EventCount
Stage 5: extend
| extend AccountCustomEntity = DstUserName
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
DstUserName | eq | activeUsers | excludes:DstUserName field:"DstUserName" value:"activeUsers" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstUserName | summarize |
AccountCustomEntity | extend |