Detection rules › Kusto
Kusto rules: 18
Admin SaaS account detected
#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.
The rule detects internal admins accounts, it's recommended to review any new administrative permission.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Privilege Escalation |
Rule Body
id: 87419138-d75f-450d-aca4-1dc802e32540
name: Admin SaaS account detected
kind: Scheduled
description: The rule detects internal admins accounts, it's recommended to review any new administrative permission.
severity: Low
status: Available
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- PrivilegeEscalation
relevantTechniques:
- T1078
- T1078
query: |-
Authomize_v2_CL
| where ingestion_time() >= ago(30m)
| extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s
| where Policy has "Admin SaaS account detected"
| project EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URL
requiredDataConnectors:
- connectorId: Authomize
dataTypes: [ "Authomize_v2_CL" ]
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 5h
matchingMethod: AnyAlert
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertnameFormat: Alert from Authomize - Admin SaaS account detected
alertDescriptionFormat: Admin SaaS account detected. The policy detects internal admins accounts, it's recommended to review any new administrative permission.
alertSeverity: Severity
alertTactics: Tactics
alertDynamicProperties:
- alertProperty: AlertLink
value: URL
customDetails:
AuthomizeEventID: EventID
EventName: Policy
EventDescription: Description
EventRecommendation: Recommendation
ReferencedURL: URL
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.2
Stages and Predicates
Stage 1: source
Authomize_v2_CL
Stage 2: where
| where ingestion_time() >= ago(30m)
Stage 3: extend
| extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s
Stage 4: where
| where Policy has "Admin SaaS account detected"
Stage 5: project
| project EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Policy | match |
| field:"policy_name_s" kind:match value:"Admin SaaS account detected" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Category | project |
Description | project |
EventID | project |
Policy | project |
Recommendation | project |
Severity | project |
Tactics | project |
URL | project |
AIShield - Natural language processing AI model extraction high suspicious vulnerability detection
#This alert creates an incident when Natural language processing AI Model extraction high suspicious, high severity vulnerability detected from the AIShield.'
Rule Body
id: 4ec55816-e07b-45fc-b89e-917c93906540
name: AIShield - Natural language processing AI model extraction high suspicious vulnerability detection
description: |
This alert creates an incident when Natural language processing AI Model extraction high suspicious, high severity vulnerability detected from the AIShield.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: BoschAIShield
dataTypes:
- AIShield
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
AIShield
| where Message has 'Natural Language Processing AI Model Extraction Attack Identified'
| where Severity =~ 'High'
| where SuspiciousLevel =~ 'high suspicious attack'
| extend NTDomain = tostring(split(Computer, '\\')[0]), HostName = tostring(split(Computer, '.')[0])
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: NTDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceName
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertDisplayNameFormat: AIShield - Natural language processing AI model extraction high suspicious vulnerability detected
alertDescriptionFormat: |
This query detects Natural language processing AI Model extraction high suspicious, high severity alert from AIShield generated at {{TimeGenerated}}.\n\nPlease check the source for more information and investigate further.
alertTacticsColumnName: null
alertSeverityColumnName: Severity
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
AIShield
Stage 2: where
| where Message has 'Natural Language Processing AI Model Extraction Attack Identified'
Stage 3: where
| where Severity =~ 'High'
Stage 4: where
| where SuspiciousLevel =~ 'high suspicious attack'
Stage 5: extend
| extend NTDomain = tostring(split(Computer, '\\')[0]), HostName = tostring(split(Computer, '.')[0])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Message | match |
| field:"Message" kind:match value:"Natural Language Processing AI Model Extraction Attack Identified" |
Severity | eq |
| field:"Severity" kind:eq value:"High" |
SuspiciousLevel | eq |
| field:"SuspiciousLevel" kind:eq value:"high suspicious attack" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
HostName | extend |
NTDomain | extend |
API - Rate limiting
#'42Crunch API protection against rate limiting'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: b808063b-07d5-432c-95d0-8900da61cce9
name: API - Rate limiting
description: |
'42Crunch API protection against rate limiting'
severity: Medium
requiredDataConnectors:
- connectorId: 42CrunchAPIProtection
dataTypes:
- FortyTwoCrunchAPIProtection
- connectorId: FortyTwoCrunchAPIProtection
dataTypes:
- FortyTwoCrunchAPIProtection
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: SingleAlert
status: Available
tactics:
- Impact
relevantTechniques:
- T1499
query: |
let firstRec = FortyTwoCrunchAPIProtection
| where TimeGenerated >= ago(5m)
| project-away NonBlockingMode, SourcePort, DestinationPort, Query, ApiId, RequestHeader, ResponseHeader, Errors, EventType, Uuid
| where UriPath has "/api/login" and Status == 429;
let rateLimitCount = (toscalar(firstRec | count));
let recCount = iff((rateLimitCount > 20), 1, 0);
firstRec | top recCount by Timestamp desc;
customDetails:
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIp
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: Hostname
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: InstanceName
version: 3.0.1
kind: Scheduled
Stages and Predicates
Parameters
let recCount = iff((rateLimitCount > 20), 1, 0);
let firstRec is inlined into the numbered stages below.
Let Binding: rateLimitCount
let rateLimitCount = (toscalar(firstRec | count));
Stages 1 to 4 define let firstRec as the rule's main pipeline source. Stage 5 uses it.
Stage 1: source
FortyTwoCrunchAPIProtection
Stage 2: where
| where TimeGenerated >= ago(5m)
Stage 3: project-away
| project-away NonBlockingMode, SourcePort, DestinationPort, Query, ApiId, RequestHeader, ResponseHeader, Errors, EventType, Uuid
Stage 4: where
| where UriPath has "/api/login" and Status == 429
Stage 5: top
firstRec
| top recCount by Timestamp desc;
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Status | eq |
| field:"Status" kind:eq value:"429" |
UriPath | match |
| field:"UriPath" kind:match value:"/api/login" |
BloodHound Attack Path Finding - AddSelf Privilege on Tier Zero Security Groups
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
BloodHound Attack Path Finding - AddSelf Privilege on Tier Zero Security Groups
Rule Body
id: f2e24dac-37a2-493d-a8d7-5b7f6bea438c
name: BloodHound Attack Path Finding - AddSelf Privilege on Tier Zero Security Groups
description: BloodHound Attack Path Finding - AddSelf Privilege on Tier Zero Security
Groups
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BloodHoundEnterprise
dataTypes:
- BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "T0AddSelf"
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: domain_name
customDetails:
Description: ShortDescription
Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
BHEAttackPathsData_CL
Stage 2: where
| where Finding == "T0AddSelf"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"T0AddSelf" |
BloodHound Attack Path Finding - Large Default Groups With WriteAccountRestrictions Privileges
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
BloodHound Attack Path Finding - Large Default Groups With WriteAccountRestrictions Privileges
Rule Body
id: 506957d5-23e2-4d38-b047-2273c121831c
name: BloodHound Attack Path Finding - Large Default Groups With WriteAccountRestrictions
Privileges
description: BloodHound Attack Path Finding - Large Default Groups With WriteAccountRestrictions
Privileges
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BloodHoundEnterprise
dataTypes:
- BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "LargeDefaultGroupsWriteAccountRestrictions"
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: domain_name
customDetails:
Description: ShortDescription
Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
BHEAttackPathsData_CL
Stage 2: where
| where Finding == "LargeDefaultGroupsWriteAccountRestrictions"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"LargeDefaultGroupsWriteAccountRestrictions" |
Canary alerts to incidents
#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 Microsoft Sentinel incidents from Thinkst Canary alerts.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement | |
| Exfiltration |
Rule Body
id: 06360572-94a7-42a4-add7-58fb933b2353
name: Canary alerts to incidents
description: Creates Microsoft Sentinel incidents from Thinkst Canary alerts.
severity: High
status: Available
requiredDataConnectors:
- connectorId: ThinkstCanary
dataTypes:
- ThinkstCanaryIncidents_CL
tactics:
- LateralMovement
- Exfiltration
relevantTechniques:
- T1021
- T1041
query: |
ThinkstCanaryIncidents_CL
| where Description != "Canary Disconnected"
| where Description != "Canary Reconnected"
| where Description != "Canary Settings Changed"
| where Description != "Fake Location"
| where Description != "Network Settings Roll-back"
| extend EventData = todynamic(RawEvent[0])
| extend IsCanarytoken = Description contains "Canarytoken"
or LogType startswith "16"
or LogType startswith "17"
or isnotempty(tostring(EventData.canarytoken))
| extend PhysicalCanaryName = coalesce(tostring(Host.name), NodeId),
PhysicalCanaryIP = coalesce(tostring(Host.ip_address), DestinationIP, IpAddress),
TokenMemo = iff(IsCanarytoken, substring(Memo, 0, 256), ""),
TokenType = coalesce(tostring(EventData.kind), tostring(EventData.type)),
TokenHostname = tostring(EventData.hostname),
TokenTarget = substring(coalesce(tostring(EventData.url), tostring(EventData.hostname), tostring(EventData.cloned_site), tostring(EventData.original_site), tostring(EventData.generic_data), tostring(EventData.location)), 0, 512),
TokenUsername = coalesce(tostring(EventData.windows_desktopini_access_username), tostring(EventData.ms_macro_username), tostring(EventData.cmd_user_name)),
TokenDomain = coalesce(tostring(EventData.windows_desktopini_access_domain), tostring(EventData.cmd_machine_name)),
TokenHost = coalesce(tostring(EventData.windows_desktopini_computer_name), tostring(EventData.cmd_computer_name), tostring(EventData.cmd_workstation), tostring(EventData.cmd_machine_name)),
TokenSourceIP = coalesce(tostring(EventData.cmd_resolved_ip), tostring(EventData.ms_macro_ip)),
TokenCity = coalesce(tostring(EventData.geoip.city), tostring(EventData.geoip.City), tostring(EventData.City)),
TokenCountry = coalesce(tostring(EventData.geoip.country), tostring(EventData.geoip.country_name), tostring(EventData.geoip.Country), tostring(EventData.Country)),
TokenContext = case(
isnotempty(tostring(EventData.client_public_key)), strcat("WireGuard public key: ", substring(tostring(EventData.client_public_key), 0, 128), "; session: ", tostring(EventData.client_session_index)),
isnotempty(tostring(EventData.masked_card_number)), strcat("Masked card: ", tostring(EventData.masked_card_number), "; transaction: ", tostring(EventData.transaction_amount), " ", tostring(EventData.transaction_currency), "; merchant: ", tostring(EventData.merchant)),
isnotempty(tostring(EventData.referer)), strcat("Referrer: ", substring(tostring(EventData.referer), 0, 256)),
""),
UserAgent = substring(coalesce(tostring(EventData.USERAGENT), tostring(EventData.HEADERS['user-agent']), tostring(EventData.headers['User-Agent']), tostring(EventData.headers['user-agent'])), 0, 512)
| extend ActorUsername = coalesce(tostring(EventData.USERNAME), tostring(EventData.USER), tostring(EventData.FORMDATA.username), tostring(EventData.POSTDATA.username), TokenUsername),
ActorDomain = coalesce(tostring(EventData.DOMAIN), tostring(EventData.DOMAINNAME), TokenDomain),
ActorHost = coalesce(TokenHost, tostring(EventData.HOSTNAME)),
Activity = coalesce(TokenContext, TokenType, tostring(EventData.INSTANCE_NAME), tostring(EventData.FUNC_NAME), tostring(EventData.METHOD), tostring(EventData.OPCODE), tostring(EventData.COMMAND), tostring(EventData.FUNCTION), Description)
| extend SourceIP = coalesce(SourceIP, tostring(EventData.src_host), TokenSourceIP),
SourceGeo = case(isnotempty(TokenCity) and isnotempty(TokenCountry), strcat(TokenCity, ", ", TokenCountry), isnotempty(TokenCity), TokenCity, TokenCountry),
AssetType = iff(IsCanarytoken, "Canarytoken", "Canary"),
AssetName = iff(IsCanarytoken, coalesce(TokenMemo, TokenTarget, TokenType, NodeId), PhysicalCanaryName),
AssetNode = NodeId,
AssetIP = iff(IsCanarytoken, "", PhysicalCanaryIP),
AssetLocation = iff(IsCanarytoken, "", tostring(Host.description)),
CanaryEntityName = iff(IsCanarytoken, "", PhysicalCanaryName),
CanaryEntityIP = iff(IsCanarytoken, "", PhysicalCanaryIP)
| extend SourceDisplay = coalesce(SourceIP, SrcHostReverse, "unknown source")
| project TimeGenerated, Description, IncidentId, AssetType, AssetName, AssetNode,
AssetIP, AssetLocation, FlockName, TokenMemo, TokenType, TokenTarget,
TokenHostname, ActorUsername, ActorDomain, ActorHost, SourceGeo, SourceIP,
SourceDisplay, SrcHostReverse, DestinationPort, Activity, UserAgent, LogType,
EventsCount, CanaryEntityName, CanaryEntityIP
suppressionDuration: 1h
suppressionEnabled: false
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: '{{Description}} - {{AssetName}} from {{SourceDisplay}}'
alertDescriptionFormat: 'Thinkst {{AssetType}} {{AssetName}} recorded this activity from {{SourceDisplay}}. Review the mapped entities and custom details for asset, actor, and activity context.'
customDetails:
AssetType: AssetType
AssetName: AssetName
AssetNode: AssetNode
AssetIP: AssetIP
AssetLocation: AssetLocation
FlockName: FlockName
TokenMemo: TokenMemo
TokenType: TokenType
TokenTarget: TokenTarget
ActorUsername: ActorUsername
ActorDomain: ActorDomain
ActorHost: ActorHost
SourceGeo: SourceGeo
IncidentKey: IncidentId
SourceIP: SourceIP
DestinationPort: DestinationPort
Activity: Activity
UserAgent: UserAgent
LogType: LogType
EventsCount: EventsCount
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIP
- entityType: IP
fieldMappings:
- identifier: Address
columnName: CanaryEntityIP
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: CanaryEntityName
- entityType: Account
fieldMappings:
- identifier: Name
columnName: ActorUsername
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: TokenHostname
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
version: 1.0.2
kind: NRT
Stages and Predicates
Stage 1: source
ThinkstCanaryIncidents_CL
Stage 2: where
| where Description != "Canary Disconnected"
Stage 3: where
| where Description != "Canary Reconnected"
Stage 4: where
| where Description != "Canary Settings Changed"
Stage 5: where
| where Description != "Fake Location"
Stage 6: where
| where Description != "Network Settings Roll-back"
Stage 7: extend (6 consecutive steps)
| extend EventData = todynamic(RawEvent[0])
| extend IsCanarytoken = Description contains "Canarytoken"
or LogType startswith "16"
or LogType startswith "17"
or isnotempty(tostring(EventData.canarytoken))
| extend PhysicalCanaryName = coalesce(tostring(Host.name), NodeId),
PhysicalCanaryIP = coalesce(tostring(Host.ip_address), DestinationIP, IpAddress),
TokenMemo = iff(IsCanarytoken, substring(Memo, 0, 256), ""),
TokenType = coalesce(tostring(EventData.kind), tostring(EventData.type)),
TokenHostname = tostring(EventData.hostname),
TokenTarget = substring(coalesce(tostring(EventData.url), tostring(EventData.hostname), tostring(EventData.cloned_site), tostring(EventData.original_site), tostring(EventData.generic_data), tostring(EventData.location)), 0, 512),
TokenUsername = coalesce(tostring(EventData.windows_desktopini_access_username), tostring(EventData.ms_macro_username), tostring(EventData.cmd_user_name)),
TokenDomain = coalesce(tostring(EventData.windows_desktopini_access_domain), tostring(EventData.cmd_machine_name)),
TokenHost = coalesce(tostring(EventData.windows_desktopini_computer_name), tostring(EventData.cmd_computer_name), tostring(EventData.cmd_workstation), tostring(EventData.cmd_machine_name)),
TokenSourceIP = coalesce(tostring(EventData.cmd_resolved_ip), tostring(EventData.ms_macro_ip)),
TokenCity = coalesce(tostring(EventData.geoip.city), tostring(EventData.geoip.City), tostring(EventData.City)),
TokenCountry = coalesce(tostring(EventData.geoip.country), tostring(EventData.geoip.country_name), tostring(EventData.geoip.Country), tostring(EventData.Country)),
TokenContext = case(
isnotempty(tostring(EventData.client_public_key)), strcat("WireGuard public key: ", substring(tostring(EventData.client_public_key), 0, 128), "; session: ", tostring(EventData.client_session_index)),
isnotempty(tostring(EventData.masked_card_number)), strcat("Masked card: ", tostring(EventData.masked_card_number), "; transaction: ", tostring(EventData.transaction_amount), " ", tostring(EventData.transaction_currency), "; merchant: ", tostring(EventData.merchant)),
isnotempty(tostring(EventData.referer)), strcat("Referrer: ", substring(tostring(EventData.referer), 0, 256)),
""),
UserAgent = substring(coalesce(tostring(EventData.USERAGENT), tostring(EventData.HEADERS['user-agent']), tostring(EventData.headers['User-Agent']), tostring(EventData.headers['user-agent'])), 0, 512)
| extend ActorUsername = coalesce(tostring(EventData.USERNAME), tostring(EventData.USER), tostring(EventData.FORMDATA.username), tostring(EventData.POSTDATA.username), TokenUsername),
ActorDomain = coalesce(tostring(EventData.DOMAIN), tostring(EventData.DOMAINNAME), TokenDomain),
ActorHost = coalesce(TokenHost, tostring(EventData.HOSTNAME)),
Activity = coalesce(TokenContext, TokenType, tostring(EventData.INSTANCE_NAME), tostring(EventData.FUNC_NAME), tostring(EventData.METHOD), tostring(EventData.OPCODE), tostring(EventData.COMMAND), tostring(EventData.FUNCTION), Description)
| extend SourceIP = coalesce(SourceIP, tostring(EventData.src_host), TokenSourceIP),
SourceGeo = case(isnotempty(TokenCity) and isnotempty(TokenCountry), strcat(TokenCity, ", ", TokenCountry), isnotempty(TokenCity), TokenCity, TokenCountry),
AssetType = iff(IsCanarytoken, "Canarytoken", "Canary"),
AssetName = iff(IsCanarytoken, coalesce(TokenMemo, TokenTarget, TokenType, NodeId), PhysicalCanaryName),
AssetNode = NodeId,
AssetIP = iff(IsCanarytoken, "", PhysicalCanaryIP),
AssetLocation = iff(IsCanarytoken, "", tostring(Host.description)),
CanaryEntityName = iff(IsCanarytoken, "", PhysicalCanaryName),
CanaryEntityIP = iff(IsCanarytoken, "", PhysicalCanaryIP)
| extend SourceDisplay = coalesce(SourceIP, SrcHostReverse, "unknown source")
Stage 8: project
| project TimeGenerated, Description, IncidentId, AssetType, AssetName, AssetNode,
AssetIP, AssetLocation, FlockName, TokenMemo, TokenType, TokenTarget,
TokenHostname, ActorUsername, ActorDomain, ActorHost, SourceGeo, SourceIP,
SourceDisplay, SrcHostReverse, DestinationPort, Activity, UserAgent, LogType,
EventsCount, CanaryEntityName, CanaryEntityIP
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Description | ne |
| field:"Description" kind:ne |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Activity | project |
ActorDomain | project |
ActorHost | project |
ActorUsername | project |
AssetIP | project |
AssetLocation | project |
AssetName | project |
AssetNode | project |
AssetType | project |
CanaryEntityIP | project |
CanaryEntityName | project |
Description | project |
DestinationPort | project |
EventsCount | project |
FlockName | project |
IncidentId | project |
LogType | project |
SourceDisplay | project |
SourceGeo | project |
SourceIP | project |
SrcHostReverse | project |
TimeGenerated | project |
TokenHostname | project |
TokenMemo | project |
TokenTarget | project |
TokenType | project |
UserAgent | project |
Contrast Suspicious
#'Creates Incidents for Suspicious events sourced from the Contrast Protect agent.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: f713404e-805c-4e0c-91fa-2c149f76a07d
name: Contrast Suspicious
description: |
'Creates Incidents for Suspicious events sourced from the Contrast Protect agent.'
severity: Medium
requiredDataConnectors:
- connectorId: ContrastProtect
dataTypes:
- CommonSecurityLog
- connectorId: ContrastProtectAma
dataTypes:
- CommonSecurityLog
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- InitialAccess
- Exfiltration
relevantTechniques:
- T1566
query: |
let extract_data=(a:string, k:string) {
parse_urlquery(replace(@';', @'&', a))["Query Parameters"][k]
};
CommonSecurityLog
| where DeviceVendor == "Contrast Security"
| where AdditionalExtensions contains "SUSPICIOUS"
| extend DeviceProduct
| extend SourceIP
| extend DeviceVersion
| extend Activity
| extend ApplicationProtocol
| extend RequestURL
| extend RequestMethod
| extend Rule = extract_data(AdditionalExtensions, 'pri')
customDetails:
Application: ApplicationProtocol
Agent: DeviceProduct
AgentVersion: DeviceVersion
Attack: Activity
Details: AdditionalExtensions
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIP
- entityType: URL
fieldMappings:
- identifier: Url
columnName: RequestURL
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: ApplicationProtocol
- entityType: Malware
fieldMappings:
- identifier: Name
columnName: Activity
- identifier: Category
columnName: Rule
version: 1.0.2
kind: Scheduled
Stages and Predicates
Let Binding: extract_data
let extract_data = (a:string, k:string) {
parse_urlquery(replace(@';', @'&', a))["Query Parameters"][k]
};
Stage 1: source
CommonSecurityLog
Stage 2: where
| where DeviceVendor == "Contrast Security"
Stage 3: where
| where AdditionalExtensions contains "SUSPICIOUS"
Stage 4: extend (8 consecutive steps)
| extend DeviceProduct
| extend SourceIP
| extend DeviceVersion
| extend Activity
| extend ApplicationProtocol
| extend RequestURL
| extend RequestMethod
| extend Rule = extract_data(AdditionalExtensions, 'pri')
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AdditionalExtensions | contains |
| field:"AdditionalExtensions" kind:contains value:"SUSPICIOUS" |
DeviceVendor | eq |
| field:"DeviceVendor" kind:eq value:"Contrast Security" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DeviceProduct | extend |
SourceIP | extend |
DeviceVersion | extend |
Activity | extend |
ApplicationProtocol | extend |
RequestURL | extend |
RequestMethod | extend |
Rule | extend |
Cyble Vision Alerts Leaked Credentials
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
'Detects leaked credentials identified by CybleVision ingestion and triggers an incident with mapped entities, severity, and details.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access | |
| Discovery |
Rule Body
id: 224a63ae-e278-4a11-b7c2-02ec3e17b56c
name: Cyble Vision Alerts Leaked Credentials
description: |
'Detects leaked credentials identified by CybleVision ingestion and triggers an incident with mapped entities, severity, and details.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
dataTypes:
- CybleVisionAlerts_CL
enabled: true
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: PT5H
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- CredentialAccess
- Discovery
- Reconnaissance
relevantTechniques:
- T1552
- T1082
subTechniques: []
query: |
Alerts_leaked_credentials
| where Service == "leaked_credentials"
| extend MappedSeverity = Severity
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
alertDetailsOverride:
alertDisplayNameFormat: Leaked Credentials Detected {{LC_Username}}
alertDescriptionFormat: |
A leaked set of credentials associated with {{LC_Username}} was detected. Domain {{LC_Domain}} Source URL {{LC_URL}}
alertDynamicProperties: []
customDetails:
MappedSeverity: Severity
Status: Status
AlertID: AlertID
Service: Service
MappedSeveritry: MappedSeverity
Leak_Username: LC_Username
Leak_Url: LC_URL
Leak_Date: LC_Date
Leak_Tags: LC_Tags
Leak_Data: LC_Data
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: LC_Username
- entityType: URL
fieldMappings:
- identifier: Url
columnName: LC_URL
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: LC_Domain
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Alerts_leaked_credentials
Stage 2: where
| where Service == "leaked_credentials"
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:"leaked_credentials" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
MappedSeverity | extend |
GreyNoise TI map IP entity to Network Session Events (ASIM Network Session schema)
#This rule identifies a match Network Sessions for which the source or destination IP address is a known GreyNoise IoC. This analytic rule uses ASIM and supports any built-in or custom source that supports the ASIM NetworkSession schema
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry Coverage
Rule Body
id: 536e8e5c-ce0e-575e-bcc9-aba8e7bf9316
name: GreyNoise TI map IP entity to Network Session Events (ASIM Network Session schema)
version: 1.0.3
kind: Scheduled
description: |
'This rule identifies a match Network Sessions for which the source or destination IP address is a known GreyNoise IoC.
This analytic rule uses [ASIM](https://aka.ms/AboutASIM) and supports any built-in or custom source that supports the ASIM NetworkSession schema'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AWSS3
dataTypes:
- AWSVPCFlow
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceNetworkEvents
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog
- connectorId: MicrosoftSysmonForLinux
dataTypes:
- Syslog
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog
- connectorId: AzureMonitor(VMInsights)
dataTypes:
- VMConnection
- connectorId: AzureFirewall
dataTypes:
- AzureDiagnostics
- connectorId: AzureNSG
dataTypes:
- AzureDiagnostics
- connectorId: CiscoASA
dataTypes:
- CommonSecurityLog
- connectorId: CiscoAsaAma
dataTypes:
- CommonSecurityLog
- connectorId: Corelight
dataTypes:
- Corelight_CL
- connectorId: AIVectraStream
dataTypes:
- VectraStream
- connectorId: CheckPoint
dataTypes:
- CommonSecurityLog
- connectorId: Fortinet
dataTypes:
- CommonSecurityLog
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: CiscoMeraki
dataTypes:
- Syslog
- CiscoMerakiNativePoller
- connectorId: GreyNoise2SentinelAPI
dataTypes:
- ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_TI = materialize (
ThreatIntelligenceIndicator
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now()
| where SourceSystem == 'GreyNoise'
| extend TI_ipEntity = coalesce(NetworkIP, NetworkDestinationIP, NetworkSourceIP,EmailSourceIpAddress,"NO_IP")
| where TI_ipEntity != "NO_IP"
);
IP_TI
// 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_NetworkSession (starttime=ago(dt_lookBack))
| where isnotempty(SrcIpAddr)
| summarize imNWS_mintime=min(TimeGenerated), imNWS_maxtime=max(TimeGenerated) by SrcIpAddr, DstIpAddr, Dvc, EventProduct, EventVendor
| lookup (IP_TI | project TI_ipEntity, Active) on $left.SrcIpAddr == $right.TI_ipEntity
| project-rename SrcMatch = Active
| lookup (IP_TI | project TI_ipEntity, Active) on $left.DstIpAddr == $right.TI_ipEntity
| project-rename DstMatch = Active
| where SrcMatch or DstMatch
| extend
IoCIP = iff(SrcMatch, SrcIpAddr, DstIpAddr),
IoCDirection = iff(SrcMatch, "Source", "Destination")
)on $left.TI_ipEntity == $right.IoCIP
| where imNWS_mintime < ExpirationDateTime
| project imNWS_mintime, imNWS_maxtime, Description, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, SrcIpAddr, DstIpAddr, IoCDirection, IoCIP, Dvc, EventVendor, EventProduct
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IoCIP
customDetails:
EventStartTime: imNWS_mintime
EventEndTime: imNWS_maxtime
IoCDescription: Description
ActivityGroupNames: ActivityGroupNames
IndicatorId: IndicatorId
ThreatType: ThreatType
IoCExpirationTime: ExpirationDateTime
IoCConfidenceScore: ConfidenceScore
IoCIPDirection: IoCDirection
alertDetailsOverride:
alertDisplayNameFormat: A network session {{IoCDirection}} address {{IoCIP}} matched an IoC.
alertDescriptionFormat: The {{IoCDirection}} address {{IoCIP}} of a network session matched a known indicator of compromise of {{ThreatType}}. Consult the threat intelligence blead for more information on the indicator.
tags:
- Schema: ASIMNetworkSession
SchemaVersion: 0.2.4
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let IP_TI is inlined into the numbered stages below.
Stages 1 to 7 define let IP_TI as the rule's main pipeline source. Stages 8 to 10 use it.
Stage 1: source
ThreatIntelligenceIndicator
Stage 2: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 3: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
Stage 4: where
| where Active == true and ExpirationDateTime > now()
Stage 5: where
| where SourceSystem == 'GreyNoise'
Stage 6: extend
| extend TI_ipEntity = coalesce(NetworkIP, NetworkDestinationIP, NetworkSourceIP,EmailSourceIpAddress,"NO_IP")
Stage 7: where
| where TI_ipEntity != "NO_IP"
Stage 8: join
join kind=innerunique (_Im_NetworkSession) on $left.TI_ipEntity, $right.IoCIP
Stage 9: where
where imNWS_mintime < ExpirationDateTime
Stage 10: project
project ActivityGroupNames, ConfidenceScore, Description, DstIpAddr, Dvc, EventProduct, EventVendor, ExpirationDateTime, IndicatorId, IoCDirection, IoCIP, SrcIpAddr, ThreatType, imNWS_maxtime, imNWS_mintime
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Active | eq |
| field:"Active" kind:eq value:"true" |
DstMatch | eq |
| field:"DstMatch" kind:eq value:"true" |
SourceSystem | eq |
| field:"SourceSystem" kind:eq value:"GreyNoise" |
SrcIpAddr | is_not_null | field:"SrcIpAddr" kind:is_not_null | |
SrcMatch | eq |
| field:"SrcMatch" kind:eq value:"true" |
TI_ipEntity | ne |
| field:"TI_ipEntity" kind:ne value:"NO_IP" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActivityGroupNames | project |
ConfidenceScore | project |
Description | project |
DstIpAddr | project |
Dvc | project |
EventProduct | project |
EventVendor | project |
ExpirationDateTime | project |
IndicatorId | project |
IoCDirection | project |
IoCIP | project |
SrcIpAddr | project |
ThreatType | project |
imNWS_maxtime | project |
imNWS_mintime | project |
Infoblox - Many High Threat Level Single Query Detected
#'Single high threat level domain queried at least 200 times in 1 hour regardless of source. Customize query count, scheduling, responses and more. This rule depends on a parser based on a Kusto Function to work as expected called InfobloxCDC.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 99278700-79ca-4b0f-b416-bf57ec699e1a
name: Infoblox - Many High Threat Level Single Query Detected
description: |
'Single high threat level domain queried at least 200 times in 1 hour regardless of source. Customize query count, scheduling, responses and more. This rule depends on a parser based on a Kusto Function to work as expected called [**InfobloxCDC**](https://aka.ms/sentinel-InfobloxCloudDataConnector-parser).'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1498
- T1565
query: |
let threshold = 200;
InfobloxCDC
| where DeviceEventClassID has_cs "RPZ"
| where ThreatLevel_Score >= 80
| summarize count() by DestinationDnsDomain
| where count_ > threshold
| join kind=inner (InfobloxCDC
| where DeviceEventClassID has_cs "RPZ"
| where ThreatLevel_Score >= 80
) on DestinationDnsDomain
entityMappings:
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: DestinationDnsDomain
- entityType: Malware
fieldMappings:
- identifier: Name
columnName: ThreatProperty
- identifier: Category
columnName: ThreatClass
customDetails:
InfobloxB1FeedName: InfobloxB1FeedName
InfobloxB1Network: InfobloxB1Network
InfobloxB1PolicyName: InfobloxB1PolicyName
eventGroupingSettings:
aggregationKind: SingleAlert
incidentConfiguration:
createIncident: true
version: 1.0.4
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 200;
Stage 1: source
InfobloxCDC
Stage 2: where
| where DeviceEventClassID has_cs "RPZ"
Stage 3: where
| where ThreatLevel_Score >= 80
Stage 4: summarize
| summarize count() by DestinationDnsDomain
Stage 5: where
| where count_ > threshold
Stage 6: join
| join kind=inner (InfobloxCDC
| where DeviceEventClassID has_cs "RPZ"
| where ThreatLevel_Score >= 80
) on DestinationDnsDomain
Indicators
These rows show field, operator, and value matches.
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DestinationDnsDomain | summarize |
Insider Risk_Sensitive Data Access Outside Organizational Geo-location
#This alert joins Azure Information Protection Logs (InformationProtectionLogs_CL) with Microsoft Entra ID Sign in Logs (SigninLogs) to provide a correlation of sensitive data access by geo-location. Results include User Principal Name, Label Name, Activity, City, State, Country/Region, and Time Generated. Recommended configuration is to include (or exclude) Sign in geo-locations (City, State, Country and/or Region) for trusted organizational locations. There is an option for configuration of correlations against Microsoft Sentinel watchlists. Accessing sensitive data from a new or unauthorized geo-location warrants further review. For more information see Sign-in logs in Microsoft Entra ID: Location Filtering
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Entra ID | SigninLogs sign-in event: Sign-in record (SigninLogs, any result) |
Rule Body
id: b81ed294-28cf-48c3-bac8-ac60dcef293b
name: Insider Risk_Sensitive Data Access Outside Organizational Geo-location
description: |
'This alert joins Azure Information Protection Logs (InformationProtectionLogs_CL) with Microsoft Entra ID Sign in Logs (SigninLogs) to provide a correlation of sensitive data access by geo-location. Results include User Principal Name, Label Name, Activity, City, State, Country/Region, and Time Generated. Recommended configuration is to include (or exclude) Sign in geo-locations (City, State, Country and/or Region) for trusted organizational locations. There is an option for configuration of correlations against Microsoft Sentinel watchlists. Accessing sensitive data from a new or unauthorized geo-location warrants further review. For more information see [Sign-in logs in Microsoft Entra ID: Location Filtering](https://docs.microsoft.com/azure/active-directory/reports-monitoring/concept-sign-ins)'
severity: High
requiredDataConnectors:
- connectorId: AzureInformationProtection
dataTypes:
- InformationProtectionLogs_CL
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
queryFrequency: 6h
queryPeriod: 6h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
relevantTechniques:
- T1567
query: |
InformationProtectionLogs_CL
| extend UserPrincipalName = UserId_s
| where LabelName_s <> ""
| join kind=inner (SigninLogs) on UserPrincipalName
| extend City = tostring(LocationDetails.city)
// | where City <> "New York" // Configure Location Details within Organizational Requirements
| extend State = tostring(LocationDetails.state)
// | where State <> "Texas" // Configure Location Details within Organizational Requirements
| extend Country_Region = tostring(LocationDetails.countryOrRegion)
// | where Country_Region <> "US" // Configure Location Details within Organizational Requirements
// | lookup kind=inner _GetWatchlist('<Your Watchlist Name>') on $left.UserPrincipalName == $right.SearchKey
| summarize count() by UserPrincipalName, LabelName_s, Activity_s, City, State, Country_Region, TimeGenerated
| sort by count_ desc
| extend AccountName = tostring(split(UserPrincipalName, "@")[0]), AccountUPNSuffix = tostring(split(UserPrincipalName, "@")[1])
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserPrincipalName
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: true
lookbackDuration: 3d
matchingMethod: Selected
groupByEntities:
- Account
version: 1.1.3
kind: Scheduled
Stages and Predicates
Stage 1: source
InformationProtectionLogs_CL
Stage 2: extend
| extend UserPrincipalName = UserId_s
Stage 3: where
| where LabelName_s <> ""
Stage 4: join
| join kind=inner (SigninLogs) on UserPrincipalName
Stage 5: extend (3 consecutive steps)
extend City, Country_Region, State
Stage 6: summarize
summarize by UserPrincipalName, LabelName_s, Activity_s, City, State, Country_Region, TimeGenerated
Stage 7: sort
sort by count_
Stage 8: extend
extend AccountName, AccountUPNSuffix
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Activity_s | summarize |
City | summarize |
Country_Region | summarize |
LabelName_s | summarize |
State | summarize |
TimeGenerated | summarize |
UserPrincipalName | summarize |
AccountName | extend |
AccountUPNSuffix | extend |
Microsoft Entra ID PowerShell accessing non-Entra ID resources
#This will alert when a user or application signs in using Microsoft Entra ID PowerShell to access non-Active Directory resources, such as the Azure Key Vault, which may be undesired or unauthorized behavior. For capabilities and expected behavior of the Microsoft Entra ID PowerShell module, see: https://docs.microsoft.com/powershell/module/azuread/?view=azureadps-2.0. For further information on Microsoft Entra ID Signin activity reports, see: https://docs.microsoft.com/azure/active-directory/reports-monitoring/concept-sign-ins.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Telemetry Coverage
Rule Body
id: 50574fac-f8d1-4395-81c7-78a463ff0c52
name: Microsoft Entra ID PowerShell accessing non-Entra ID resources
description: |
'This will alert when a user or application signs in using Microsoft Entra ID PowerShell to access non-Active Directory resources, such as the Azure Key Vault, which may be undesired or unauthorized behavior.
For capabilities and expected behavior of the Microsoft Entra ID PowerShell module, see: https://docs.microsoft.com/powershell/module/azuread/?view=azureadps-2.0.
For further information on Microsoft Entra ID Signin activity reports, see: https://docs.microsoft.com/azure/active-directory/reports-monitoring/concept-sign-ins.'
severity: Low
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- connectorId: AzureActiveDirectory
dataTypes:
- AADNonInteractiveUserSignInLogs
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- InitialAccess
relevantTechniques:
- T1078
tags:
- Solorigate
- NOBELIUM
query: |
let aadFunc = (tableName:string){
table(tableName)
| where AppId =~ "1b730954-1685-4b74-9bfd-dac224a7b894" // AppDisplayName IS Azure Active Directory PowerShell
| where TokenIssuerType =~ "AzureAD"
| where ResourceIdentity !in ("00000002-0000-0000-c000-000000000000", "00000003-0000-0000-c000-000000000000") // ResourceDisplayName IS NOT Windows Azure Active Directory OR Microsoft Graph
| extend Status = todynamic(Status)
| where Status.errorCode == 0 // Success
| project-reorder IPAddress, UserAgent, ResourceDisplayName, UserDisplayName, UserId, UserPrincipalName, Type
| order by TimeGenerated desc
| extend Name = tostring(split(UserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName,'@',1)[0])
};
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
union isfuzzy=true aadSignin, aadNonInt
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserPrincipalName
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: UserId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPAddress
version: 1.0.4
kind: Scheduled
Stages and Predicates
Parameters
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
Let Binding: aadFunc
let aadFunc = (tableName:string){
table(tableName)
| where AppId =~ "1b730954-1685-4b74-9bfd-dac224a7b894"
| where TokenIssuerType =~ "AzureAD"
| where ResourceIdentity !in ("00000002-0000-0000-c000-000000000000", "00000003-0000-0000-c000-000000000000")
| extend Status = todynamic(Status)
| where Status.errorCode == 0
| project-reorder IPAddress, UserAgent, ResourceDisplayName, UserDisplayName, UserId, UserPrincipalName, Type
| order by TimeGenerated desc
| extend Name = tostring(split(UserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName,'@',1)[0])
};
Union isfuzzy=true (2 Sources)
Each leg below queries one source; the rule matches if any leg does. Sources: aadSignin, aadNonInt
Leg 1: aadSignin
Leg 2: aadNonInt
Microsoft Entra ID Rare UserAgent App Sign-in
#This query establishes a baseline of the type of UserAgent (i.e. browser, office application, etc) that is typically used for a particular application by looking back for a number of days. It then searches the current day for any deviations from this pattern, i.e. types of UserAgents not seen before in combination with this application.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Azure | Success: Successful sign-in |
| Entra ID | SigninLogs sign-in event: Successful sign-in |
Rules Detecting the Same Action
These rules filter on the same operation.
- [Entra ID] Suspicious Continuous OAuth Token Usage (Kusto)
- Anomalous sign-in location by user account and authenticating application (Kusto)
- Anomalous Single Factor Signin (Kusto)
- Authentications of Privileged Accounts Outside of Expected Controls (Kusto)
- Azure Portal sign in from another Azure Tenant (Kusto)
- Azure Service Principal Sign-In Followed by Arc Cluster Credential Access (Elastic)
- Azure SignIn via Legacy Authentication Protocol (Panther)
- Cisco - firewall block but success logon to Microsoft Entra ID (Kusto)
Rule Body
id: 87d5cd18-211d-4fd4-9b86-65d23fed87ea
name: Microsoft Entra ID Rare UserAgent App Sign-in
description: |
This query establishes a baseline of the type of UserAgent (i.e. browser, office application, etc) that is typically used for a particular application by looking back for a number of days.
It then searches the current day for any deviations from this pattern, i.e. types of UserAgents not seen before in combination with this application.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- connectorId: AzureActiveDirectory
dataTypes:
- AADNonInteractiveUserSignInLogs
queryFrequency: 1d
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1036
query: |
let minimumAppThreshold = 100;
let timeframe = 1d;
let lookback_timeframe= 7d;
let ExtractBrowserTypeFromUA=(ua:string) {
// Note: these are in a specific order since, for example, Edge contains "Chrome/" and "Edge/" strings.
case(
ua has "Edge/", dynamic({"AgentType": "Browser", "AgentName": "Edge"}),
ua has "Edg/", dynamic({"AgentType": "Browser", "AgentName": "Edge"}),
ua has "Trident/", dynamic({"AgentType": "Browser", "AgentName": "Internet Explorer"}),
ua has "Chrome/" and ua has "Safari/", dynamic({"AgentType": "Browser", "AgentName": "Chrome"}),
ua has "Gecko/" and ua has "Firefox/", dynamic({"AgentType": "Browser", "AgentName": "Firefox"}),
not(ua has "Mobile/") and ua has "Safari/" and ua has "Version/", dynamic({"AgentType": "Browser", "AgentName": "Safari"}),
ua startswith "Dalvik/" and ua has "Android", dynamic({"AgentType": "Browser", "AgentName": "Android Browser"}),
ua startswith "MobileSafari//", dynamic({"AgentType": "Browser", "AgentName": "Mobile Safari"}),
ua has "Mobile/" and ua has "Safari/" and ua has "Version/", dynamic({"AgentType": "Browser", "AgentName": "Mobile Safari"}),
ua has "Mobile/" and ua has "FxiOS/", dynamic({"AgentType": "Browser", "AgentName": "IOS Firefox"}),
ua has "Mobile/" and ua has "CriOS/", dynamic({"AgentType": "Browser", "AgentName": "IOS Chrome"}),
ua has "Mobile/" and ua has "WebKit/", dynamic({"AgentType": "Browser", "AgentName": "Mobile Webkit"}),
//
ua startswith "Excel/", dynamic({"AgentType": "OfficeApp", "AgentName": "Excel"}),
ua startswith "Outlook/", dynamic({"AgentType": "OfficeApp", "AgentName": "Outlook"}),
ua startswith "OneDrive/", dynamic({"AgentType": "OfficeApp", "AgentName": "OneDrive"}),
ua startswith "OneNote/", dynamic({"AgentType": "OfficeApp", "AgentName": "OneNote"}),
ua startswith "Office/", dynamic({"AgentType": "OfficeApp", "AgentName": "Office"}),
ua startswith "PowerPoint/", dynamic({"AgentType": "OfficeApp", "AgentName": "PowerPoint"}),
ua startswith "PowerApps/", dynamic({"AgentType": "OfficeApp", "AgentName": "PowerApps"}),
ua startswith "SharePoint/", dynamic({"AgentType": "OfficeApp", "AgentName": "SharePoint"}),
ua startswith "Word/", dynamic({"AgentType": "OfficeApp", "AgentName": "Word"}),
ua startswith "Visio/", dynamic({"AgentType": "OfficeApp", "AgentName": "Visio"}),
ua startswith "Whiteboard/", dynamic({"AgentType": "OfficeApp", "AgentName": "Whiteboard"}),
ua =~ "Mozilla/5.0 (compatible; MSAL 1.0)", dynamic({"AgentType": "OfficeApp", "AgentName": "Office Telemetry"}),
//
ua has ".NET CLR", dynamic({"AgentType": "Custom", "AgentName": "Dotnet"}),
ua startswith "Java/", dynamic({"AgentType": "Custom", "AgentName": "Java"}),
ua startswith "okhttp/", dynamic({"AgentType": "Custom", "AgentName": "okhttp"}),
ua has "Drupal/", dynamic({"AgentType": "Custom", "AgentName": "Drupal"}),
ua has "PHP/", dynamic({"AgentType": "Custom", "AgentName": "PHP"}),
ua startswith "curl/", dynamic({"AgentType": "Custom", "AgentName": "curl"}),
ua has "python-requests", dynamic({"AgentType": "Custom", "AgentName": "Python"}),
pack("AgentType","Other","AgentName", extract(@"^([^/]*)/",1,ua))
)
};
// Query to obtain 'simplified' user agents in a given timespan.
let QueryUserAgents = (start_time:timespan, end_time:timespan) {
union withsource=tbl_name AADNonInteractiveUserSignInLogs, SigninLogs
| where TimeGenerated >= ago(start_time)
| where TimeGenerated < ago(end_time)
| where ResultType == 0 // Only look at succesful logins
| extend ParsedUserAgent=ExtractBrowserTypeFromUA(UserAgent)
| extend UserAgentType=tostring(ParsedUserAgent.AgentType)
| extend UserAgentName=tostring(ParsedUserAgent.AgentName)
//| extend SimpleUserAgent=strcat(UserAgentType,"_",UserAgentName)
| extend SimpleUserAgent=UserAgentType
| where not(isempty(UserAgent))
| where not(isempty(AppId))
};
// Get baseline usage per application.
let BaselineUserAgents=materialize(
QueryUserAgents(lookback_timeframe+timeframe, timeframe)
| summarize RequestCount=count() by AppId, AppDisplayName, SimpleUserAgent
);
let BaselineSummarizedAgents=(
BaselineUserAgents
| summarize BaselineUAs=make_set(SimpleUserAgent),BaselineRequestCount=sum(RequestCount) by AppId, AppDisplayName
);
QueryUserAgents(timeframe, 0d)
| summarize count() by AppId, AppDisplayName, UserAgent, SimpleUserAgent
| join kind=leftanti BaselineUserAgents on AppId, AppDisplayName, SimpleUserAgent
| join BaselineSummarizedAgents on AppId, AppDisplayName
| where BaselineRequestCount > minimumAppThreshold // Search only for actively used applications.
// Get back full original requests.
| join QueryUserAgents(timeframe, 0d) on AppId, UserAgent
| project-away ParsedUserAgent, UserAgentName
| project-reorder TimeGenerated, AppDisplayName, UserPrincipalName, UserAgent, BaselineUAs
// Begin allow-list.
// End allow-list.
| summarize count() by UserPrincipalName, AppDisplayName, AppId, UserAgentType, SimpleUserAgent, UserAgent
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserPrincipalName
version: 1.0.1
kind: Scheduled
Stages and Predicates
Parameters
let minimumAppThreshold = 100;
let timeframe = 1d;
let lookback_timeframe = 7d;
let QueryUserAgents is inlined into the numbered stages below.
Let Binding: ExtractBrowserTypeFromUA
let ExtractBrowserTypeFromUA = (ua:string) {
case(
ua has "Edge/", dynamic({"AgentType": "Browser", "AgentName": "Edge"}),
ua has "Edg/", dynamic({"AgentType": "Browser", "AgentName": "Edge"}),
ua has "Trident/", dynamic({"AgentType": "Browser", "AgentName": "Internet Explorer"}),
ua has "Chrome/" and ua has "Safari/", dynamic({"AgentType": "Browser", "AgentName": "Chrome"}),
ua has "Gecko/" and ua has "Firefox/", dynamic({"AgentType": "Browser", "AgentName": "Firefox"}),
not(ua has "Mobile/") and ua has "Safari/" and ua has "Version/", dynamic({"AgentType": "Browser", "AgentName": "Safari"}),
ua startswith "Dalvik/" and ua has "Android", dynamic({"AgentType": "Browser", "AgentName": "Android Browser"}),
ua startswith "MobileSafari//", dynamic({"AgentType": "Browser", "AgentName": "Mobile Safari"}),
ua has "Mobile/" and ua has "Safari/" and ua has "Version/", dynamic({"AgentType": "Browser", "AgentName": "Mobile Safari"}),
ua has "Mobile/" and ua has "FxiOS/", dynamic({"AgentType": "Browser", "AgentName": "IOS Firefox"}),
ua has "Mobile/" and ua has "CriOS/", dynamic({"AgentType": "Browser", "AgentName": "IOS Chrome"}),
ua has "Mobile/" and ua has "WebKit/", dynamic({"AgentType": "Browser", "AgentName": "Mobile Webkit"}),
ua startswith "Excel/", dynamic({"AgentType": "OfficeApp", "AgentName": "Excel"}),
ua startswith "Outlook/", dynamic({"AgentType": "OfficeApp", "AgentName": "Outlook"}),
ua startswith "OneDrive/", dynamic({"AgentType": "OfficeApp", "AgentName": "OneDrive"}),
ua startswith "OneNote/", dynamic({"AgentType": "OfficeApp", "AgentName": "OneNote"}),
ua startswith "Office/", dynamic({"AgentType": "OfficeApp", "AgentName": "Office"}),
ua startswith "PowerPoint/", dynamic({"AgentType": "OfficeApp", "AgentName": "PowerPoint"}),
ua startswith "PowerApps/", dynamic({"AgentType": "OfficeApp", "AgentName": "PowerApps"}),
ua startswith "SharePoint/", dynamic({"AgentType": "OfficeApp", "AgentName": "SharePoint"}),
ua startswith "Word/", dynamic({"AgentType": "OfficeApp", "AgentName": "Word"}),
ua startswith "Visio/", dynamic({"AgentType": "OfficeApp", "AgentName": "Visio"}),
ua startswith "Whiteboard/", dynamic({"AgentType": "OfficeApp", "AgentName": "Whiteboard"}),
ua =~ "Mozilla/5.0 (compatible; MSAL 1.0)", dynamic({"AgentType": "OfficeApp", "AgentName": "Office Telemetry"}),
ua has ".NET CLR", dynamic({"AgentType": "Custom", "AgentName": "Dotnet"}),
ua startswith "Java/", dynamic({"AgentType": "Custom", "AgentName": "Java"}),
ua startswith "okhttp/", dynamic({"AgentType": "Custom", "AgentName": "okhttp"}),
ua has "Drupal/", dynamic({"AgentType": "Custom", "AgentName": "Drupal"}),
ua has "PHP/", dynamic({"AgentType": "Custom", "AgentName": "PHP"}),
ua startswith "curl/", dynamic({"AgentType": "Custom", "AgentName": "curl"}),
ua has "python-requests", dynamic({"AgentType": "Custom", "AgentName": "Python"}),
pack("AgentType","Other","AgentName", extract(@"^([^/]*)/",1,ua))
)
};
Let Binding: BaselineUserAgents
let BaselineUserAgents = materialize(
QueryUserAgents(lookback_timeframe+timeframe, timeframe)
| summarize RequestCount=count() by AppId, AppDisplayName, SimpleUserAgent
);
Let Binding: BaselineSummarizedAgents
let BaselineSummarizedAgents = (
BaselineUserAgents
| summarize BaselineUAs=make_set(SimpleUserAgent),BaselineRequestCount=sum(RequestCount) by AppId, AppDisplayName
);
Stages 1 to 9 define let QueryUserAgents as the rule's main pipeline source. Stages 10 to 17 use it.
Stage 1: union
union of 2 branches
Stage 2: source
AADNonInteractiveUserSignInLogs
Stage 3: source
SigninLogs
Stage 4: where
where ...
Stage 5: where
where TimeGenerated < ago(0s)
Stage 6: where
where ResultType == 0
Stage 7: extend (4 consecutive steps)
extend ParsedUserAgent, SimpleUserAgent, UserAgentName, UserAgentType
Stage 8: where
where not (isempty(UserAgent))
Stage 9: where
where not (isempty(AppId))
Stage 10: summarize
summarize by AppId, AppDisplayName, UserAgent, SimpleUserAgent
Stage 11: join (negated)
join kind=leftanti (BaselineUserAgents) on AppId, AppDisplayName, SimpleUserAgent
Stage 12: join
| join BaselineSummarizedAgents on AppId, AppDisplayName
Stage 13: where
where BaselineRequestCount > 100
Stage 14: join
| join QueryUserAgents(timeframe, 0d) on AppId, UserAgent
Stage 15: project-away
project-away ParsedUserAgent, UserAgentName
Stage 16: project-reorder
project-reorder
Stage 17: summarize
summarize by UserPrincipalName, AppDisplayName, AppId, UserAgentType, SimpleUserAgent, UserAgent
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
BaselineRequestCount | gt |
| field:"BaselineRequestCount" kind:gt value:"100" |
ResultType | eq |
| field:"ResultType" kind:eq value:"0" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
UserAgent | is_null | excludes:UserAgent | |
AppId | is_null | excludes:AppId |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AppDisplayName | summarize |
AppId | summarize |
SimpleUserAgent | summarize |
UserAgent | summarize |
UserAgentType | summarize |
UserPrincipalName | summarize |
Pathlock TDnR - SAP Cloud Connector Events
#Detects security events from the SAP Cloud Connector (on-premise agent), forwarded by Pathlock Threat Detection and Response. Cloud Connector anomalies may indicate unauthorized configuration changes, tunnel abuse, or attempts to exploit the SAP Cloud Connector as a pivot point between on-premise and cloud environments.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Lateral Movement |
Rule Body
id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c54
name: Pathlock TDnR - SAP Cloud Connector Events
kind: Scheduled
description: >-
Detects security events from the SAP Cloud Connector (on-premise agent), forwarded by Pathlock
Threat Detection and Response. Cloud Connector anomalies may indicate unauthorized configuration
changes, tunnel abuse, or attempts to exploit the SAP Cloud Connector as a pivot point between
on-premise and cloud environments.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Pathlock_TDnR
dataTypes:
- Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- LateralMovement
relevantTechniques:
- T1021
query: |
Pathlock_TDnR_CL
| where DataSource == "SCC_LOGS"
| project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Bname
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: Hostname
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIp
eventGroupingSettings:
aggregationKind: SingleAlert
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 5h
matchingMethod: AnyAlert
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.0
Stages and Predicates
Stage 1: source
Pathlock_TDnR_CL
Stage 2: where
| where DataSource == "SCC_LOGS"
Stage 3: project
| project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DataSource | eq |
| field:"DataSource" kind:eq value:"SCC_LOGS" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AffectedUser | project |
Area | project |
Bname | project |
CentralTs | project |
DataSource | project |
DestIp | project |
Eventid | project |
Hostname | project |
Instance | project |
LogLine | project |
MessageV1 | project |
MessageV2 | project |
MessageV3 | project |
MessageV4 | project |
MsgId | project |
MsgNo | project |
MsgType | project |
Report | project |
SrcIp | project |
Subid | project |
Sysid | project |
Tcode | project |
TimeGenerated | project |
Preview - TI map Email entity to Cloud App Events
#Identifies compromises and attacks and detect malicious activities in one's email entity from TI
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | CloudAppEvents action any: Cloud app activity |
Rule Body
id: 47b9bb10-d216-4359-8cef-08ca2c67e5be
name: Preview - TI map Email entity to Cloud App Events
description: |
'Identifies compromises and attacks and detect malicious activities in one's email entity from TI'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- CloudAppEvents
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1566
query: |
let dt_lookBack = 10d;
let ioc_lookBack = 30d;
let emailregex = @'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$';
ThreatIntelligenceIndicator
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now()
| where isnotempty(EmailSenderAddress)
| join kind=innerunique (CloudAppEvents
| extend User_Id = tostring(RawEventData.UserId)
| where isnotempty(User_Id)
| where TimeGenerated >= ago(dt_lookBack) and isnotempty(Application)
| extend CloudAppEvents_TimeGenerated = TimeGenerated
| where User_Id matches regex emailregex) on $left.EmailSenderAddress == $right.User_Id
| where CloudAppEvents_TimeGenerated < ExpirationDateTime
| summarize CloudAppEvents_TimeGenerated = argmax(CloudAppEvents_TimeGenerated, *) by IndicatorId, User_Id
| extend Name = tostring(split(User_Id, '@', 0)[0]), UPNSuffix = tostring(split(User_Id, '@', 1)[0])
| extend timestamp = CloudAppEvents_TimeGenerated
entityMappings:
- entityType: Account
fieldMappings:
- identifier: DisplayName
columnName: Name
- identifier: FullName
columnName: User_Id
- identifier: UPNSuffix
columnName: UPNSuffix
version: 1.0.3
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 10d;
let ioc_lookBack = 30d;
let emailregex = @'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$';
Stage 1: source
ThreatIntelligenceIndicator
Stage 2: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 3: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
Stage 4: where
| where Active == true and ExpirationDateTime > now()
Stage 5: where
| where isnotempty(EmailSenderAddress)
Stage 6: join
| join kind=innerunique (CloudAppEvents
| extend User_Id = tostring(RawEventData.UserId)
| where isnotempty(User_Id)
| where TimeGenerated >= ago(dt_lookBack) and isnotempty(Application)
| extend CloudAppEvents_TimeGenerated = TimeGenerated
| where User_Id matches regex emailregex) on $left.EmailSenderAddress == $right.User_Id
Stage 7: where
where CloudAppEvents_TimeGenerated < ExpirationDateTime
Stage 8: summarize
summarize CloudAppEvents_TimeGenerated by IndicatorId, User_Id
Stage 9: extend
extend Name, UPNSuffix
Stage 10: extend
extend timestamp
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Active | eq |
| field:"Active" kind:eq value:"true" |
Application | is_not_null | field:"Application" kind:is_not_null | |
EmailSenderAddress | is_not_null | field:"EmailSenderAddress" kind:is_not_null | |
User_Id | is_not_null | field:"User_Id" kind:is_not_null | |
User_Id | regex_match |
| field:"User_Id" kind:regex_match value:"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
CloudAppEvents_TimeGenerated | summarize |
IndicatorId | summarize |
User_Id | summarize |
Name | extend |
UPNSuffix | extend |
timestamp | extend |
Trusted Developer Utilities Proxy Execution
#This detection looks at process executions - in some cases with specific command line attributes to filter a lot of common noise.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Stealth |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceProcessEvents action any: Process activity |
Rule Body
id: 5c2bb446-926f-4160-a233-21e335c2c290
name: Trusted Developer Utilities Proxy Execution
description: |
This detection looks at process executions - in some cases with specific command line attributes to filter a lot of common noise.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceProcessEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1127
query: |
let Timeframe = 1h;
DeviceProcessEvents
| where Timestamp > ago(Timeframe)
| where (FileName has_any ("msbuild.exe", "msxsl.exe")
or (FileName has_any ("vbc.exe","csc.exe","jsc.exe") and ProcessCommandLine has_any ("/exe","/dll","/pe64","-exe","-dll","-pe64"))
or (FileName == ("ilsasm.exe") and ProcessCommandLine has_any ("out","target","t:","reference","r:")))
// Filter initations by Visual Studio since this is expected behavior.
and not(FolderPath startswith "C:\\Program Files (x86)\\Microsoft Visual Studio"
or InitiatingProcessFolderPath startswith "C:\\Program Files (x86)\\Microsoft Visual Studio"
and (InitiatingProcessFileName == "devenv.exe" or InitiatingProcessFileName == "WDExpress.exe"))
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- entityType: Account
fieldMappings:
- identifier: Sid
columnName: AccountSid
- identifier: Name
columnName: AccountName
- identifier: NTDomain
columnName: AccountDomain
- entityType: Process
fieldMappings:
- identifier: CommandLine
columnName: ProcessCommandLine
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let Timeframe = 1h;
Stage 1: source
DeviceProcessEvents
Stage 2: where
| where Timestamp > ago(Timeframe)
Stage 3: where
| where (FileName has_any ("msbuild.exe", "msxsl.exe")
or (FileName has_any ("vbc.exe","csc.exe","jsc.exe") and ProcessCommandLine has_any ("/exe","/dll","/pe64","-exe","-dll","-pe64"))
or (FileName == ("ilsasm.exe") and ProcessCommandLine has_any ("out","target","t:","reference","r:")))
and not(FolderPath startswith "C:\\Program Files (x86)\\Microsoft Visual Studio"
or InitiatingProcessFolderPath startswith "C:\\Program Files (x86)\\Microsoft Visual Studio"
and (InitiatingProcessFileName == "devenv.exe" or InitiatingProcessFileName == "WDExpress.exe"))
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
FileName | eq |
| field:"process_basename" kind:eq value:"ilsasm.exe" |
FileName | match |
| field:"process_basename" kind:match |
ProcessCommandLine | match |
| field:"CommandLine" kind:match |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
InitiatingProcessFileName | eq | WDExpress.exe | excludes:InitiatingProcessFileName field:"InitiatingProcessFileName" value:"WDExpress.exe" |
InitiatingProcessFileName | eq | devenv.exe | excludes:InitiatingProcessFileName field:"InitiatingProcessFileName" value:"devenv.exe" |
InitiatingProcessFolderPath | starts_with | C:\Program Files (x86)\Microsoft Visual Studio | excludes:InitiatingProcessFolderPath field:"InitiatingProcessFolderPath" value:"C:\Program Files (x86)\Microsoft Visual Studio" |
FolderPath | starts_with | C:\Program Files (x86)\Microsoft Visual Studio | excludes:FolderPath field:"FolderPath" value:"C:\Program Files (x86)\Microsoft Visual Studio" |
Uniqkey - Sign-in from unfamiliar IP address
#Identifies successful Uniqkey sign-ins (browser extension, mobile, web or desktop) originating from an IP address that has not been observed for that user during the trailing 14 days. A new source address can indicate session hijacking or use of stolen master credentials, particularly when combined with an unusual client system. Uniqkey audits successful authentications only, so pair this rule with identity-provider telemetry if failed sign-in coverage is required.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 6909c70c-fb52-47a3-9eb9-3b8109b0b32a
name: Uniqkey - Sign-in from unfamiliar IP address
version: 1.0.0
kind: Scheduled
description: |-
Identifies successful Uniqkey sign-ins (browser extension, mobile, web or desktop) originating from an IP address that has not been observed for that user during the trailing 14 days. A new source address can indicate session hijacking or use of stolen master credentials, particularly when combined with an unusual client system. Uniqkey audits successful authentications only, so pair this rule with identity-provider telemetry if failed sign-in coverage is required.
severity: Low
requiredDataConnectors:
- connectorId: UniqkeyEventsConnector
dataTypes:
- UniqkeyEvents_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |-
let lookback = 14d;
let frequency = 1h;
let knownPairs =
UniqkeyEvents_CL
| where TimeGenerated between (ago(lookback) .. ago(frequency))
| where Category == "authentication" and isnotempty(SrcIpAddr) and isnotempty(ActorEmail)
| summarize by ActorEmail, SrcIpAddr;
UniqkeyEvents_CL
| where TimeGenerated > ago(frequency)
| where Category == "authentication" and isnotempty(SrcIpAddr) and isnotempty(ActorEmail)
| join kind=leftanti knownPairs on ActorEmail, SrcIpAddr
| summarize arg_max(TimeGenerated, Action, ActionId, ActorType, ClientSystem) by ActorEmail, SrcIpAddr
| project TimeGenerated, ActorEmail, ActorType, Action, ActionId, ClientSystem, SrcIpAddr
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: ActorEmail
- 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 lookback = 14d;
let frequency = 1h;
Let Binding: knownPairs
let knownPairs = UniqkeyEvents_CL
| where TimeGenerated between (ago(lookback) .. ago(frequency))
| where Category == "authentication" and isnotempty(SrcIpAddr) and isnotempty(ActorEmail)
| summarize by ActorEmail, SrcIpAddr;
Stage 1: source
UniqkeyEvents_CL
Stage 2: where
| where TimeGenerated > ago(frequency)
Stage 3: where
| where Category == "authentication" and isnotempty(SrcIpAddr) and isnotempty(ActorEmail)
Stage 4: join (negated)
join kind=leftanti (knownPairs) on ActorEmail, SrcIpAddr
Stage 5: summarize
summarize by ActorEmail, SrcIpAddr
Stage 6: project
project Action, ActionId, ActorEmail, ActorType, ClientSystem, SrcIpAddr, TimeGenerated
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActorEmail | is_not_null | field:"ActorEmail" kind:is_not_null | |
Category | eq |
| field:"Category" kind:eq value:"authentication" |
SrcIpAddr | is_not_null | field:"SrcIpAddr" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
ActorEmail | is_not_null | excludes:ActorEmail | |
Category | eq | authentication | excludes:Category field:"Category" value:"authentication" |
SrcIpAddr | is_not_null | excludes:SrcIpAddr |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Action | project |
ActionId | project |
ActorEmail | project |
ActorType | project |
ClientSystem | project |
SrcIpAddr | project |
TimeGenerated | project |
Vectra Create Incident Based on Priority for Hosts
#Create an incident when an identity is suspected to be compromised. Vectra is using AI to prioritize an entity based on multiple factors (attack rating, velocity, breadth, importance.etc.). This layer of aggregation at the entity level provides a greater signal-to-noise ratio and help analyst focus on what matters.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Rule Body
id: 9b51b0fb-0419-4450-9ea0-0a48751c4902
name: Vectra Create Incident Based on Priority for Hosts
version: 1.1.1
kind: Scheduled
description: Create an incident when an identity is suspected to be compromised. Vectra is using AI to prioritize an entity based on multiple factors (attack rating, velocity, breadth, importance.etc.). This layer of aggregation at the entity level provides a greater signal-to-noise ratio and help analyst focus on what matters.
status: Available
requiredDataConnectors:
- connectorId: VectraXDR
dataTypes:
- Entities_Data_CL
severity: Medium
queryFrequency: 10m
queryPeriod: 10m
suppressionDuration: PT1H
suppressionEnabled: false
triggerOperator: GreaterThan
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1546
query: |
Entities_Data_CL
| where entity_type == "host" and is_prioritized == true
| summarize arg_max(['last_modified_timestamp'], *) by ['name']
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: name
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: true
lookbackDuration: P7D
matchingMethod: AllEntities
customDetails:
entity_id: id
tags: tags
entity_type: entity_type
attack_profile: attack_profile
ip_address: ip
alertDetailsOverride:
alertDisplayNameFormat: "Vectra AI Incident- {{name}}"
alertDescriptionFormat: An incident has been generated for Vectra AI entity {{name}} that is presenting an urgency score of {{urgency_score}}.
alertDynamicProperties:
- alertProperty: AlertLink
value: url
eventGroupingSettings:
aggregationKind: AlertPerResult
Stages and Predicates
Stage 1: source
Entities_Data_CL
Stage 2: where
| where entity_type == "host" and is_prioritized == true
Stage 3: summarize
| summarize arg_max(['last_modified_timestamp'], *) by ['name']
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
entity_type | eq |
| field:"entity_type" kind:eq value:"host" |
is_prioritized | eq |
| field:"is_prioritized" kind:eq value:"true" |