Detection rules › Kusto
Users searching for VIP user activity
This query monitors for users running Log Analytics queries that contain filters for specific, defined VIP user accounts or the VIPUser watchlist template. Use this detection to alert for users specifically searching for activity of sensitive users.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Collection | |
| Exfiltration |
Rule body
id: f7f4a77e-f68f-4b56-9aaf-a0c9d87d7a8e
name: Users searching for VIP user activity
description: |
This query monitors for users running Log Analytics queries that contain filters for specific, defined VIP user accounts or the VIPUser watchlist template.
Use this detection to alert for users specifically searching for activity of sensitive users.
severity: Low
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Collection
- Exfiltration
relevantTechniques:
- T1530
- T1213
- T1020
query: |
// Replace these with the username or emails of your VIP users you wish to monitor for.
let vips = dynamic(['vip1@email.com','vip2@email.com']);
// Add users who are allowed to conduct these searches - this could be specific SOC team members
let allowed_users = dynamic([]);
LAQueryLogs
| where QueryText has_any (vips) or QueryText has_any ('_GetWatchlist("VIPUsers")', "_GetWatchlist('VIPUsers')")
| where AADEmail !in (allowed_users)
| project TimeGenerated, AADEmail, RequestClientApp, QueryText, ResponseRowCount, RequestTarget
| extend timestamp = TimeGenerated, AccountName = tostring(split(AADEmail, "@")[0]), AccountUPNSuffix = tostring(split(AADEmail, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: AzureResource
fieldMappings:
- identifier: ResourceId
columnName: RequestTarget
version: 1.1.4
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Others" ]
Stages and Predicates
Parameters
let vips = dynamic(['vip1@email.com','vip2@email.com']);
let allowed_users = dynamic([]);
Stage 1: source
LAQueryLogs
Stage 2: where
| where QueryText has_any (vips) or QueryText has_any ('_GetWatchlist("VIPUsers")', "_GetWatchlist('VIPUsers')")
Stage 3: where
| where AADEmail !in (allowed_users)
Stage 4: project
| project TimeGenerated, AADEmail, RequestClientApp, QueryText, ResponseRowCount, RequestTarget
Stage 5: extend
| extend timestamp = TimeGenerated, AccountName = tostring(split(AADEmail, "@")[0]), AccountUPNSuffix = tostring(split(AADEmail, "@")[1])
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
AADEmail | eq | [] | excludes:AADEmail field:"AADEmail" value:"[]" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
QueryText | match |
| field:"QueryText" kind:match |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AADEmail | project |
QueryText | project |
RequestClientApp | project |
RequestTarget | project |
ResponseRowCount | project |
TimeGenerated | project |
AccountName | extend |
AccountUPNSuffix | extend |
timestamp | extend |