Detection rules › Kusto

Vaikora - Quarantine rate spike

Status
available
Severity
medium
Time window
7d
Group by
TenantId_s
Source
github.com/Azure/Azure-Sentinel

Identifies an abnormal spike in Vaikora for O365 quarantine volume for a tenant. Fires when the hourly count exceeds three times the rolling 7-day baseline, signaling a targeted campaign or model surge worth admin review.

MITRE ATT&CK coverage

TacticTechniques
Initial Access

Rule body

id: 3b3eb1cd-578d-4198-9b7a-bd7253b0dc9f
name: Vaikora - Quarantine rate spike
description: |
  Identifies an abnormal spike in Vaikora for O365 quarantine volume for a tenant. Fires when the hourly count exceeds three times the rolling 7-day baseline, signaling a targeted campaign or model surge worth admin review.
severity: Medium
requiredDataConnectors:
  - connectorId: VaikoraO365
    dataTypes:
      - VaikoraO365_Quarantine_CL
queryFrequency: 1h
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - InitialAccess
relevantTechniques:
  - T1566
query: |
  let baseline = VaikoraO365_Quarantine_CL
    | where TimeGenerated between (ago(7d) .. ago(1h))
    | summarize HourlyCount = count() by bin(TimeGenerated, 1h), TenantId_s
    | summarize AvgHourlyCount = avg(HourlyCount) by TenantId_s;
  VaikoraO365_Quarantine_CL
  | where TimeGenerated >= ago(1h)
  | summarize RecentCount = count() by TenantId_s
  | join kind=inner baseline on TenantId_s
  | where RecentCount > (AvgHourlyCount * 3.0)
  | where RecentCount >= 10
  | project TimeGenerated = now(), TenantId = TenantId_s, RecentCount, AvgHourlyCount, RatioOverBaseline = (RecentCount / AvgHourlyCount)
entityMappings:
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: TenantId
alertDetailsOverride:
  alertDisplayNameFormat: "Vaikora-O365 quarantine spike on tenant {{TenantId}}"
  alertDescriptionFormat: "Quarantine count for the last hour is {{RecentCount}}, which is more than three times the 7-day baseline of {{AvgHourlyCount}}."
version: 1.0.0
kind: Scheduled

Stages and Predicates

Let binding: baseline used in Stage 4

let baseline = VaikoraO365_Quarantine_CL
  | where TimeGenerated between (ago(7d) .. ago(1h))
  | summarize HourlyCount = count() by bin(TimeGenerated, 1h), TenantId_s
  | summarize AvgHourlyCount = avg(HourlyCount) by TenantId_s;

Stage 1: source

VaikoraO365_Quarantine_CL

Stage 2: where

| where TimeGenerated >= ago(1h)

Stage 3: summarize

| summarize RecentCount = count() by TenantId_s

Stage 4: join

| join kind=inner baseline on TenantId_s

Stage 5: where

| where RecentCount > (AvgHourlyCount * 3.0)

Stage 6: where

| where RecentCount >= 10

Stage 7: project

| project TimeGenerated = now(), TenantId = TenantId_s, RecentCount, AvgHourlyCount, RatioOverBaseline = (RecentCount / AvgHourlyCount)

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
RecentCountcross_field_compare
  • AvgHourlyCount transforms: op:gt, rhs:mul:3.0
field:"RecentCount" kind:cross_field_compare value:"AvgHourlyCount"
RecentCountge
  • 10
field:"RecentCount" kind:ge value:"10"

Output fields

These fields are emitted when the rule matches.

FieldSource
AvgHourlyCountproject
RatioOverBaselineproject
RecentCountproject
TenantIdproject
TimeGeneratedproject