Detection rules › Kusto

Kusto rules: 12

AppServices AV Scan Failure

#
Severity
informational
Time window
1d
Author
SecurityJedi
Source
github.com/Azure/Azure-Sentinel

'Identifies if an AV scan fails in Azure App Services.'

Rule Body

id: c2da1106-bfe4-4a63-bf14-5ab73130ccd5
name: AppServices AV Scan Failure
description: |
  'Identifies if an AV scan fails in Azure App Services.'
severity: Informational
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 1
query: |
  let timeframe = ago(1d);
  AppServiceAntivirusScanAuditLogs
  | where ScanStatus == "Failed"
  | extend timestamp = TimeGenerated
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: AzureID
        columnName: _ResourceId
version: 1.0.3
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: SecurityJedi
    support:
        tier: Community
    categories:
        domains: [ "Security - Others", "Platform" ]

Stages and Predicates

Parameters

let timeframe = ago(1d);

Stage 1: source

AppServiceAntivirusScanAuditLogs

Stage 2: where

| where ScanStatus == "Failed"

Stage 3: extend

| extend timestamp = TimeGenerated

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
timestampextend

Azure DevOps Pipeline Created and Deleted on the Same Day

#
Status
available
Severity
medium
Time window
3d
Group by
PipelineId, timekey
Source
github.com/Azure/Azure-Sentinel

'An attacker with access to Azure DevOps could create a pipeline to inject artifacts used by other pipelines, or to create a malicious software build that looks legitimate by using a pipeline that incorporates legitimate elements. An attacker would also likely want to cover their tracks once conducting such activity. This query looks for Pipelines created and deleted within the same day, this is unlikely to be legitimate user activity in the majority of cases.'

MITRE ATT&CK Coverage

TacticTechniques
Execution

Rule Body

id: 17f23fbe-bb73-4324-8ecf-a18545a5dc26
name: Azure DevOps Pipeline Created and Deleted on the Same Day
description: |
  'An attacker with access to Azure DevOps could create a pipeline to inject artifacts used by other pipelines, or to create a malicious software build that looks legitimate by using a pipeline that incorporates legitimate elements. 
  An attacker would also likely want to cover their tracks once conducting such activity. This query looks for Pipelines created and deleted within the same day, this is unlikely to be legitimate user activity in the majority of cases.'
severity: Medium
status: Available
requiredDataConnectors: []
queryFrequency: 3d
queryPeriod: 3d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
relevantTechniques:
  - T1072
query: |
  let timeframe = 3d;
  // Get Release Pipeline Creation Events and group by day
  ADOAuditLogs
  | where TimeGenerated > ago(timeframe)
  | where OperationName =~ "Release.ReleasePipelineCreated"
  // Group by day
  | extend timekey = bin(TimeGenerated, 1d)
  | extend PipelineId = tostring(Data.PipelineId)
  | extend PipelineName = tostring(Data.PipelineName)
  // Rename some columns to make output clearer
  | project-rename TimeCreated = TimeGenerated, CreatingUser = ActorUPN, CreatingUserAgent = UserAgent, CreatingIP = IpAddress
  // Join with Release Pipeline Deletions where Pipeline ID is the same and deletion occurred on same day as creation
  | join (ADOAuditLogs
  | where TimeGenerated > ago(timeframe)
  | where OperationName =~ "Release.ReleasePipelineDeleted"
  // Group by day
  | extend timekey = bin(TimeGenerated, 1d)
  | extend PipelineId = tostring(Data.PipelineId)
  | extend PipelineName = tostring(Data.PipelineName)
  // Rename some things to make the output clearer
  | project-rename TimeDeleted = TimeGenerated,DeletingUser = ActorUPN, DeletingUserAgent = UserAgent, DeletingIP = IpAddress) on PipelineId, timekey
  | project TimeCreated, TimeDeleted, PipelineName, PipelineId, CreatingUser, CreatingIP, CreatingUserAgent, DeletingUser, DeletingIP, DeletingUserAgent, ScopeDisplayName, ProjectName, Data, OperationName, OperationName1
  | extend timestamp = TimeCreated
  | extend CreatingUserAccountName = tostring(split(CreatingUser, "@")[0]), CreatingUserAccountUPNSuffix = tostring(split(CreatingUser, "@")[1])
  | extend DeletingUserAccountName = tostring(split(DeletingUser, "@")[0]), DeletingUserAccountUPNSuffix = tostring(split(DeletingUser, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: CreatingUser
      - identifier: Name
        columnName: CreatingUserAccountName
      - identifier: UPNSuffix
        columnName: CreatingUserAccountUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: DeletingUser
      - identifier: Name
        columnName: DeletingUserAccountName
      - identifier: UPNSuffix
        columnName: DeletingUserAccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: CreatingIP
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: DeletingIP
version: 1.0.4
kind: Scheduled

Stages and Predicates

Parameters

let timeframe = 3d;

Stage 1: source

ADOAuditLogs

Stage 2: where

| where TimeGenerated > ago(timeframe)

Stage 3: where

| where OperationName =~ "Release.ReleasePipelineCreated"

Stage 4: extend (3 consecutive steps)

| extend timekey = bin(TimeGenerated, 1d)
| extend PipelineId = tostring(Data.PipelineId)
| extend PipelineName = tostring(Data.PipelineName)

Stage 5: project-rename

| project-rename TimeCreated = TimeGenerated, CreatingUser = ActorUPN, CreatingUserAgent = UserAgent, CreatingIP = IpAddress

Stage 6: join

| join (ADOAuditLogs
| where TimeGenerated > ago(timeframe)
| where OperationName =~ "Release.ReleasePipelineDeleted"
| extend timekey = bin(TimeGenerated, 1d)
| extend PipelineId = tostring(Data.PipelineId)
| extend PipelineName = tostring(Data.PipelineName)
| project-rename TimeDeleted = TimeGenerated,DeletingUser = ActorUPN, DeletingUserAgent = UserAgent, DeletingIP = IpAddress) on PipelineId, timekey

Stage 7: project

project CreatingIP, CreatingUser, CreatingUserAgent, Data, DeletingIP, DeletingUser, DeletingUserAgent, OperationName, OperationName1, PipelineId, PipelineName, ProjectName, ScopeDisplayName, TimeCreated, TimeDeleted

Stage 8: extend (3 consecutive steps)

extend CreatingUserAccountName, CreatingUserAccountUPNSuffix, DeletingUserAccountName, DeletingUserAccountUPNSuffix, timestamp

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
OperationNameeq
  • Release.ReleasePipelineCreated
  • Release.ReleasePipelineDeleted
field:"OperationName" kind:eq

Output Fields

These fields are emitted when the rule matches.

FieldSource
CreatingIPproject
CreatingUserproject
CreatingUserAgentproject
Dataproject
DeletingIPproject
DeletingUserproject
DeletingUserAgentproject
OperationNameproject
OperationName1project
PipelineIdproject
PipelineNameproject
ProjectNameproject
ScopeDisplayNameproject
TimeCreatedproject
TimeDeletedproject
timestampextend
CreatingUserAccountNameextend
CreatingUserAccountUPNSuffixextend
DeletingUserAccountNameextend
DeletingUserAccountUPNSuffixextend

Bitglass - Impossible travel distance

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

'Detects logins from different geo locations.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: cdb6e4a4-b9bd-4c30-94b9-ecce5a72d528
name: Bitglass - Impossible travel distance
description: |
  'Detects logins from different geo locations.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Bitglass
    dataTypes:
      - Bitglass
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |
  Bitglass
  | where EventType =~ 'access'
  | where EventResultDetails startswith 'Simultaneous logins from geographically distant locations'
  | extend AccountCustomEntity = User
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

Bitglass

Stage 2: where

| where EventType =~ 'access'

Stage 3: where

| where EventResultDetails startswith 'Simultaneous logins from geographically distant locations'

Stage 4: extend

| extend AccountCustomEntity = User

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend

BloodHound Attack Path Finding - Cloud App Admin Over Tier Zero Principal

#

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 - Cloud App Admin Over Tier Zero Principal

Rule Body

id: 39984923-f74a-45ea-b321-b1dd45ba04e2
name: BloodHound Attack Path Finding - Cloud App Admin Over Tier Zero Principal
description: BloodHound Attack Path Finding - Cloud App Admin Over Tier Zero Principal
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 == "AzureT0CloudAppAdmin"
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 == "AzureT0CloudAppAdmin"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • AzureT0CloudAppAdmin
field:"Finding" kind:eq value:"AzureT0CloudAppAdmin"

BloodHound Attack Path Finding - Kerberoastable User Accounts

#

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 - Kerberoastable User Accounts

Rule Body

id: 008a74e7-ad90-4855-87e6-6bdb86eed127
name: BloodHound Attack Path Finding - Kerberoastable User Accounts
description: BloodHound Attack Path Finding - Kerberoastable User Accounts
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 == "Kerberoasting"
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 == "Kerberoasting"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • Kerberoasting
field:"Finding" kind:eq value:"Kerberoasting"

BloodHound Attack Path Finding - Large Default Groups in Local Administrator 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 - Large Default Groups in Local Administrator Groups

Rule Body

id: 141b9381-50fe-4beb-a91a-799da4f466fc
name: BloodHound Attack Path Finding - Large Default Groups in Local Administrator
  Groups
description: BloodHound Attack Path Finding - Large Default Groups in Local Administrator
  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 == "LargeDefaultGroupsAdmins"
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 == "LargeDefaultGroupsAdmins"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • LargeDefaultGroupsAdmins
field:"Finding" kind:eq value:"LargeDefaultGroupsAdmins"

BloodHound Attack Path Finding - Large Default Groups With WriteGpLink Privilege

#

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 WriteGpLink Privilege

Rule Body

id: 660af442-45ec-4321-b6ce-ee39677f0472
name: BloodHound Attack Path Finding - Large Default Groups With WriteGpLink Privilege
description: BloodHound Attack Path Finding - Large Default Groups With WriteGpLink
  Privilege
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 == "LargeDefaultGroupsWriteGPLink"
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 == "LargeDefaultGroupsWriteGPLink"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • LargeDefaultGroupsWriteGPLink
field:"Finding" kind:eq value:"LargeDefaultGroupsWriteGPLink"

CYFIRMA - Brand Intelligence - Malicious Mobile App Medium Rule

#

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
5m
Source
github.com/Azure/Azure-Sentinel

"This analytic rule detects instances where malicious or unauthorized mobile applications are discovered mimicking legitimate brand assets. Such impersonations may be distributed through unofficial app stores or third-party websites, potentially deceiving customers, harvesting sensitive data, or damaging brand reputation. This alert is triggered when CYFIRMA threat intelligence identifies a suspicious mobile app associated with the organization's brand or product names."

MITRE ATT&CK Coverage

Rule Body

id: b73e6628-d44c-4ad3-a801-ea225c5744ee
name: CYFIRMA - Brand Intelligence - Malicious Mobile App Medium Rule
description: |
  "This analytic rule detects instances where malicious or unauthorized mobile applications are discovered mimicking legitimate brand assets. 
  Such impersonations may be distributed through unofficial app stores or third-party websites, potentially deceiving customers, harvesting sensitive data, or damaging brand reputation. 
  This alert is triggered when CYFIRMA threat intelligence identifies a suspicious mobile app associated with the organization's brand or product names."
version: 1.0.1
kind: Scheduled
severity: Medium
requiredDataConnectors:
  - connectorId: CyfirmaBrandIntelligenceAlertsDC
    dataTypes:
      - CyfirmaBIMaliciousMobileAppsAlerts_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - ResourceDevelopment
  - Execution
  - DefenseEvasion
  - CredentialAccess
  - CommandAndControl
relevantTechniques:
  - T1406
  - T1414
  - T1437
  - T1583.001
  - T1204.002
query: |
  // Medium severity - Malicious Mobile App Impersonation
  let timeFrame = 5m;
  CyfirmaBIMaliciousMobileAppsAlerts_CL
  | where severity == 'High' and TimeGenerated between (ago(timeFrame) .. now())
  | extend
      Description=description,
      FirstSeen=first_seen,
      LastSeen=last_seen,
      RiskScore=risk_score,
      AlertUID=alert_uid,
      UID=uid,
      AssetType=asset_type,
      AssetValue=asset_value,
      Impact=impact,
      Recommendation=recommendation,
      ProviderName='CYFIRMA',
      ProductName='DeCYFIR/DeTCT'
  | project
      TimeGenerated,
      Description,
      RiskScore,
      FirstSeen,
      LastSeen,
      AlertUID,
      UID,
      AssetType,
      AssetValue,
      Impact,
      Recommendation,
      ProductName,
      ProviderName
customDetails:
  AssetType: AssetType
  AssetValue: AssetValue
  Impact: Impact
  Recommendation: Recommendation
  Description: Description
  RiskScore: RiskScore
  TimeGenerated: TimeGenerated
  FirstSeen: FirstSeen
  LastSeen: LastSeen
  AlertUID: AlertUID
  UID: UID
alertDetailsOverride:
  alertDisplayNameFormat: "CYFIRMA - Medium Severity Alert: Malicious Mobile App Impersonating Brand Detected - {{AssetType}} - {{AssetValue}} "
  alertDescriptionFormat: "{{Description}} "
  alertDynamicProperties:
    - alertProperty: ProductName
      value: ProductName
    - alertProperty: ProviderName
      value: ProviderName
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult

Stages and Predicates

Parameters

let timeFrame = 5m;

Stage 1: source

CyfirmaBIMaliciousMobileAppsAlerts_CL

Stage 2: where

| where severity == 'High' and TimeGenerated between (ago(timeFrame) .. now())

Stage 3: extend

| extend
    Description=description,
    FirstSeen=first_seen,
    LastSeen=last_seen,
    RiskScore=risk_score,
    AlertUID=alert_uid,
    UID=uid,
    AssetType=asset_type,
    AssetValue=asset_value,
    Impact=impact,
    Recommendation=recommendation,
    ProviderName='CYFIRMA',
    ProductName='DeCYFIR/DeTCT'

Stage 4: project

| project
    TimeGenerated,
    Description,
    RiskScore,
    FirstSeen,
    LastSeen,
    AlertUID,
    UID,
    AssetType,
    AssetValue,
    Impact,
    Recommendation,
    ProductName,
    ProviderName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AlertUIDproject
AssetTypeproject
AssetValueproject
Descriptionproject
FirstSeenproject
Impactproject
LastSeenproject
ProductNameproject
ProviderNameproject
Recommendationproject
RiskScoreproject
TimeGeneratedproject
UIDproject

Dataverse - Executable uploaded to SharePoint document management site

#
Status
available
Severity
low
Time window
14d
Group by
$left.SharePointUrl, $right.Site_Url
Source
github.com/Azure/Azure-Sentinel

Identifies executable files and scripts uploaded to SharePoint sites used for Dynamics document management, circumventing native file extension restrictions in Dataverse.

MITRE ATT&CK Coverage

TacticTechniques
Execution
Persistence

Telemetry Coverage

PlatformRecord / event type
Microsoft 365FileUploaded

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: ba5e608f-7879-4927-8b0d-a9948b4fe6f3
kind: Scheduled
name: Dataverse - Executable uploaded to SharePoint document management site
description: Identifies executable files and scripts uploaded to SharePoint sites
  used for Dynamics document management, circumventing native file extension restrictions
  in Dataverse.
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: Office365
    dataTypes:
      - OfficeActivity (SharePoint)
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
  - Persistence
relevantTechniques:
  - T0863
  - T0873
query: |
  let file_extensions = dynamic(['com', 'exe', 'bat', 'cmd', 'vbs', 'vbe', 'js', 'jse', 'wsf', 'wsh', 'msc', 'cpl', 'ps1', 'scr']);
  let query_frequency = 1h;
  DataverseSharePointSites
  | join kind=inner (
      OfficeActivity
      | where TimeGenerated >= ago(query_frequency)
      | where OfficeWorkload == "SharePoint" and Operation == "FileUploaded")
      on $left.SharePointUrl == $right.Site_Url
  | where SourceFileExtension in (file_extensions)
  | extend
      CloudAppId = int(32780),
      SharePointId = int(20892),
      AccountName = tostring(split(UserId, '@')[0]),
      UPNSuffix = tostring(split(UserId, '@')[1])
  | project
      TimeGenerated,
      UserId,
      ClientIP,
      InstanceUrl,
      SourceFileName,
      SharePointUrl,
      CloudAppId,
      SharePointId,
      AccountName,
      UPNSuffix
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIP
  - entityType: CloudApplication
    fieldMappings:
      - identifier: AppId
        columnName: CloudAppId
      - identifier: InstanceName
        columnName: InstanceUrl
  - entityType: File
    fieldMappings:
      - identifier: Name
        columnName: SourceFileName
  - entityType: CloudApplication
    fieldMappings:
      - identifier: AppId
        columnName: SharePointId
      - identifier: InstanceName
        columnName: SharePointUrl
alertDetailsOverride:
  alertDisplayNameFormat: Dataverse - Executable files uploaded in document management
    for {{InstanceUrl}}
  alertDescriptionFormat: Executable/script {{SourceFileName}} was uploaded by {{UserId}}
    in SharePoint site {{SharePointUrl}}
version: 3.2.0

Stages and Predicates

Parameters

let file_extensions = dynamic(['com', 'exe', 'bat', 'cmd', 'vbs', 'vbe', 'js', 'jse', 'wsf', 'wsh', 'msc', 'cpl', 'ps1', 'scr']);
let query_frequency = 1h;

Stage 1: source

DataverseSharePointSites

Stage 2: join

| join kind=inner (
    OfficeActivity
    | where TimeGenerated >= ago(query_frequency)
    | where OfficeWorkload == "SharePoint" and Operation == "FileUploaded")
    on $left.SharePointUrl == $right.Site_Url

Stage 3: where

where SourceFileExtension in ("bat", "cmd", "com", "cpl", "exe", "js", "jse", "msc", "ps1", "scr", "vbe", "vbs", "wsf", "wsh")

Stage 4: extend

extend AccountName, CloudAppId, SharePointId, UPNSuffix

Stage 5: project

project AccountName, ClientIP, CloudAppId, InstanceUrl, SharePointId, SharePointUrl, SourceFileName, TimeGenerated, UPNSuffix, UserId

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNameproject
ClientIPproject
CloudAppIdproject
InstanceUrlproject
SharePointIdproject
SharePointUrlproject
SourceFileNameproject
TimeGeneratedproject
UPNSuffixproject
UserIdproject

Dataverse - TI map IP to DataverseActivity

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

Identifies a match in DataverseActivity from any IP IOC from Microsoft Sentinel Threat Intelligence.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: 56d5aa0c-d871-4167-ba13-61c2f0fd17bf
kind: Scheduled
name: Dataverse - TI map IP to DataverseActivity
description: Identifies a match in DataverseActivity from any IP IOC from Microsoft
  Sentinel Threat Intelligence.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Dataverse
    dataTypes:
      - DataverseActivity
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - LateralMovement
  - Discovery
relevantTechniques:
  - T1078
  - T1199
  - T1133
  - T0886
  - T0859
  - T1428
  - T1021
  - T1210
  - T1526
  - T1580
query: |
  let dt_lookBack = 1h;
  let ioc_lookBack = 14d;
  ThreatIntelligenceIndicator
  | where TimeGenerated >= ago(ioc_lookBack) and ExpirationDateTime > now()
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
  | where Active == true
  // Picking up only IOC's that contain the entities we want
  | where isnotempty(NetworkIP)
      or isnotempty(EmailSourceIpAddress)
      or isnotempty(NetworkDestinationIP)
      or isnotempty(NetworkSourceIP)
  // As there is potentially more than 1 indicator type for matching IP, taking NetworkIP first, then others if that is empty.
  // Taking the first non-empty value based on potential IOC match availability
  | extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
  | extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
  | extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
  //Exclude local addresses, using the ipv4_is_private operator
  | where ipv4_is_private(TI_ipEntity) == false
      and TI_ipEntity !startswith "fe80"
      and TI_ipEntity !startswith "::"
      and TI_ipEntity !startswith "127."
  // 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 (
      DataverseActivity
      | where TimeGenerated >= ago(dt_lookBack)
      | where isnotempty(ClientIp)
      //Exclude local addresses, using the ipv4_is_private operator
      | where ipv4_is_private(ClientIp) == false
          and ClientIp !startswith "fe80"
          and ClientIp !startswith "::"
          and ClientIp !startswith "127."
      // renaming time column so it is clear the log this came from
      | extend DataverseActivity_TimeGenerated = TimeGenerated
      )
      on $left.TI_ipEntity == $right.ClientIp
  | where DataverseActivity_TimeGenerated < ExpirationDateTime
  | summarize DataverseActivity_TimeGenerated = arg_max(DataverseActivity_TimeGenerated, *) by IndicatorId, ClientIp
  | project DataverseActivity_TimeGenerated, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore,
      TI_ipEntity, ClientIp, NetworkIP, NetworkDestinationIP, NetworkSourceIP, EmailSourceIpAddress, InstanceUrl, UserId
  | extend
      timestamp = DataverseActivity_TimeGenerated,
      AccountName = tostring(split(UserId, '@')[0]),
      UPNSuffix = tostring(split(UserId, '@')[0]),
      CloudAppId = int(32780)
eventGroupingSettings:
  aggregationKind: AlertPerResult
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIp
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
  - entityType: CloudApplication
    fieldMappings:
      - identifier: AppId
        columnName: CloudAppId
      - identifier: InstanceName
        columnName: InstanceUrl
alertDetailsOverride:
  alertDisplayNameFormat: 'Dataverse - TI map IP in {{InstanceUrl}} '
  alertDescriptionFormat: Malicous IP {{ClientIp}} was found in {{InstanceUrl}} .
    User affected is {{UserId}}
version: 3.2.0

Stages and Predicates

Parameters

let dt_lookBack = 1h;
let ioc_lookBack = 14d;

Stage 1: source

ThreatIntelligenceIndicator

Stage 2: where

| where TimeGenerated >= ago(ioc_lookBack) and ExpirationDateTime > now()

Stage 3: summarize

| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId

Stage 4: where

| where Active == true

Stage 5: where

| where isnotempty(NetworkIP)
    or isnotempty(EmailSourceIpAddress)
    or isnotempty(NetworkDestinationIP)
    or isnotempty(NetworkSourceIP)

Stage 6: extend (3 consecutive steps)

| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
TI_ipEntity =
ifisnotempty(NetworkIP)NetworkIP
elseNetworkDestinationIP

Stage 7: where

| where ipv4_is_private(TI_ipEntity) == false
    and TI_ipEntity !startswith "fe80"
    and TI_ipEntity !startswith "::"
    and TI_ipEntity !startswith "127."

Stage 8: join

| join kind=innerunique (
    DataverseActivity
    | where TimeGenerated >= ago(dt_lookBack)
    | where isnotempty(ClientIp)
    | where ipv4_is_private(ClientIp) == false
        and ClientIp !startswith "fe80"
        and ClientIp !startswith "::"
        and ClientIp !startswith "127."
    | extend DataverseActivity_TimeGenerated = TimeGenerated
    )
    on $left.TI_ipEntity == $right.ClientIp

Stage 9: where

where DataverseActivity_TimeGenerated < ExpirationDateTime

Stage 10: summarize

summarize DataverseActivity_TimeGenerated by IndicatorId, ClientIp

Stage 11: project

project ActivityGroupNames, ClientIp, ConfidenceScore, DataverseActivity_TimeGenerated, Description, EmailSourceIpAddress, ExpirationDateTime, IndicatorId, InstanceUrl, NetworkDestinationIP, NetworkIP, NetworkSourceIP, TI_ipEntity, ThreatType, Url, UserId

Stage 12: extend

extend AccountName, CloudAppId, UPNSuffix, timestamp

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Activeeq
  • true corpus 60 (kusto 60)
field:"Active" kind:eq value:"true"
ClientIpis_not_null
  • (no value, null check)
field:"ClientIp" kind:is_not_null
EmailSourceIpAddressis_not_null
  • (no value, null check)
field:"EmailSourceIpAddress" kind:is_not_null
NetworkDestinationIPis_not_null
  • (no value, null check)
field:"NetworkDestinationIP" kind:is_not_null
NetworkIPis_not_null
  • (no value, null check)
field:"NetworkIP" kind:is_not_null
NetworkSourceIPis_not_null
  • (no value, null check)
field:"NetworkSourceIP" kind:is_not_null

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
TI_ipEntitycidr_match10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8excludes:TI_ipEntity
TI_ipEntitystarts_with127.excludes:TI_ipEntity field:"TI_ipEntity" value:"127."
TI_ipEntitystarts_with::excludes:TI_ipEntity field:"TI_ipEntity" value:"::"
TI_ipEntitystarts_withfe80excludes:TI_ipEntity field:"TI_ipEntity" value:"fe80"
ClientIpcidr_match10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8excludes:ClientIp
ClientIpstarts_with127.excludes:ClientIp field:"ClientIp" value:"127."
ClientIpstarts_with::excludes:ClientIp field:"ClientIp" value:"::"
ClientIpstarts_withfe80excludes:ClientIp field:"ClientIp" value:"fe80"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActivityGroupNamesproject
ClientIpproject
ConfidenceScoreproject
DataverseActivity_TimeGeneratedproject
Descriptionproject
EmailSourceIpAddressproject
ExpirationDateTimeproject
IndicatorIdproject
InstanceUrlproject
NetworkDestinationIPproject
NetworkIPproject
NetworkSourceIPproject
TI_ipEntityproject
ThreatTypeproject
Urlproject
UserIdproject
AccountNameextend
CloudAppIdextend
UPNSuffixextend
timestampextend

MosaicLoader

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

This query looks Malware Hides Itself Among Windows Defender Exclusions to Evade Detection.

MITRE ATT&CK Coverage

TacticTechniques
Defense Impairment

Telemetry Coverage

Rule Body

id: 506f4d6b-3864-4bb1-8f75-a13fb066f97a
name: MosaicLoader
description: |
  This query looks Malware Hides Itself Among Windows Defender Exclusions to Evade Detection.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceRegistryEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1562
tags:
  - MosaicLoader
  - Windows Defender Exclusions
query: |
  DeviceRegistryEvents 
  | where ((ActionType == "RegistryValueSet") and (RegistryKey startswith @"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Defender\\Exclusions\\Paths" 
  or RegistryKey startswith @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Extensions"
  or RegistryKey startswith @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Processes"))
  | extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
  | extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: DeviceName
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
  - entityType: RegistryValue
    fieldMappings:
      - identifier: Name
        columnName: RegistryValueName
      - identifier: Value
        columnName: RegistryValueData
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

DeviceRegistryEvents

Stage 2: where

| where ((ActionType == "RegistryValueSet") and (RegistryKey startswith @"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Defender\\Exclusions\\Paths" 
or RegistryKey startswith @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Extensions"
or RegistryKey startswith @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Processes"))

Stage 3: extend

| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
HostName =
ifDeviceName has "."substring(DeviceName, 0, indexof(DeviceName, '.'))
elseDeviceName

Stage 4: extend

| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
DnsDomain =
ifDeviceName has "."substring(DeviceName, (indexof(DeviceName, '.') + 1))
else""

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActionTypeeq
  • RegistryValueSet
field:"EventType" kind:eq value:"RegistryValueSet"
RegistryKeystarts_with
  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Extensions
  • HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Defender\Exclusions\Processes
  • HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Defender\\Exclusions\\Paths
field:"TargetObject" kind:starts_with

Output Fields

These fields are emitted when the rule matches.

FieldSource
HostNameextend
DnsDomainextend

Multi-Factor Authentication for User Disabled

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

Detects when multi-factor authentication is disabled for a specific user.

Rule Body

id: 3fefd6c5-bbe9-49a9-95b2-d4b8a5591d9c
name: Multi-Factor Authentication for User Disabled
description: Detects when multi-factor authentication is disabled for a specific user.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 40204\n| project\n    Date\
  \ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n    DataSource = original_host,\n\
  \    EventId = instanceId,\n    UserName = user,\n    MessageDetails = Description,\n\
  \    Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
  Date: Date
  VbrHostName: DataSource
  EventId: EventId
  MessageDetails: MessageDetails
  Severity: Severity

Stages and Predicates

Stage 1: source

Veeam_GetSecurityEvents

Stage 2: where

| where instanceId == 40204

Stage 3: project

| project
    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
    DataSource = original_host,
    EventId = instanceId,
    UserName = user,
    MessageDetails = Description,
    Severity = SeverityDescription

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MessageDetailsproject
Severityproject
UserNameproject

Power Platform - Connector added to a sensitive environment

#
Status
available
Severity
low
Time window
7d
Source
github.com/Azure/Azure-Sentinel

Identifies occurrences of new API connector creations within Power Platform, specifically targeting a predefined list of sensitive environments.

MITRE ATT&CK Coverage

Telemetry Coverage

PlatformRecord / event type
Microsoft 365PutConnection

Rule Body

id: 886a5655-3d12-42f1-8927-4095789c575e
kind: Scheduled
name: Power Platform - Connector added to a sensitive environment
description: Identifies occurrences of new API connector creations within Power Platform,
  specifically targeting a predefined list of sensitive environments.
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: PowerPlatformAdmin
    dataTypes:
      - PowerPlatformAdminActivity
queryFrequency: 1h
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
  - Exfiltration
relevantTechniques:
  - T0871
  - T1567
  - T1537
query: |
  let sensitive_environment_id = dynamic([
      // Specify the list of sensitive power platform environment ID's to monitor here.
      // Example: "10e72012-8886-41ec-b973-250286419b38", "183c7056-7ed0-426f-8ae6-69819cf72259"
      ]);
  let query_frequency = 11h;
  PowerPlatformAdminActivity
  | where TimeGenerated >= ago (query_frequency)
  | where EventOriginalType == "PutConnection"
  | extend Properties = tostring(PropertyCollection)
  | extend SrcIpAddr = extract(@'"enduser.ip_address","Value":"([^"]+)"', 1, Properties)
  | extend SrcIpAddr = iif(SrcIpAddr startswith '::ffff:', replace_string('::ffff:', '', SrcIpAddr), SrcIpAddr)
  | extend
      EnvironmentId = extract(@'"powerplatform.analytics.resource.environment.id","Value":"([^"]+)"', 1, Properties),
      ConnectionId = extract(@'"powerplatform.analytics.resource.connection.id","Value":"([^"]+)"', 1, Properties)
  | where EnvironmentId in~ (sensitive_environment_id)
  | extend
      AccountName = tostring(split(ActorName, "@")[0]),
      UPNSuffix = tostring(split(ActorName, "@")[1])
  | project
      TimeGenerated,
      EventOriginalType,
      ActorName,
      SrcIpAddr,
      ConnectionId,
      EnvironmentId,
      AccountName,
      UPNSuffix
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
alertDetailsOverride:
  alertDisplayNameFormat: New Power Platform connector added in a sensitive environment
  alertDescriptionFormat: '{{ActorName}} added a new API connector in environment
    id {{EnvironmentId}}. This environment has been listed as sensitive.'
customDetails:
  Environment: EnvironmentId
  Connection: ConnectionId
version: 3.2.0

Stages and Predicates

Parameters

let query_frequency = 11h;

Let Binding: sensitive_environment_id used in Stage 5

let sensitive_environment_id = dynamic([
    ]);

Stage 1: source

PowerPlatformAdminActivity

Stage 2: where

| where TimeGenerated >= ago (query_frequency)

Stage 3: where

| where EventOriginalType == "PutConnection"

Stage 4: extend (4 consecutive steps)

| extend Properties = tostring(PropertyCollection)
| extend SrcIpAddr = extract(@'"enduser.ip_address","Value":"([^"]+)"', 1, Properties)
| extend SrcIpAddr = iif(SrcIpAddr startswith '::ffff:', replace_string('::ffff:', '', SrcIpAddr), SrcIpAddr)
| extend
    EnvironmentId = extract(@'"powerplatform.analytics.resource.environment.id","Value":"([^"]+)"', 1, Properties),
    ConnectionId = extract(@'"powerplatform.analytics.resource.connection.id","Value":"([^"]+)"', 1, Properties)

Stage 5: where

| where EnvironmentId in~ (sensitive_environment_id)

Stage 6: extend

| extend
    AccountName = tostring(split(ActorName, "@")[0]),
    UPNSuffix = tostring(split(ActorName, "@")[1])

Stage 7: project

| project
    TimeGenerated,
    EventOriginalType,
    ActorName,
    SrcIpAddr,
    ConnectionId,
    EnvironmentId,
    AccountName,
    UPNSuffix

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNameproject
ActorNameproject
ConnectionIdproject
EnvironmentIdproject
EventOriginalTypeproject
SrcIpAddrproject
TimeGeneratedproject
UPNSuffixproject

Theom - Healthcare data unencrypted

#

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
Time window
5m
Source
github.com/Azure/Azure-Sentinel

"Creates Sentinel incidents for critical/high Theom risks, associated with ruleId TRIS0004 (Healthcare data has been observed in unencrypted data stores. Encrypt data at rest to comply with this CIS requirement)"

MITRE ATT&CK Coverage

Rule Body

id: fb1b0deb-2a8f-4d8d-8d9d-0a8d327442e7
name: Theom - Healthcare data unencrypted
description: |
   "Creates Sentinel incidents for critical/high Theom risks, associated with ruleId TRIS0004 (Healthcare data has been observed in unencrypted data stores. Encrypt data at rest to comply with this CIS requirement)"
severity: High
status : Available
requiredDataConnectors: 
  - connectorId: Theom
    dataTypes:
      - TheomAlerts_CL
queryPeriod: 5m
queryFrequency: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Collection
relevantTechniques:
  - T1213
  - T1530
query: |
   TheomAlerts_CL
     | where customProps_RuleId_s == "TRIS0004" and (priority_s == "P1" or priority_s == "P2")
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: "Theom Alert ID: {{id_s}} "
  alertDescriptionFormat: "\nSummary: {{summary_s}}  \nAdditional info: {{details_s}}\nPlease investigate further on Theom UI at {{deepLink_s}}\n"
entityMappings:
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: customProps_AssetName_s
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: deepLink_s
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

TheomAlerts_CL

Stage 2: where

| where customProps_RuleId_s == "TRIS0004" and (priority_s == "P1" or priority_s == "P2")

Indicators

These rows show field, operator, and value matches.

Uniqkey - Credential export from newly created account

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

Detects a data export performed by an employee whose Uniqkey account was created or activated within the preceding 14 days. A freshly provisioned account has little legitimate reason to export organization credentials, so this pattern can indicate a compromised onboarding flow or an account created specifically to stage exfiltration. The action filter matches the provisioning action identifiers from the Uniqkey audit-log catalog, with a name-based fallback for future action variants.

MITRE ATT&CK Coverage

TacticTechniques
Persistence
Credential Access

Rule Body

id: 3939f01f-9563-4cd3-8423-97a82e82719b
name: Uniqkey - Credential export from newly created account
version: 1.0.0
kind: Scheduled
description: |-
  Detects a data export performed by an employee whose Uniqkey account was created or activated within the preceding 14 days. A freshly provisioned account has little legitimate reason to export organization credentials, so this pattern can indicate a compromised onboarding flow or an account created specifically to stage exfiltration. The action filter matches the provisioning action identifiers from the Uniqkey audit-log catalog, with a name-based fallback for future action variants.
severity: Medium
requiredDataConnectors:
  - connectorId: UniqkeyEventsConnector
    dataTypes:
      - UniqkeyEvents_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
- Persistence
relevantTechniques:
- T1555
- T1136
query: |-
  let lookback = 14d;
  let frequency = 1h;
  let provisioningActionIds = dynamic([
      "eb7dea42-df3a-44c6-808d-94b7fc46dde9", // Create employee account
      "263c44a2-2075-46bd-949d-aa828517a3d1", // Link employee account
      "4ed5b3be-d1b1-4447-af32-9ae2f1c090c7", // Activate employee
      "e494db5c-b135-441c-8fa3-6e5ad94d05f8", // Activate employee account
      "a652e6df-5a3c-438b-afde-198cd62e8491", // Activated account
      "c7d4f9b2-5e3a-4f8c-9b6d-2e7a1c8d5f3b", // Activate SSO employee
      "3cc88450-39ef-472e-9e0c-06ad7c9af257", // Process new employee
      "c9ebd265-3c60-4228-b948-863c8134d29c", // Approve process new employee
      "1b6c2121-00cc-40fe-9ea4-01f6dd085c34"  // Approve process invite employee
  ]);
  let newAccounts =
      UniqkeyEvents_CL
      | where TimeGenerated > ago(lookback)
      | where Category == "account_management"
      | where tolower(ActionId) in (provisioningActionIds) or Action has_any ("create", "invite", "activate", "link")
      | where TargetType == "employee"
      | project CreationTime = TimeGenerated, CreationAction = Action, NewEmployeeId = TargetId, NewEmployeeName = TargetName;
  UniqkeyEvents_CL
  | where TimeGenerated > ago(frequency)
  | where Category == "data_export"
  | join kind=inner newAccounts on $left.ActorId == $right.NewEmployeeId
  | where TimeGenerated > CreationTime
  | project TimeGenerated, ActorEmail, ActorType, Action, TargetType, TargetName, SrcIpAddr, CreationTime, CreationAction, NewEmployeeName
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: 5h
    matchingMethod: AllEntities
suppressionDuration: 5h
eventGroupingSettings:
  aggregationKind: AlertPerResult

Stages and Predicates

Parameters

let lookback = 14d;
let frequency = 1h;

Let Binding: provisioningActionIds

let provisioningActionIds = dynamic([
    "eb7dea42-df3a-44c6-808d-94b7fc46dde9",
    "263c44a2-2075-46bd-949d-aa828517a3d1",
    "4ed5b3be-d1b1-4447-af32-9ae2f1c090c7",
    "e494db5c-b135-441c-8fa3-6e5ad94d05f8",
    "a652e6df-5a3c-438b-afde-198cd62e8491",
    "c7d4f9b2-5e3a-4f8c-9b6d-2e7a1c8d5f3b",
    "3cc88450-39ef-472e-9e0c-06ad7c9af257",
    "c9ebd265-3c60-4228-b948-863c8134d29c",
    "1b6c2121-00cc-40fe-9ea4-01f6dd085c34"
]);

Let Binding: newAccounts used in Stage 4

let newAccounts = UniqkeyEvents_CL
    | where TimeGenerated > ago(lookback)
    | where Category == "account_management"
    | where tolower(ActionId) in (provisioningActionIds) or Action has_any ("create", "invite", "activate", "link")
    | where TargetType == "employee"
    | project CreationTime = TimeGenerated, CreationAction = Action, NewEmployeeId = TargetId, NewEmployeeName = TargetName;

Stage 1: source

UniqkeyEvents_CL

Stage 2: where

| where TimeGenerated > ago(frequency)

Stage 3: where

| where Category == "data_export"

Stage 4: join

| join kind=inner newAccounts on $left.ActorId == $right.NewEmployeeId

Stage 5: where

where TimeGenerated > CreationTime

Stage 6: project

project Action, ActorEmail, ActorType, CreationAction, CreationTime, NewEmployeeName, SrcIpAddr, TargetName, TargetType, TimeGenerated

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Actionmatch
  • activate transforms: term
  • create transforms: term
  • invite transforms: term
  • link transforms: term
field:"Action" kind:match
ActionIdin
  • 1b6c2121-00cc-40fe-9ea4-01f6dd085c34 transforms: tolower
  • 263c44a2-2075-46bd-949d-aa828517a3d1 transforms: tolower
  • 3cc88450-39ef-472e-9e0c-06ad7c9af257 transforms: tolower
  • 4ed5b3be-d1b1-4447-af32-9ae2f1c090c7 transforms: tolower
  • a652e6df-5a3c-438b-afde-198cd62e8491 transforms: tolower
  • c7d4f9b2-5e3a-4f8c-9b6d-2e7a1c8d5f3b transforms: tolower
  • c9ebd265-3c60-4228-b948-863c8134d29c transforms: tolower
  • e494db5c-b135-441c-8fa3-6e5ad94d05f8 transforms: tolower
  • eb7dea42-df3a-44c6-808d-94b7fc46dde9 transforms: tolower
field:"ActionId" kind:in
Categoryeq
  • account_management
  • data_export
field:"Category" kind:eq
TargetTypeeq
  • employee
field:"TargetType" kind:eq value:"employee"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Actionproject
ActorEmailproject
ActorTypeproject
CreationActionproject
CreationTimeproject
NewEmployeeNameproject
SrcIpAddrproject
TargetNameproject
TargetTypeproject
TimeGeneratedproject

Back to all Kusto rules