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.

Status
available
Severity
low
Time window
30m
Source
github.com/Azure/Azure-Sentinel

The rule detects internal admins accounts, it's recommended to review any new administrative permission.

MITRE ATT&CK Coverage

TacticTechniques
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.

FieldKindValuesSearch
Policymatch
  • Admin SaaS account detected transforms: term
field:"policy_name_s" kind:match value:"Admin SaaS account detected"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Categoryproject
Descriptionproject
EventIDproject
Policyproject
Recommendationproject
Severityproject
Tacticsproject
URLproject

AIShield - Natural language processing AI model extraction high suspicious vulnerability detection

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

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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
HostNameextend
NTDomainextend

API - Rate limiting

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

'42Crunch API protection against rate limiting'

MITRE ATT&CK Coverage

TacticTechniques
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.

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.

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

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.

FieldKindValuesSearch
Findingeq
  • T0AddSelf
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.

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

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.

FieldKindValuesSearch
Findingeq
  • LargeDefaultGroupsWriteAccountRestrictions
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.

Status
available
Severity
high
Source
github.com/Azure/Azure-Sentinel

Creates Microsoft Sentinel incidents from Thinkst Canary alerts.

MITRE ATT&CK Coverage

TacticTechniques
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.

FieldKindValuesSearch
Descriptionne
  • Canary Disconnected
  • Canary Reconnected
  • Canary Settings Changed
  • Fake Location
  • Network Settings Roll-back
field:"Description" kind:ne

Output Fields

These fields are emitted when the rule matches.

FieldSource
Activityproject
ActorDomainproject
ActorHostproject
ActorUsernameproject
AssetIPproject
AssetLocationproject
AssetNameproject
AssetNodeproject
AssetTypeproject
CanaryEntityIPproject
CanaryEntityNameproject
Descriptionproject
DestinationPortproject
EventsCountproject
FlockNameproject
IncidentIdproject
LogTypeproject
SourceDisplayproject
SourceGeoproject
SourceIPproject
SrcHostReverseproject
TimeGeneratedproject
TokenHostnameproject
TokenMemoproject
TokenTargetproject
TokenTypeproject
UserAgentproject

Contrast Suspicious

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

'Creates Incidents for Suspicious events sourced from the Contrast Protect agent.'

MITRE ATT&CK Coverage

TacticTechniques
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 used in Stage 4

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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DeviceProductextend
SourceIPextend
DeviceVersionextend
Activityextend
ApplicationProtocolextend
RequestURLextend
RequestMethodextend
Ruleextend

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.

Status
available
Severity
low
Time window
30m
Source
github.com/Azure/Azure-Sentinel

'Detects leaked credentials identified by CybleVision ingestion and triggers an incident with mapped entities, severity, and details.'

MITRE ATT&CK Coverage

TacticTechniques
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.

FieldKindValuesSearch
Serviceeq
  • leaked_credentials
field:"Service" kind:eq value:"leaked_credentials"

Output Fields

These fields are emitted when the rule matches.

FieldSource
MappedSeverityextend

GreyNoise TI map IP entity to Network Session Events (ASIM Network Session schema)

#
Status
available
Severity
medium
Time window
14d
Group by
$left.TI_ipEntity, $right.IoCIP, IndicatorId
Source
github.com/Azure/Azure-Sentinel

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

TacticTechniques
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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActivityGroupNamesproject
ConfidenceScoreproject
Descriptionproject
DstIpAddrproject
Dvcproject
EventProductproject
EventVendorproject
ExpirationDateTimeproject
IndicatorIdproject
IoCDirectionproject
IoCIPproject
SrcIpAddrproject
ThreatTypeproject
imNWS_maxtimeproject
imNWS_mintimeproject

Infoblox - Many High Threat Level Single Query Detected

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

'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

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.

FieldSource
DestinationDnsDomainsummarize

Insider Risk_Sensitive Data Access Outside Organizational Geo-location

#
Severity
high
Time window
6h
Group by
Activity_s, City, Country_Region, LabelName_s, State, TimeGenerated, UserPrincipalName
Source
github.com/Azure/Azure-Sentinel

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

TacticTechniques
Exfiltration

Telemetry Coverage

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.

FieldSource
Activity_ssummarize
Citysummarize
Country_Regionsummarize
LabelName_ssummarize
Statesummarize
TimeGeneratedsummarize
UserPrincipalNamesummarize
AccountNameextend
AccountUPNSuffixextend

Microsoft Entra ID PowerShell accessing non-Entra ID resources

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

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

TacticTechniques
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

#
Status
available
Severity
medium
Time window
7d
Group by
AppDisplayName, AppId, SimpleUserAgent, UserAgent, UserAgentType, UserPrincipalName
Source
github.com/Azure/Azure-Sentinel

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

TacticTechniques
Stealth

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

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 used in Stage 11

let BaselineUserAgents = materialize(
    QueryUserAgents(lookback_timeframe+timeframe, timeframe)
    | summarize RequestCount=count() by AppId, AppDisplayName, SimpleUserAgent
);

Let Binding: BaselineSummarizedAgents used in Stage 12

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.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
UserAgentis_null(no value, null check)excludes:UserAgent
AppIdis_null(no value, null check)excludes:AppId

Output Fields

These fields are emitted when the rule matches.

FieldSource
AppDisplayNamesummarize
AppIdsummarize
SimpleUserAgentsummarize
UserAgentsummarize
UserAgentTypesummarize
UserPrincipalNamesummarize

Pathlock TDnR - SAP Cloud Connector Events

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

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

TacticTechniques
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.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AffectedUserproject
Areaproject
Bnameproject
CentralTsproject
DataSourceproject
DestIpproject
Eventidproject
Hostnameproject
Instanceproject
LogLineproject
MessageV1project
MessageV2project
MessageV3project
MessageV4project
MsgIdproject
MsgNoproject
MsgTypeproject
Reportproject
SrcIpproject
Subidproject
Sysidproject
Tcodeproject
TimeGeneratedproject

Preview - TI map Email entity to Cloud App Events

#
Severity
medium
Time window
14d
Group by
$left.EmailSenderAddress, $right.User_Id, IndicatorId, User_Id
Source
github.com/Azure/Azure-Sentinel

Identifies compromises and attacks and detect malicious activities in one's email entity from TI

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Telemetry Coverage

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.

FieldKindValuesSearch
Activeeq
  • true corpus 60 (kusto 60)
field:"Active" kind:eq value:"true"
Applicationis_not_null
  • (no value, null check)
field:"Application" kind:is_not_null
EmailSenderAddressis_not_null
  • (no value, null check)
field:"EmailSenderAddress" kind:is_not_null
User_Idis_not_null
  • (no value, null check)
field:"User_Id" kind:is_not_null
User_Idregex_match
  • ^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$
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.

FieldSource
CloudAppEvents_TimeGeneratedsummarize
IndicatorIdsummarize
User_Idsummarize
Nameextend
UPNSuffixextend
timestampextend

Trusted Developer Utilities Proxy Execution

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

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

Telemetry Coverage

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.

FieldKindValuesSearch
FileNameeq
  • ilsasm.exe
field:"process_basename" kind:eq value:"ilsasm.exe"
FileNamematch
  • csc.exe transforms: term
  • jsc.exe transforms: term
  • msbuild.exe transforms: term corpus 2 (kusto 2)
  • msxsl.exe transforms: term
  • vbc.exe transforms: term
field:"process_basename" kind:match
ProcessCommandLinematch
  • -dll transforms: term
  • -exe transforms: term
  • -pe64 transforms: term
  • /dll transforms: term
  • /exe transforms: term
  • /pe64 transforms: term
  • out transforms: term
  • r: transforms: term
  • reference transforms: term
  • t: transforms: term
  • target transforms: term
field:"CommandLine" kind:match

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
InitiatingProcessFileNameeqWDExpress.exeexcludes:InitiatingProcessFileName field:"InitiatingProcessFileName" value:"WDExpress.exe"
InitiatingProcessFileNameeqdevenv.exeexcludes:InitiatingProcessFileName field:"InitiatingProcessFileName" value:"devenv.exe"
InitiatingProcessFolderPathstarts_withC:\Program Files (x86)\Microsoft Visual Studioexcludes:InitiatingProcessFolderPath field:"InitiatingProcessFolderPath" value:"C:\Program Files (x86)\Microsoft Visual Studio"
FolderPathstarts_withC:\Program Files (x86)\Microsoft Visual Studioexcludes:FolderPath field:"FolderPath" value:"C:\Program Files (x86)\Microsoft Visual Studio"

Uniqkey - Sign-in from unfamiliar IP address

#
Severity
low
Time window
14d
Group by
ActorEmail, SrcIpAddr
Source
github.com/Azure/Azure-Sentinel

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

TacticTechniques
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 used in Stage 4

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.

FieldKindValuesSearch
ActorEmailis_not_null
  • (no value, null check)
field:"ActorEmail" kind:is_not_null
Categoryeq
  • authentication
field:"Category" kind:eq value:"authentication"
SrcIpAddris_not_null
  • (no value, null check)
field:"SrcIpAddr" kind:is_not_null

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
ActorEmailis_not_null(no value, null check)excludes:ActorEmail
Categoryeqauthenticationexcludes:Category field:"Category" value:"authentication"
SrcIpAddris_not_null(no value, null check)excludes:SrcIpAddr

Output Fields

These fields are emitted when the rule matches.

FieldSource
Actionproject
ActionIdproject
ActorEmailproject
ActorTypeproject
ClientSystemproject
SrcIpAddrproject
TimeGeneratedproject

Vectra Create Incident Based on Priority for Hosts

#
Status
available
Severity
medium
Time window
10m
Source
github.com/Azure/Azure-Sentinel

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

TacticTechniques
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.

Back to all Kusto rules