Detection rules › Kusto

BTP - Cloud Integration access policy tampering

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

Identifies changes to access policies in SAP Cloud Integration. Access policies control authorization for integration artifacts, defining which users and roles can access specific integration flows and related content. Unauthorized access policy manipulation could indicate: - Attacker granting themselves access to sensitive integration artifacts - Removal of security controls to enable further malicious activity - Defense evasion by modifying artifact references to hide unauthorized access

MITRE ATT&CK coverage

Rule body kusto

id: 9e6f4b2c-0d3e-5a8f-c9b7-2f5d8a1e4c6b
kind: Scheduled
name: BTP - Cloud Integration access policy tampering
description: |
  Identifies changes to access policies in SAP Cloud Integration. Access policies control
  authorization for integration artifacts, defining which users and roles can access specific
  integration flows and related content.
  
  Unauthorized access policy manipulation could indicate:
  - Attacker granting themselves access to sensitive integration artifacts
  - Removal of security controls to enable further malicious activity
  - Defense evasion by modifying artifact references to hide unauthorized access
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SAPBTPAuditEvents
    dataTypes:
      - SAPBTPAuditLog_CL
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
  - PrivilegeEscalation
relevantTechniques:
  - T1548
  - T1222
query: |
  let accessPolicyTypes = dynamic(["Access Policy", "Artifact Reference"]);
  let monitoredActions = dynamic(["Create", "Change", "Delete"]);
  SAPBTPAuditLog_CL
  | where Category == "audit.security-events"
  | extend data_s = tostring(Message.data),
           ipAddress = tostring(Message.ip)
  | extend parsedData = parse_json(data_s)
  | extend action = tostring(parsedData.action),
           objectType = tostring(parsedData.objectType),
           objectId = tostring(parsedData.objectId),
           policyMessage = tostring(parsedData.attributes.message)
  | where objectType in (accessPolicyTypes)
  | where action in (monitoredActions)
  | extend normalizedAction = case(
      action == "Create", "created",
      action == "Change", "modified",
      action == "Delete", "deleted",
      action
  )
  | extend MessageText = case(
      objectType == "Access Policy", strcat("Access policy '", objectId, "' was ", normalizedAction),
      objectType == "Artifact Reference", strcat("Artifact reference '", objectId, "' was ", normalizedAction),
      strcat(objectType, " '", objectId, "' was ", normalizedAction)
  )
  | project
      UpdatedOn,
      UserName,
      MessageText,
      ObjectType = objectType,
      ObjectId = objectId,
      Action = action,
      PolicyMessage = policyMessage,
      Tenant,
      ipAddress,
      CloudApp = "SAP Cloud Integration"
  | extend AccountName = split(UserName, "@")[0], UPNSuffix = split(UserName, "@")[1]
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ipAddress
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: CloudApp
alertDetailsOverride:
  alertDisplayNameFormat: 'SAP Cloud Integration: {{MessageText}}'
  alertDescriptionFormat: |
    {{MessageText}} by {{UserName}} from IP {{ipAddress}}.
    
    This could indicate:
    - Legitimate access policy administration
    - Unauthorized privilege escalation attempt
    - Attacker modifying security controls to access sensitive integrations
customDetails:
  ObjectType: ObjectType
  ObjectId: ObjectId
  Action: Action
  PolicyMessage: PolicyMessage
  SourceIP: ipAddress
version: 1.0.0

Stages and Predicates

Parameters

let accessPolicyTypes = dynamic(["Access Policy", "Artifact Reference"]);
let monitoredActions = dynamic(["Create", "Change", "Delete"]);

Stage 1: source

SAPBTPAuditLog_CL

Stage 2: where

| where Category == "audit.security-events"

Stage 3: extend (3 consecutive steps)

| extend data_s = tostring(Message.data),
         ipAddress = tostring(Message.ip)
| extend parsedData = parse_json(data_s)
| extend action = tostring(parsedData.action),
         objectType = tostring(parsedData.objectType),
         objectId = tostring(parsedData.objectId),
         policyMessage = tostring(parsedData.attributes.message)

Stage 4: where

| where objectType in (accessPolicyTypes)

Stage 5: where

| where action in (monitoredActions)

Stage 6: extend

| extend normalizedAction = case(
    action == "Create", "created",
    action == "Change", "modified",
    action == "Delete", "deleted",
    action
)
normalizedAction =
ifaction == "Create""created"
elifaction == "Change""modified"
elifaction == "Delete""deleted"
elseaction

Stage 7: extend

| extend MessageText = case(
    objectType == "Access Policy", strcat("Access policy '", objectId, "' was ", normalizedAction),
    objectType == "Artifact Reference", strcat("Artifact reference '", objectId, "' was ", normalizedAction),
    strcat(objectType, " '", objectId, "' was ", normalizedAction)
)
MessageText =
ifobjectType == "Access Policy"strcat("Access policy '", objectId, "' was ", normalizedAction)
elifobjectType == "Artifact Reference"strcat("Artifact reference '", objectId, "' was ", normalizedAction)
elsestrcat(objectType, " '", objectId, "' was ", normalizedAction)

Stage 8: project

| project
    UpdatedOn,
    UserName,
    MessageText,
    ObjectType = objectType,
    ObjectId = objectId,
    Action = action,
    PolicyMessage = policyMessage,
    Tenant,
    ipAddress,
    CloudApp = "SAP Cloud Integration"

Stage 9: extend

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

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.

FieldKindValues
Categoryeq
  • audit.security-events transforms: cased
actionin
  • Change transforms: cased
  • Create transforms: cased
  • Delete transforms: cased
objectTypein
  • Access Policy transforms: cased
  • Artifact Reference transforms: cased

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.

FieldSource
Actionproject
CloudAppproject
MessageTextproject
ObjectIdproject
ObjectTypeproject
PolicyMessageproject
Tenantproject
UpdatedOnproject
UserNameproject
ipAddressproject
AccountNameextend
UPNSuffixextend