Detection rules › Kusto
F&O - Non-interactive account mapped to self or sensitive privileged user
Identifies changes to Microsoft Entra client apps registered for Finance & Operations, specifically when a new client is mapped to a predefined list of sensitive privileged user accounts, or when a user associates a client app with their own account.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | T1078 Valid Accounts, T1098 Account Manipulation, T1136 Create Account, T1556 Modify Authentication Process |
| Privilege Escalation | T1078 Valid Accounts, T1098 Account Manipulation |
| Credential Access | T1556 Modify Authentication Process |
| Persistence | T0859 Valid Accounts |
| Lateral Movement | T0859 Valid Accounts |
Rule body kusto
id: 5b7cc7f9-fe54-4138-9fb0-d650807345d3
kind: Scheduled
name: F&O - Non-interactive account mapped to self or sensitive privileged user
description: Identifies changes to Microsoft Entra client apps registered for Finance
& Operations, specifically when a new client is mapped to a predefined list of sensitive
privileged user accounts, or when a user associates a client app with their own
account.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Dynamics365Finance
dataTypes:
- FinanceOperationsActivity_CL
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1556
- T1098
- T1136
- T1078
- T0859
query: |
// Add sensitive privilege accounts to the privileged_user_accounts variable.
// Example: let privileged_user_accounts = dynamic(["Admin1", "Admin"]);
let privileged_user_accounts = dynamic([]);
FinanceOperationsActivity_CL
| where TableName == "SysAADClientTable" and LogType in ("Insert", "Update")
| extend ClientId = tostring(parse_json(tostring(FormattedData.["03::AADClientId"])).NewData)
| extend User = parse_json(tostring(FormattedData.UserId))
| extend
MappedUser = tostring(User.NewData),
PreviousUserId = tostring(User.OldData),
TargetAppName = tostring(parse_json(tostring(FormattedData.Name)).NewData),
FinOpsAppId = 32780
| where MappedUser in (privileged_user_accounts) or LogCreatedBy == MappedUser
| project
LogCreatedDateTime,
LogCreatedBy,
LogType,
TargetAppName,
MappedUser,
PreviousUserId,
ClientId,
FinOpsAppId
eventGroupingSettings:
aggregationKind: AlertPerResult
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: LogCreatedBy
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: ClientId
- entityType: CloudApplication
fieldMappings:
- identifier: AppId
columnName: FinOpsAppId
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: MappedUser
alertDetailsOverride:
alertDisplayNameFormat: F&O - Sensitive non-interactive user mapping detected
alertDescriptionFormat: User account {{LogCreatedBy}} mapped an Azure AD App to
senstitive privileged user account {{MappedUser}}. The associated Azure AD client
ID is {{ClientId}}
version: 3.2.0
Stages and Predicates
Parameters
let privileged_user_accounts = dynamic([]);
Stage 1: source
FinanceOperationsActivity_CL
Stage 2: where
| where TableName == "SysAADClientTable" and LogType in ("Insert", "Update")
Stage 3: extend (3 consecutive steps)
| extend ClientId = tostring(parse_json(tostring(FormattedData.["03::AADClientId"])).NewData)
| extend User = parse_json(tostring(FormattedData.UserId))
| extend
MappedUser = tostring(User.NewData),
PreviousUserId = tostring(User.OldData),
TargetAppName = tostring(parse_json(tostring(FormattedData.Name)).NewData),
FinOpsAppId = 32780
Stage 4: where
| where MappedUser in (privileged_user_accounts) or LogCreatedBy == MappedUser
Stage 5: project
| project
LogCreatedDateTime,
LogCreatedBy,
LogType,
TargetAppName,
MappedUser,
PreviousUserId,
ClientId,
FinOpsAppId
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
LogCreatedBy | eq |
|
LogType | in |
|
MappedUser | in |
|
TableName | eq |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
ClientId | project |
FinOpsAppId | project |
LogCreatedBy | project |
LogCreatedDateTime | project |
LogType | project |
MappedUser | project |
PreviousUserId | project |
TargetAppName | project |