Detection rules › Kusto
Kusto rules: 0t
Addition of a Temporary Access Pass to a Privileged Account
#Detects when a Temporary Access Pass (TAP) is created for a Privileged Account. A Temporary Access Pass is a time-limited passcode issued by an admin that satisfies strong authentication requirements and can be used to onboard other authentication methods, including Passwordless ones such as Microsoft Authenticator or even Windows Hello. A threat actor could use a TAP to register a new authentication method to maintain persistance to an account. Review any TAP creations to ensure they were used legitimately. Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-privileged-accounts#changes-to-privileged-accounts
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
- [Entra ID] Authentication Method Changed for Privileged Account (Kusto)
- Account Created and Deleted in Short Timeframe (Kusto)
- Account Created And Deleted Within A Close Time Frame (Sigma)
- Account created from non-approved sources (Kusto)
- Account created or deleted by non-approved user (Kusto)
- Authentication Method Changed for Privileged Account (Kusto)
- Authentication Methods Changed for Privileged Account (Kusto)
- Azure AD External Guest User Invited (Splunk)
Rule Body
id: d7feb859-f03e-4e8d-8b21-617be0213b13
name: Addition of a Temporary Access Pass to a Privileged Account
description: |
'Detects when a Temporary Access Pass (TAP) is created for a Privileged Account.
A Temporary Access Pass is a time-limited passcode issued by an admin that satisfies strong authentication requirements and can be used to onboard other authentication methods, including Passwordless ones such as Microsoft Authenticator or even Windows Hello.
A threat actor could use a TAP to register a new authentication method to maintain persistance to an account.
Review any TAP creations to ensure they were used legitimately.
Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-privileged-accounts#changes-to-privileged-accounts'
severity: High
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
- connectorId: BehaviorAnalytics
dataTypes:
- IdentityInfo
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1078.004
tags:
- AADSecOpsGuide
query: |
let admin_users = (IdentityInfo
| summarize arg_max(TimeGenerated, *) by AccountUPN
| where AssignedRoles contains "admin"
| summarize by tolower(AccountUPN));
AuditLogs
| where OperationName =~ "Admin registered security info"
| where ResultReason =~ "Admin registered temporary access pass method for user"
| extend TargetUserPrincipalName = tostring(TargetResources[0].userPrincipalName)
| where tolower(TargetUserPrincipalName) in (admin_users)
| extend TargetAadUserId = tostring(TargetResources[0].id)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIPAddress = tostring(InitiatedBy.user.ipAddress)
| extend TargetAccountName = tostring(split(TargetUserPrincipalName, "@")[0]), TargetAccountUPNSuffix = tostring(split(TargetUserPrincipalName, "@")[1])
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetUserPrincipalName
- identifier: Name
columnName: TargetAccountName
- identifier: UPNSuffix
columnName: TargetAccountUPNSuffix
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: InitiatingUserPrincipalName
- identifier: Name
columnName: InitiatingAccountName
- identifier: UPNSuffix
columnName: InitiatingAccountUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: TargetAadUserId
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: InitiatingAadUserId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: InitiatingIPAddress
version: 1.0.4
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Threat Protection", "Identity" ]
Stages and Predicates
Let Binding: admin_users
let admin_users = (IdentityInfo
| summarize arg_max(TimeGenerated, *) by AccountUPN
| where AssignedRoles contains "admin"
| summarize by tolower(AccountUPN));
Stage 1: source
let admin_users
Stage 2: source
AuditLogs
Stage 3: where
where OperationName =~ "Admin registered security info"
Stage 4: where
where ResultReason =~ "Admin registered temporary access pass method for user"
Stage 5: extend
extend TargetUserPrincipalName
Stage 6: where
where TargetUserPrincipalName == "admin_users"
Stage 7: extend (6 consecutive steps)
extend InitiatingAadUserId, InitiatingAccountName, InitiatingAccountUPNSuffix, InitiatingIPAddress, InitiatingUserPrincipalName, TargetAadUserId, TargetAccountName, TargetAccountUPNSuffix
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
OperationName | eq |
| field:"OperationName" kind:eq value:"Admin registered security info" |
ResultReason | eq |
| field:"ResultReason" kind:eq value:"Admin registered temporary access pass method for user" |
TargetUserPrincipalName | in |
| field:"TargetUserPrincipalName" kind:in value:"admin_users" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
TargetUserPrincipalName | extend |
TargetAadUserId | extend |
InitiatingUserPrincipalName | extend |
InitiatingAadUserId | extend |
InitiatingIPAddress | extend |
TargetAccountName | extend |
TargetAccountUPNSuffix | extend |
InitiatingAccountName | extend |
InitiatingAccountUPNSuffix | extend |
AWSCloudTrail - Changes to Amazon VPC settings
#Identifies changes to Amazon VPC(Virtual Private Cloud) settings that can alter network exposure, routing, or access controls. This rule identifies changes to Amazon VPC (Virtual Private Cloud) settings such as new ACL entries, routes, routetable or Gateways. AWS VPC API Docs: https://docs.aws.amazon.com/AWSEC2/latest/APIReference/OperationList-query-vpc.html'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation | |
| Stealth | |
| Defense Impairment |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
- ASL AWS Network Access Control List Created with All Open Ports (Splunk)
- AWS Network Access Control List Created with All Open Ports (Splunk)
- AWSCloudTrail - Network ACL with all the open ports to a specified CIDR (Kusto)
- EC2 Network ACL Modified (Panther)
- EC2 Network Gateway Modified (Panther)
- EC2 Route Table Modified (Panther)
Rule Body
id: 65360bb0-8986-4ade-a89d-af3cf44d28aa
name: AWSCloudTrail - Changes to Amazon VPC settings
description: |
Identifies changes to Amazon VPC(Virtual Private Cloud) settings that can alter network exposure, routing, or access controls.
This rule identifies changes to Amazon VPC (Virtual Private Cloud) settings such as new ACL entries, routes, routetable or Gateways.
AWS VPC API Docs: https://docs.aws.amazon.com/AWSEC2/latest/APIReference/OperationList-query-vpc.html'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
- connectorId: AWSS3
dataTypes:
- AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
- DefenseEvasion
relevantTechniques:
- T1078
- T1562.007
query: |
let EventNameList = dynamic(["CreateNetworkAclEntry","CreateRoute","CreateRouteTable","CreateInternetGateway","CreateNatGateway"]);
AWSCloudTrail
| where EventName in~ (EventNameList)
| where EventSource != "apigateway.amazonaws.com"
| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by EventName, EventTypeName, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, UserIdentityPrincipalid, UserAgent,
UserIdentityUserName, SessionMfaAuthenticated, SourceIpAddress, AWSRegion, EventSource, AdditionalEventData, ResponseElements
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- identifier: CloudAppAccountId
columnName: RecipientAccountId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIpAddress
customDetails:
AWSRegion: AWSRegion
RecipientAccountId: RecipientAccountId
EventName: EventName
EventSource: EventSource
alertDetailsOverride:
alertDisplayNameFormat: 'AWS VPC change by {{AccountName}} in {{AWSRegion}}'
alertDescriptionFormat: 'Event {{EventName}} executed by {{AccountName}} from {{SourceIpAddress}}.'
version: 1.0.8
kind: Scheduled
Stages and Predicates
Parameters
let EventNameList = dynamic(["CreateNetworkAclEntry","CreateRoute","CreateRouteTable","CreateInternetGateway","CreateNatGateway"]);
Stage 1: source
AWSCloudTrail
Stage 2: where
| where EventName in~ (EventNameList)
Stage 3: where
| where EventSource != "apigateway.amazonaws.com"
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: summarize
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by EventName, EventTypeName, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, UserIdentityPrincipalid, UserAgent,
UserIdentityUserName, SessionMfaAuthenticated, SourceIpAddress, AWSRegion, EventSource, AdditionalEventData, ResponseElements
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventName | in |
| field:"aws::eventName" kind:in |
EventSource | ne |
| field:"aws::eventSource" kind:ne value:"apigateway.amazonaws.com" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AWSRegion | summarize |
AccountName | summarize |
AccountUPNSuffix | summarize |
AdditionalEventData | summarize |
EndTimeUtc | summarize |
EventName | summarize |
EventSource | summarize |
EventTypeName | summarize |
RecipientAccountId | summarize |
ResponseElements | summarize |
SessionMfaAuthenticated | summarize |
SourceIpAddress | summarize |
StartTimeUtc | summarize |
UserAgent | summarize |
UserIdentityAccountId | summarize |
UserIdentityPrincipalid | summarize |
UserIdentityUserName | summarize |
Box - Executable file in folder
#'Detects executable files in folders.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: b91ec98d-5747-45c8-b2f6-a07bf47068f0
name: Box - Executable file in folder
description: |
'Detects executable files in folders.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BoxDataConnector
dataTypes:
- BoxEvents_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1189
query: |
BoxEvents
| where SourceFileName hassuffix '.exe' or SourceItemName hassuffix '.exe'
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
BoxEvents
Stage 2: where
| where SourceFileName hassuffix '.exe' or SourceItemName hassuffix '.exe'
Stage 3: extend
| extend IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
SourceFileName | ends_with |
| field:"SourceFileName" kind:ends_with value:".exe" |
SourceItemName | ends_with |
| field:"SourceItemName" kind:ends_with value:".exe" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
IPCustomEntity | extend |
Cisco SEG - Multiple large emails sent to external recipient
#'Detects possible data exfiltration.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
Rule Body
id: 1399a9a5-6200-411e-8c34-ca5658754cf7
name: Cisco SEG - Multiple large emails sent to external recipient
description: |
'Detects possible data exfiltration.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
relevantTechniques:
- T1030
query: |
let e_theshold = 3;
let s_threshold = 10000000;
CiscoSEGEvent
| where AdditionalFields[15]['ESAMsgSize'] > s_threshold
| where NetworkDirection =~ 'Outgoing'
| extend rec_domain = extract(@'@(.*)', 1, DstUserName)
| extend s_domain = extract(@'@(.*)', 1, SrcUserName)
| where s_domain != rec_domain
| summarize count() by SrcUserName
| where count_ >= e_theshold
| extend AccountCustomEntity = SrcUserName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.3
kind: Scheduled
Stages and Predicates
Parameters
let e_theshold = 3;
let s_threshold = 10000000;
Stage 1: source
CiscoSEGEvent
Stage 2: where
| where AdditionalFields[15]['ESAMsgSize'] > s_threshold
Stage 3: where
| where NetworkDirection =~ 'Outgoing'
Stage 4: extend
| extend rec_domain = extract(@'@(.*)', 1, DstUserName)
Stage 5: extend
| extend s_domain = extract(@'@(.*)', 1, SrcUserName)
Stage 6: where
| where s_domain != rec_domain
Stage 7: summarize
| summarize count() by SrcUserName
Stage 8: where
| where count_ >= e_theshold
Stage 9: extend
| extend AccountCustomEntity = SrcUserName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AdditionalFields[15]["ESAMsgSize"] | gt |
| |
NetworkDirection | eq |
| field:"NetworkDirection" kind:eq value:"Outgoing" |
count_ | ge |
| field:"count_" kind:ge value:"3" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SrcUserName | summarize |
AccountCustomEntity | extend |
Conditional Access - Dynamic Group Exclusion Changes
#// Detects changes to Dynamic Membership Rules for specified groups (often used in CA exclusions)
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Azure | Update group |
Rules Detecting the Same Action
These rules filter on the same operation.
- Detect suspicious conditional access policy modifications (Kusto)
- Detecting Impossible travel with mailbox permission tampering & Privilege Escalation attempt (Kusto)
- Entra ID Successful Group Deletion (YARA-L)
- Guest accounts added in Entra ID Groups other than the ones specified (Kusto)
- User Removed From Group With CA Policy Modification Access (Sigma)
Rule Body
id: c385944b-17b9-4b2b-921e-0e8d0341a675
name: Conditional Access - Dynamic Group Exclusion Changes
version: 1.0.1
kind: Scheduled
description: // Detects changes to Dynamic Membership Rules for specified groups (often used in CA exclusions)
severity: High
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
relevantTechniques:
- T1484
query: |-
// Detects changes to Dynamic Membership Rules for specified groups (often used in CA exclusions)
let monitoredGroups = dynamic(["Group1", "Group2"]); // <-- Customize this list
AuditLogs
| where OperationName == "Update group"
| where AdditionalDetails[0].value == "DynamicMembership"
| extend DynamicGroupName = tostring(TargetResources[0].displayName)
| where DynamicGroupName in (monitoredGroups)
| extend modifiedBy = tostring(InitiatedBy.user.userPrincipalName)
| extend accountName = tostring(split(modifiedBy, "@")[0])
| extend upnSuffix = tostring(split(modifiedBy, "@")[1])
| extend oldRule = tostring(TargetResources[0].modifiedProperties[0].oldValue)
| extend newRule = tostring(TargetResources[0].modifiedProperties[0].newValue)
| where oldRule != newRule
| project
TimeGenerated,
OperationName,
DynamicGroupName,
modifiedBy,
accountName,
upnSuffix,
result = Result,
oldRule,
newRule
| order by TimeGenerated desc
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: accountName
- identifier: UPNSuffix
columnName: upnSuffix
suppressionEnabled: false
suppressionDuration: 5h
eventGroupingSettings:
aggregationKind: AlertPerResult
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
Stages and Predicates
Parameters
let monitoredGroups = dynamic(["Group1", "Group2"]);
Stage 1: source
AuditLogs
Stage 2: where
| where OperationName == "Update group"
Stage 3: where
| where AdditionalDetails[0].value == "DynamicMembership"
Stage 4: extend
| extend DynamicGroupName = tostring(TargetResources[0].displayName)
Stage 5: where
| where DynamicGroupName in (monitoredGroups)
Stage 6: extend (5 consecutive steps)
| extend modifiedBy = tostring(InitiatedBy.user.userPrincipalName)
| extend accountName = tostring(split(modifiedBy, "@")[0])
| extend upnSuffix = tostring(split(modifiedBy, "@")[1])
| extend oldRule = tostring(TargetResources[0].modifiedProperties[0].oldValue)
| extend newRule = tostring(TargetResources[0].modifiedProperties[0].newValue)
Stage 7: where
| where oldRule != newRule
Stage 8: project
| project
TimeGenerated,
OperationName,
DynamicGroupName,
modifiedBy,
accountName,
upnSuffix,
result = Result,
oldRule,
newRule
Stage 9: sort
| order by TimeGenerated desc
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AdditionalDetails[0].value | eq |
| field:"AdditionalDetails[0].value" kind:eq value:"DynamicMembership" |
DynamicGroupName | in |
| field:"DynamicGroupName" kind:in |
OperationName | eq |
| field:"OperationName" kind:eq value:"Update group" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DynamicGroupName | project |
OperationName | project |
TimeGenerated | project |
accountName | project |
modifiedBy | project |
newRule | project |
oldRule | project |
result | project |
upnSuffix | project |
CTE - Device License Capacity Critical (99%)
#Device license capacity has reached 99% - new and existing unmanaged devices may go unprotected. Emitted once when usage newly crosses this threshold by the daily tenant health-check job (api/src/functions/tenantHealthChecks.js); resets and can re-fire if usage later drops back below 95% and crosses again.
Rule Body
id: 7f4af730-5c89-4f7a-aa7a-933b6e9309b5
name: CTE - Device License Capacity Critical (99%)
description: |
Device license capacity has reached 99% - new and existing unmanaged devices may go unprotected. Emitted once when usage newly crosses this threshold by the daily tenant health-check job (api/src/functions/tenantHealthChecks.js); resets and can re-fire if usage later drops back below 95% and crosses again.
severity: High
requiredDataConnectors:
- connectorId: cybereinforce_cte
dataTypes:
- CybereinforceCTE_CL
queryFrequency: 24h
queryPeriod: 2d
triggerOperator: gt
triggerThreshold: 0
tactics:
[]
relevantTechniques: []
query: |
CybereinforceCTE_CL
| where EventType == "license.capacity_critical"
| extend d = parse_json(Details)
| extend AppName = "Cybereinforce", ActiveDevices = toint(d.activeDevices), LicensedDevices = toint(d.licensedDevices), UsedPct = todouble(d.usedPct)
| project TimeGenerated, TenantId, AppName, ActiveDevices, LicensedDevices, UsedPct, Severity, Category, Source
entityMappings:
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: AppName
- identifier: InstanceName
columnName: TenantId
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
CybereinforceCTE_CL
Stage 2: where
| where EventType == "license.capacity_critical"
Stage 3: extend
| extend d = parse_json(Details)
Stage 4: extend
| extend AppName = "Cybereinforce", ActiveDevices = toint(d.activeDevices), LicensedDevices = toint(d.licensedDevices), UsedPct = todouble(d.usedPct)
Stage 5: project
| project TimeGenerated, TenantId, AppName, ActiveDevices, LicensedDevices, UsedPct, Severity, Category, Source
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | eq |
| field:"EventType" kind:eq value:"license.capacity_critical" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActiveDevices | project |
AppName | project |
Category | project |
LicensedDevices | project |
Severity | project |
Source | project |
TenantId | project |
TimeGenerated | project |
UsedPct | project |
Cyble Vision Alerts News Feed Alert
#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.
'Triggers when monitored keywords are found in external news feeds. Helps analysts track relevant cybersecurity news, advisories, or threat intelligence updates tied to monitored entities.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance |
Rule Body
id: d205a93f-b2e3-4708-a359-5e0c88ee3e59
name: Cyble Vision Alerts News Feed Alert
description: |
'Triggers when monitored keywords are found in external news feeds. Helps analysts track relevant cybersecurity news, advisories, or threat intelligence updates tied to monitored entities.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
dataTypes:
- CybleVisionAlerts_CL
enabled: true
queryfrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- Reconnaissance
relevantTechniques:
- T1598
query: |
Alerts_news_feed
| where Service == "news_feed"
| extend MappedSeverity = Severity
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
alertDetailsOverride:
alertDisplayNameFormat: Relevant Cybersecurity News {{NF_PostTitle}}
alertDescriptionFormat: |
A news article referencing the monitored keyword {{KeywordName}} was identified. Source {{NF_PostSourceName}}. URL {{NF_PostURL}}.
customDetails:
MappedSeverity: Severity
Status: Status
AlertID: AlertID
Service: Service
NF_PostTitle: NF_PostTitle
NF_PostSource: NF_PostSourceName
NF_PostURL: NF_PostURL
NF_AI_Summary: NF_AI_Summary
entityMappings:
- entityType: Url
fieldMappings:
- identifier: Url
columnName: NF_PostURL
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: KeywordName
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Alerts_news_feed
Stage 2: where
| where Service == "news_feed"
Stage 3: extend
| extend MappedSeverity = Severity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Service | eq |
| field:"Service" kind:eq value:"news_feed" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
MappedSeverity | extend |
Egress Defend - Dangerous Attachment Detected
#'Defend has detected a user has a suspicious file type from a suspicious sender in their mailbox.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Persistence | |
| Privilege Escalation | |
| Execution |
Rule Body
id: a0e55dd4-8454-4396-91e6-f28fec3d2cab
name: Egress Defend - Dangerous Attachment Detected
description: |
'Defend has detected a user has a suspicious file type from a suspicious sender in their mailbox.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: EgressDefend
dataTypes:
- EgressDefend_CL
- EgressDefend_v4_CL
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
- InitialAccess
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1204
- T0853
- T0863
- T1566
- T1546
- T1546
tags:
- Defend
query: |
DefendAuditData_v4
| where ThreatLevel == "suspicious" or ThreatLevel == "dangerous"
| mv-expand todynamic(Attachments)
| where Attachments.name matches regex @"(?i)^.*\.(doc|docx|docm|pdf|xls|xlsx|xlsm|html|zip)$(?-i)"
| summarize attachmentCount=count() by TimeGenerated, tostring(Attachments.name), Subject, From, Account_0_FullName = trim(@"[^@.\w]+",Recipients), timesClicked = LinksClicked, SenderIP
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Account_0_FullName
- entityType: File
fieldMappings:
- identifier: Name
columnName: Attachments_name
- entityType: Mailbox
fieldMappings:
- identifier: MailboxPrimaryAddress
columnName: Account_0_FullName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SenderIP
alertDetailsOverride:
alertDisplayNameFormat: Alert - {{Account_0_FullName}} has suspicious attachment.
version: 1.1.0
kind: Scheduled
Stages and Predicates
Stage 1: source
DefendAuditData_v4
Stage 2: where
| where ThreatLevel == "suspicious" or ThreatLevel == "dangerous"
Stage 3: mv-expand
| mv-expand todynamic(Attachments)
Stage 4: where
| where Attachments.name matches regex @"(?i)^.*\.(doc|docx|docm|pdf|xls|xlsx|xlsm|html|zip)$(?-i)"
Stage 5: summarize
| summarize attachmentCount=count() by TimeGenerated, tostring(Attachments.name), Subject, From, Account_0_FullName = trim(@"[^@.\w]+",Recipients), timesClicked = LinksClicked, SenderIP
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Attachments.name | regex_match |
| field:"Attachments.name" kind:regex_match value:"(?i)^.*.(doc|docx|docm|pdf|xls|xlsx|xlsm|html|zip)$(?-i)" |
ThreatLevel | eq |
| field:"ThreatLevel" kind:eq |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account_0_FullName | summarize |
From | summarize |
SenderIP | summarize |
Subject | summarize |
TimeGenerated | summarize |
attachmentCount | summarize |
timesClicked | summarize |
GitHub - Repository was destroyed
#Detect activities when a repository was destroyed. This query runs every day and its severity is Medium.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| GitHub | Audit log action repo.destroy: Repository deleted |
Rules Detecting the Same Action
These rules filter on the same operation.
- Github Delete Action Invoked (Sigma)
- GitHub Enterprise Repository Deleted (Splunk)
- GitHub Organizations Repository Deleted (Splunk)
- GitHub Repository Archived Or Deleted (YARA-L)
- GitHub Repository Deleted (Elastic)
Rule Body
id: 0b85a077-8ba5-4cb5-90f7-1e882afe10c3
name: GitHub - Repository was destroyed
description: |
'Detect activities when a repository was destroyed. This query runs every day and its severity is Medium.'
severity: Medium
status: Available
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |
GitHubAuditData
| where Action == "repo.destroy"
| extend AccountName = tostring(split(Actor, "@")[0]), AccountUPNSuffix = tostring(split(Actor, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Actor
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
GitHubAuditData
Stage 2: where
| where Action == "repo.destroy"
Stage 3: extend
| extend AccountName = tostring(split(Actor, "@")[0]), AccountUPNSuffix = tostring(split(Actor, "@")[1])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action | eq |
| field:"Action" kind:eq value:"repo.destroy" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | extend |
AccountUPNSuffix | extend |
GWorkspace - Admin permissions granted
#Triggers on admin permissions granted.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Google Workspace | GRANT_ADMIN_PRIVILEGE: Admin Privileges Grant |
Rule Body
id: 03f25156-6172-11ec-90d6-0242ac120003
name: GWorkspace - Admin permissions granted
description: |
'Triggers on admin permissions granted.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: GoogleWorkspaceReportsAPI
dataTypes:
- GWorkspaceActivityReports
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1098
query: |
GWorkspaceActivityReports
| where EventMessage =~ 'grant_admin_privilege'
| 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 EventMessage =~ 'grant_admin_privilege'
Stage 3: extend
| extend AccountCustomEntity = ActorEmail
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventMessage | eq |
| field:"EventMessage" kind:eq value:"grant_admin_privilege" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
HoneyLabs TI Map IP Entity to Network Session (ASIM)
#Identifies network sessions whose source IP addresses HoneyLabs honeypot telemetry has observed running exploit or loader commands. Uses the ASIM NetworkSession parser, so it works across any normalised network source (firewalls, VPN, cloud NSG flow logs) without requiring CEF. Only the structured source IP is matched; destination IPs are intentionally excluded. Indicator Confidence reflects how much evidence HoneyLabs holds (90 = 100+ observed attacks, 60 = a single sighting); raise the threshold in the query to trade coverage for precision.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Command & Control |
Telemetry Coverage
Rule Body
id: 5d3a1c77-2b40-4e6c-9f2d-71c0a8e4b912
name: HoneyLabs TI Map IP Entity to Network Session (ASIM)
version: 1.0.0
kind: Scheduled
description: |
'Identifies network sessions whose source IP addresses HoneyLabs honeypot telemetry has observed running exploit or loader commands. Uses the ASIM NetworkSession parser, so it works across any normalised network source (firewalls, VPN, cloud NSG flow logs) without requiring CEF. Only the structured source IP is matched; destination IPs are intentionally excluded. Indicator Confidence reflects how much evidence HoneyLabs holds (90 = 100+ observed attacks, 60 = a single sighting); raise the threshold in the query to trade coverage for precision.'
severity: Medium
requiredDataConnectors: []
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- CommandAndControl
relevantTechniques:
- T1190
- T1071
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let HoneyLabs_IPs = ThreatIntelIndicators
| where TimeGenerated >= ago(ioc_lookBack)
| where IsActive == true and ValidUntil > now()
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
| where SourceSystem startswith 'HoneyLabs'
| where ObservableKey == 'ipv4-addr:value'
| extend TI_ipEntity = tostring(ObservableValue),
HoneyLabsConfidence = Confidence,
IndicatorDescription = tostring(Data.description),
HoneyLabsReport = strcat('https://honeylabs.net/lookup/', tostring(ObservableValue));
HoneyLabs_IPs
| join kind=innerunique (
_Im_NetworkSession(starttime=ago(dt_lookBack))
| where isnotempty(SrcIpAddr)
| extend NS_ipEntity = SrcIpAddr
| extend NetworkSession_TimeGenerated = TimeGenerated
) on $left.TI_ipEntity == $right.NS_ipEntity
| where NetworkSession_TimeGenerated < ValidUntil
| summarize NetworkSession_TimeGenerated = arg_max(NetworkSession_TimeGenerated, *) by Id, NS_ipEntity
| project NetworkSession_TimeGenerated, IndicatorDescription, Id, ValidUntil, HoneyLabsConfidence,
HoneyLabsReport, TI_ipEntity, NS_ipEntity, SrcIpAddr, DstIpAddr, DstPortNumber, Dvc, EventResult
| extend timestamp = NetworkSession_TimeGenerated
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: NS_ipEntity
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let HoneyLabs_IPs is inlined into the numbered stages below.
Stages 1 to 7 define let HoneyLabs_IPs as the rule's main pipeline source. Stages 8 to 12 use it.
Stage 1: source
ThreatIntelIndicators
Stage 2: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 3: where
| where IsActive == true and ValidUntil > now()
Stage 4: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
Stage 5: where
| where SourceSystem startswith 'HoneyLabs'
Stage 6: where
| where ObservableKey == 'ipv4-addr:value'
Stage 7: extend
| extend TI_ipEntity = tostring(ObservableValue),
HoneyLabsConfidence = Confidence,
IndicatorDescription = tostring(Data.description),
HoneyLabsReport = strcat('https://honeylabs.net/lookup/', tostring(ObservableValue))
Stage 8: join
HoneyLabs_IPs
| join kind=innerunique (
_Im_NetworkSession(starttime=ago(dt_lookBack))
| where isnotempty(SrcIpAddr)
| extend NS_ipEntity = SrcIpAddr
| extend NetworkSession_TimeGenerated = TimeGenerated
) on $left.TI_ipEntity == $right.NS_ipEntity
Stage 9: where
where NetworkSession_TimeGenerated < ValidUntil
Stage 10: summarize
summarize NetworkSession_TimeGenerated by Id, NS_ipEntity
Stage 11: project
project DstIpAddr, DstPortNumber, Dvc, EventResult, HoneyLabsConfidence, HoneyLabsReport, Id, IndicatorDescription, NS_ipEntity, NetworkSession_TimeGenerated, SrcIpAddr, TI_ipEntity, ValidUntil
Stage 12: extend
extend timestamp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
IsActive | eq |
| field:"IsActive" kind:eq value:"true" |
ObservableKey | eq |
| field:"ObservableKey" kind:eq value:"ipv4-addr:value" |
SourceSystem | starts_with |
| field:"SourceSystem" kind:starts_with value:"HoneyLabs" |
SrcIpAddr | is_not_null | field:"SrcIpAddr" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstIpAddr | project |
DstPortNumber | project |
Dvc | project |
EventResult | project |
HoneyLabsConfidence | project |
HoneyLabsReport | project |
Id | project |
IndicatorDescription | project |
NS_ipEntity | project |
NetworkSession_TimeGenerated | project |
SrcIpAddr | project |
TI_ipEntity | project |
ValidUntil | project |
timestamp | extend |
Malware in the recycle bin
#The query detects Windows binaries that can be used for executing malware and have been hidden in the recycle bin. The list of these binaries is sourced from https://lolbas-project.github.io/ References: https://azure.microsoft.com/blog/how-azure-security-center-helps-reveal-a-cyberattack/.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 4688: A new process has been created. |
Rule Body
id: 75bf9902-0789-47c1-a5d8-f57046aa72df
name: Malware in the recycle bin
description: |
'The query detects Windows binaries that can be used for executing malware and have been hidden in the recycle bin.
The list of these binaries is sourced from https://lolbas-project.github.io/
References: https://azure.microsoft.com/blog/how-azure-security-center-helps-reveal-a-cyberattack/.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1564
query: |
let procList = externaldata(Process:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Microsoft_Lolbas_Execution_Binaries.csv"] with (format="csv", ignoreFirstRecord=True);
let recycle_bin_paths = dynamic([@":\RECYCLER", @":\$RECYCLE.BIN"]);
let ProcessCreationEvents=(union isfuzzy=true
(SecurityEvent
| where EventID==4688
| where isnotempty(CommandLine)
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, NewProcessName,
FileName = Process, CommandLine, ParentProcessName
),
(WindowsEvent
| where EventID==4688 and EventData has_any (procList) and EventData has_any (recycle_bin_paths)
| extend CommandLine = tostring(EventData.CommandLine)
| where isnotempty(CommandLine)
| extend SubjectUserName = tostring(EventData.SubjectUserName)
| extend SubjectDomainName = tostring(EventData.SubjectDomainName)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend ParentProcessName = tostring(EventData.ParentProcessName)
| extend Process=tostring(split(NewProcessName, '\\')[-1])
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, NewProcessName,
FileName = Process, CommandLine, ParentProcessName
));
ProcessCreationEvents
| where FileName in~ (procList)
| where CommandLine has_any (recycle_bin_paths)
| project StartTimeUtc = TimeGenerated, Computer, Account, NewProcessName, FileName, CommandLine, ParentProcessName
| extend HostName = iif(Computer has '.',substring(Computer,0,indexof(Computer,'.')),Computer) , DnsDomain = iif(Computer has '.',substring(Computer,indexof(Computer,'.')+1),'')
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Account
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
version: 1.1.6
kind: Scheduled
Stages and Predicates
Parameters
let recycle_bin_paths = dynamic([@":\RECYCLER", @":\$RECYCLE.BIN"]);
Let Binding: procList
let procList = externaldata(Process:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Microsoft_Lolbas_Execution_Binaries.csv"] with (format="csv", ignoreFirstRecord=True);
Union isfuzzy=true (2 Sources)
Each leg below queries one source; the rule matches if any leg does. Sources: SecurityEvent, WindowsEvent
Leg 1: SecurityEvent
SecurityEvent
| where EventID==4688
| where isnotempty(CommandLine)
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, NewProcessName,
FileName = Process, CommandLine, ParentProcessName
Leg 2: WindowsEvent
WindowsEvent
| where EventID==4688 and EventData has_any (procList) and EventData has_any (recycle_bin_paths)
| extend CommandLine = tostring(EventData.CommandLine)
| where isnotempty(CommandLine)
| extend SubjectUserName = tostring(EventData.SubjectUserName)
| extend SubjectDomainName = tostring(EventData.SubjectDomainName)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend ParentProcessName = tostring(EventData.ParentProcessName)
| extend Process=tostring(split(NewProcessName, '\\')[-1])
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, NewProcessName,
FileName = Process, CommandLine, ParentProcessName
Applied to the Combined Result
| where FileName in~ (procList) | where CommandLine has_any (recycle_bin_paths) | project StartTimeUtc = TimeGenerated, Computer, Account, NewProcessName, FileName, CommandLine, ParentProcessName | extend HostName = iif(Computer has '.',substring(Computer,0,indexof(Computer,'.')),Computer) , DnsDomain = iif(Computer has '.',substring(Computer,indexof(Computer,'.')+1),'')
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CommandLine | is_not_null | field:"CommandLine" kind:is_not_null | |
CommandLine | match |
| field:"CommandLine" kind:match |
EventData | match |
| field:"EventData" kind:match |
EventID | eq |
| field:"EventID" kind:eq value:"4688" |
FileName | in |
| field:"FileName" kind:in value:"procList" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account | project |
CommandLine | project |
Computer | project |
FileName | project |
NewProcessName | project |
ParentProcessName | project |
StartTimeUtc | project |
DnsDomain | extend |
HostName | extend |
Ping Federate - OAuth old version
#'Detects requests using not the latest version of OAuth protocol.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 85f70197-4865-4635-a4b2-a9c57e8fea1b
name: Ping Federate - OAuth old version
description: |
'Detects requests using not the latest version of OAuth protocol.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
query: |
PingFederateEvent
| where isnotempty(DeviceCustomString3)
| extend proto = extract(@'(OAuth)', 1, DeviceCustomString3)
| extend ver = extract(@'(\d+)', 1, DeviceCustomString3)
| where proto =~ 'OAuth'
| where ver !~ '20'
| extend AccountCustomEntity = DstUserName
| extend IpCustomEntity = SrcIpAddr
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IpCustomEntity
version: 1.0.3
kind: Scheduled
Stages and Predicates
Stage 1: source
PingFederateEvent
Stage 2: where
| where isnotempty(DeviceCustomString3)
Stage 3: extend
| extend proto = extract(@'(OAuth)', 1, DeviceCustomString3)
Stage 4: extend
| extend ver = extract(@'(\d+)', 1, DeviceCustomString3)
Stage 5: where
| where proto =~ 'OAuth'
Stage 6: where
| where ver !~ '20'
Stage 7: extend
| extend AccountCustomEntity = DstUserName
Stage 8: extend
| extend IpCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DeviceCustomString3 | is_not_null | field:"DeviceCustomString3" kind:is_not_null | |
proto | eq |
| field:"proto" kind:eq value:"OAuth" |
ver | ne |
| field:"ver" kind:ne value:"20" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
proto | extend |
ver | extend |
AccountCustomEntity | extend |
IpCustomEntity | extend |
Silk Typhoon Suspicious File Downloads.
#'This query looks for messages related to file downloads of suspicious file types. This query uses the Exchange HttpProxy AOBGeneratorLog, you will need to onboard this log as a custom log under the table http_proxy_oab_CL before using this query. Reference: https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 03e04c97-8cae-48b3-9d2f-4ab262e4ffff
name: Silk Typhoon Suspicious File Downloads.
description: |
'This query looks for messages related to file downloads of suspicious file types. This query uses the Exchange HttpProxy AOBGeneratorLog, you will need to onboard this log as a custom log under the table http_proxy_oab_CL before using this query.
Reference: https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/'
severity: Medium
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
query: |
let scriptExtensions = dynamic([".php", ".jsp", ".js", ".aspx", ".asmx", ".asax", ".cfm", ".shtml"]);
http_proxy_oab_CL
| where RawData contains "Download failed and temporary file"
| extend File = extract("([^\\\\]*)(\\\\[^']*)",2,RawData)
| extend Extension = strcat(".",split(File, ".")[-1])
| extend InteractiveFile = iif(Extension in (scriptExtensions), "Yes", "No")
// Uncomment the following line to alert only on interactive file download type
//| where InteractiveFile =~ "Yes"
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: HostNameDomain
version: 1.0.3
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - 0-day Vulnerability" ]
Stages and Predicates
Parameters
let scriptExtensions = dynamic([".php", ".jsp", ".js", ".aspx", ".asmx", ".asax", ".cfm", ".shtml"]);
Stage 1: source
http_proxy_oab_CL
Stage 2: where
| where RawData contains "Download failed and temporary file"
Stage 3: extend (5 consecutive steps)
| extend File = extract("([^\\\\]*)(\\\\[^']*)",2,RawData)
| extend Extension = strcat(".",split(File, ".")[-1])
| extend InteractiveFile = iif(Extension in (scriptExtensions), "Yes", "No")
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
RawData | contains |
| field:"RawData" kind:contains value:"Download failed and temporary file" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
File | extend |
Extension | extend |
InteractiveFile | extend |
DomainIndex | extend |
HostName | extend |
HostNameDomain | extend |
Theom - Healthcare data exposed
#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 TRIS0015 (Theom has observed healthcare data in a data store that is publicly exposed. As per this requirement, use this information to apply data access control lists or access permissions to secure your data)"
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Collection |
Rule Body
id: 078b5614-54c7-41a6-8289-5b5870e4c0f9
name: Theom - Healthcare data exposed
description: |
"Creates Sentinel incidents for critical/high Theom risks, associated with ruleId TRIS0015 (Theom has observed healthcare data in a data store that is publicly exposed. As per this requirement, use this information to apply data access control lists or access permissions to secure your data)"
severity: High
status : Available
requiredDataConnectors:
- connectorId: Theom
dataTypes:
- TheomAlerts_CL
queryPeriod: 5m
queryFrequency: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Collection
relevantTechniques:
- T1213
- T1530
query: |
TheomAlerts_CL
| where customProps_RuleId_s == "TRIS0015" 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 == "TRIS0015" 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 value:"TRIS0015" |
priority_s | eq |
| field:"priority_s" kind:eq |
Vectra AI Detect - Suspected Compromised Account
#'Create an incident when an Account is suspected to be compromised. The higher the severity level is, the more immediate attention it requires as Vectra AI engine is more confident that this is a real threat. Level of severity are: Low, Medium, High, Critical). Recommended configuration is to trigger an alert for at least High and Critical.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access | |
| Discovery | |
| Lateral Movement | |
| Collection | |
| Command & Control | |
| Exfiltration | |
| Impact |
Rule Body
id: 321f9dbd-64b7-4541-81dc-08cf7732ccb0
name: Vectra AI Detect - Suspected Compromised Account
description: |
'Create an incident when an Account is suspected to be compromised.
The higher the severity level is, the more immediate attention it requires as Vectra AI engine is more confident that this is a real threat.
Level of severity are: Low, Medium, High, Critical). Recommended configuration is to trigger an alert for at least High and Critical.'
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- CredentialAccess
- Discovery
- LateralMovement
- Collection
- CommandAndControl
- Exfiltration
- Impact
relevantTechniques:
- T1003
- T1087
- T1021
- T1119
- T1071
- T1041
- T1499
query: |
// Edit this variable to only keep the Severity level where an incident needs to be created.
//Level of severity are: Low, Medium, High, Critical). Recommended configuration is to trigger an alert for at least High and Critical.'
let configured_level = dynamic(["High", "Critical"]);
let upn_has_prefix = ":";
CommonSecurityLog
| where DeviceVendor == "Vectra Networks"
| where DeviceProduct == "X Series"
| where DeviceEventClassID == "asc"
| extend saccount = extract("saccount=(.+?);", 1, AdditionalExtensions)
| extend type = iff(saccount matches regex upn_has_prefix, tostring(split(saccount,":")[0]) ,"network" )
| extend upn = iff(saccount matches regex upn_has_prefix, tostring(split(saccount,":")[1]) , saccount )
| extend name = tostring(split(upn,"@")[0])
| extend upn_suffix = tostring(split(upn,"@")[1])
| project-rename threat_score = FlexNumber1
| project-rename certainty_score = FlexNumber2
| project-rename vectra_URL = DeviceCustomString4
| project-rename detection_name = DeviceEventClassID
| project-rename score_decreases = DeviceCustomString3
| extend level = case( threat_score < 50 and certainty_score < 50, "Low",
threat_score < 50 and certainty_score >= 50 , "Medium",
threat_score >= 50 and certainty_score <= 50, "High",
threat_score >= 50 and certainty_score >= 50, "Critical",
"UNKNOWN")
| extend Severity = case(level == "Info", "Informational",level == "Critical", "High", level)
| where level in (configured_level)
//keep only the event with the highest threat score per Host
| summarize arg_max(threat_score, *) by saccount
| sort by TimeGenerated
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: name
- identifier: UPNSuffix
columnName: upn_suffix
alertDetailsOverride:
alertDisplayNameFormat: Vectra AI Detect - Account {{saccount}} reaches {{level}} severity
alertDescriptionFormat: |
The account {{saccount}} has a threat score of {{threat_score}} and a
certainty of {{certainty_score}}
alertSeverityColumnName: Severity
alertDynamicProperties:
- alertProperty: AlertLink
value: vectra_URL
- alertProperty: ProductName
value: DeviceProduct
- alertProperty: ProviderName
value: DeviceVendor
- alertProperty: ConfidenceScore
value: certainty_score
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: true
lookbackDuration: 7d
matchingMethod: AllEntities
customDetails:
ScoreDecrease: score_decreases
version: 1.0.9
kind: Scheduled
Stages and Predicates
Parameters
let configured_level = dynamic(["High", "Critical"]);
let upn_has_prefix = ":";
Stage 1: source
CommonSecurityLog
Stage 2: where
| where DeviceVendor == "Vectra Networks"
Stage 3: where
| where DeviceProduct == "X Series"
Stage 4: where
| where DeviceEventClassID == "asc"
Stage 5: extend (5 consecutive steps)
| extend saccount = extract("saccount=(.+?);", 1, AdditionalExtensions)
| extend type = iff(saccount matches regex upn_has_prefix, tostring(split(saccount,":")[0]) ,"network" )
| extend upn = iff(saccount matches regex upn_has_prefix, tostring(split(saccount,":")[1]) , saccount )
| extend name = tostring(split(upn,"@")[0])
| extend upn_suffix = tostring(split(upn,"@")[1])
Stage 6: project-rename
| project-rename threat_score = FlexNumber1
Stage 7: project-rename
| project-rename certainty_score = FlexNumber2
Stage 8: project-rename
| project-rename vectra_URL = DeviceCustomString4
Stage 9: project-rename
| project-rename detection_name = DeviceEventClassID
Stage 10: project-rename
| project-rename score_decreases = DeviceCustomString3
Stage 11: extend
| extend level = case( threat_score < 50 and certainty_score < 50, "Low",
threat_score < 50 and certainty_score >= 50 , "Medium",
threat_score >= 50 and certainty_score <= 50, "High",
threat_score >= 50 and certainty_score >= 50, "Critical",
"UNKNOWN")
level =threat_score < 50 and certainty_score < 50"Low"threat_score < 50 and certainty_score >= 50"Medium"threat_score >= 50 and certainty_score <= 50"High"threat_score >= 50 and certainty_score >= 50"Critical""UNKNOWN"Stage 12: extend
| extend Severity = case(level == "Info", "Informational",level == "Critical", "High", level)
Severity =level == "Info""Informational"level == "Critical""High"levelStage 13: where
| where level in (configured_level)
Stage 14: summarize
| summarize arg_max(threat_score, *) by saccount
Stage 15: sort
| sort by TimeGenerated
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DeviceEventClassID | eq |
| field:"DeviceEventClassID" kind:eq value:"asc" |
DeviceProduct | eq |
| field:"DeviceProduct" kind:eq value:"X Series" |
DeviceVendor | eq |
| field:"DeviceVendor" kind:eq value:"Vectra Networks" |
level | in |
| field:"level" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
saccount | summarize |
Virtual Machines Alerts for Prancer
#'High severity virtual machine alerts found by Prancer.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance |
Rule Body
id: c13b025c-ea31-4e4b-8e08-955b8fa91fa0
name: Virtual Machines Alerts for Prancer
description: |
'High severity virtual machine 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.Compute/virtualMachines'
| 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.Compute/virtualMachines"
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.Compute/virtualMachines" |
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 |
Web Application attack detected
#Identifies Web application attack in Azure Firewall IDPS logs.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 8159a8d2-13a5-49af-847b-e062c45ab92b
name: Web Application attack detected
description: |
Identifies Web application attack in Azure Firewall IDPS logs.
severity: High
status: Available
requiredDataConnectors:
- connectorId: AzureFirewall
dataTypes:
- AZFWIdpsSignature
queryFrequency: 1h
queryPeriod: 24h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
query: |
let TimeWindow = 90d;
let HitThreshold = 10;
let MinSeverity = 2;
let EnableCategoryFilter = true;
let EnableDescriptionFilter = false;
let EnableActionFilter = false;
let CategoriesOfInterest = dynamic([
"access to a potentially vulnerable web application",
"Web Application Attack"
]);
let DescriptionsOfInterest = dynamic([
"web-application-activity",
"web-application-attack"
]);
let MatchActions = dynamic(["Deny", "alert"]);
AZFWIdpsSignature
| where TimeGenerated >= ago(TimeWindow)
| where Severity >= MinSeverity
| where (EnableCategoryFilter == false) or (Category has_any (CategoriesOfInterest))
| where (EnableDescriptionFilter == false) or (Description has_any (DescriptionsOfInterest))
| where (EnableActionFilter == false) or (Action in~ (MatchActions))
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
TotalHits = count(),
MaxSeverity = max(Severity),
Actions = make_set(Action, 5),
Signatures = make_set(SignatureId, 20),
Description = make_set(substring(tostring(Description), 0, 120), 3)
by SourceIp, ThreatCategory = Category
| where TotalHits >= HitThreshold
| project
StartTime,
EndTime,
SourceIp,
ThreatCategory,
TotalHits,
MaxSeverity,
Actions,
Signatures,
Description
| order by MaxSeverity desc, TotalHits desc
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIp
version: 1.0.1
kind: Scheduled
Stages and Predicates
Parameters
let TimeWindow = 90d;
let HitThreshold = 10;
let MinSeverity = 2;
let EnableCategoryFilter = true;
let EnableDescriptionFilter = false;
let EnableActionFilter = false;
let MatchActions = dynamic(["Deny", "alert"]);
Let Binding: CategoriesOfInterest
let CategoriesOfInterest = dynamic([
"access to a potentially vulnerable web application",
"Web Application Attack"
]);
Let Binding: DescriptionsOfInterest
let DescriptionsOfInterest = dynamic([
"web-application-activity",
"web-application-attack"
]);
Stage 1: source
AZFWIdpsSignature
Stage 2: where
| where TimeGenerated >= ago(TimeWindow)
Stage 3: where
| where Severity >= MinSeverity
Stage 4: where
| where (EnableCategoryFilter == false) or (Category has_any (CategoriesOfInterest))
Stage 5: where
| where (EnableDescriptionFilter == false) or (Description has_any (DescriptionsOfInterest))
Stage 6: where
| where (EnableActionFilter == false) or (Action in~ (MatchActions))
Stage 7: summarize
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
TotalHits = count(),
MaxSeverity = max(Severity),
Actions = make_set(Action, 5),
Signatures = make_set(SignatureId, 20),
Description = make_set(substring(tostring(Description), 0, 120), 3)
by SourceIp, ThreatCategory = Category
Stage 8: where
| where TotalHits >= HitThreshold
Stage 9: project
| project
StartTime,
EndTime,
SourceIp,
ThreatCategory,
TotalHits,
MaxSeverity,
Actions,
Signatures,
Description
Stage 10: sort
| order by MaxSeverity desc, TotalHits desc
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Category | match |
| field:"Category" kind:match |
Severity | ge |
| field:"Severity" kind:ge value:"2" |
TotalHits | ge |
| field:"TotalHits" kind:ge value:"10" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Actions | project |
Description | project |
EndTime | project |
MaxSeverity | project |
Signatures | project |
SourceIp | project |
StartTime | project |
ThreatCategory | project |
TotalHits | project |