Detection rules › Kusto

BTP - Cloud Integration tampering with security material

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

Identifies operations on security material (credentials, certificates, and keys) within SAP Cloud Integration. This includes credentials (passwords/secrets), X.509 certificates and key pairs, and PGP keys. Unauthorized manipulation of security material could indicate an attacker attempting to: - Gain access to external systems using stored credentials - Intercept or tamper with encrypted communications - Establish persistence through certificate manipulation - Cover tracks by deleting security artifacts

MITRE ATT&CK coverage

TacticTechniques
StealthT1070 Indicator Removal
Credential AccessT1552 Unsecured Credentials

Rule body kusto

id: 8d5f3a1b-9c2e-4f7d-b8a6-1e4c7f9d2b5a
kind: Scheduled
name: BTP - Cloud Integration tampering with security material
description: |
  Identifies operations on security material (credentials, certificates, and keys) within SAP Cloud Integration.
  This includes credentials (passwords/secrets), X.509 certificates and key pairs, and PGP keys.
  Unauthorized manipulation of security material could indicate an attacker attempting to:
  - Gain access to external systems using stored credentials
  - Intercept or tamper with encrypted communications
  - Establish persistence through certificate manipulation
  - Cover tracks by deleting security artifacts
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SAPBTPAuditEvents
    dataTypes:
      - SAPBTPAuditLog_CL
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - DefenseEvasion
relevantTechniques:
  - T1552
  - T1070
query: |
  let securityMaterialTypes = dynamic(["Credential", "X.509 Certificate", "X.509 Key-Pair", "PGP Public Keys", "PGP Secret Keys"]);
  let keystoreActions = dynamic(["Create", "Update", "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),
           keystoreName = tostring(parsedData.attributes["Keystore Name"])
  | where objectType in (securityMaterialTypes)
  | where 
      (objectType == "Credential" and action in ("PasswordStore", "PasswordUpdate", "PasswordDelete"))
      or 
      (objectType != "Credential" and action in (keystoreActions))
  | extend normalizedAction = case(
      action == "PasswordStore", "created",
      action == "PasswordUpdate", "updated",
      action == "PasswordDelete", "deleted",
      action == "Create", "created",
      action == "Update", "updated",
      action == "Change", "changed",
      action == "Delete", "deleted",
      action
  )
  | extend MessageText = case(
      objectType == "Credential", strcat("Security credential '", objectId, "' was ", normalizedAction),
      isnotempty(keystoreName), strcat(objectType, " '", objectId, "' was ", normalizedAction, " in keystore '", keystoreName, "'"),
      strcat(objectType, " '", objectId, "' was ", normalizedAction)
  )
  | project
      UpdatedOn,
      UserName,
      MessageText,
      ObjectType = objectType,
      ObjectId = objectId,
      Action = action,
      NormalizedAction = normalizedAction,
      KeystoreName = keystoreName,
      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 security material management
    - Unauthorized credential or certificate manipulation
    - Attacker tampering with security artifacts to gain access or cover tracks
customDetails:
  ObjectType: ObjectType
  ObjectId: ObjectId
  Action: Action
  KeystoreName: KeystoreName
  SourceIP: ipAddress
version: 1.0.0

Stages and Predicates

Parameters

let securityMaterialTypes = dynamic(["Credential", "X.509 Certificate", "X.509 Key-Pair", "PGP Public Keys", "PGP Secret Keys"]);
let keystoreActions = dynamic(["Create", "Update", "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),
         keystoreName = tostring(parsedData.attributes["Keystore Name"])

Stage 4: where

| where objectType in (securityMaterialTypes)

Stage 5: where

| where 
    (objectType == "Credential" and action in ("PasswordStore", "PasswordUpdate", "PasswordDelete"))
    or 
    (objectType != "Credential" and action in (keystoreActions))

Stage 6: extend

| extend normalizedAction = case(
    action == "PasswordStore", "created",
    action == "PasswordUpdate", "updated",
    action == "PasswordDelete", "deleted",
    action == "Create", "created",
    action == "Update", "updated",
    action == "Change", "changed",
    action == "Delete", "deleted",
    action
)
normalizedAction =
ifaction == "PasswordStore""created"
elifaction == "PasswordUpdate""updated"
elifaction == "PasswordDelete""deleted"
elifaction == "Create""created"
elifaction == "Update""updated"
elifaction == "Change""changed"
elifaction == "Delete""deleted"
elseaction

Stage 7: extend

| extend MessageText = case(
    objectType == "Credential", strcat("Security credential '", objectId, "' was ", normalizedAction),
    isnotempty(keystoreName), strcat(objectType, " '", objectId, "' was ", normalizedAction, " in keystore '", keystoreName, "'"),
    strcat(objectType, " '", objectId, "' was ", normalizedAction)
)
MessageText =
ifobjectType == "Credential"strcat("Security credential '", objectId, "' was ", normalizedAction)
elifisnotempty(keystoreName)strcat(objectType, " '", objectId, "' was ", normalizedAction, " in keystore '", keystoreName, "'")
elsestrcat(objectType, " '", objectId, "' was ", normalizedAction)

Stage 8: project

| project
    UpdatedOn,
    UserName,
    MessageText,
    ObjectType = objectType,
    ObjectId = objectId,
    Action = action,
    NormalizedAction = normalizedAction,
    KeystoreName = keystoreName,
    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
  • PasswordDelete transforms: cased
  • PasswordStore transforms: cased
  • PasswordUpdate transforms: cased
  • Update transforms: cased
objectTypeeq
  • Credential transforms: cased
objectTypein
  • Credential transforms: cased
  • PGP Public Keys transforms: cased
  • PGP Secret Keys transforms: cased
  • X.509 Certificate transforms: cased
  • X.509 Key-Pair transforms: cased
objectTypene
  • Credential 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
KeystoreNameproject
MessageTextproject
NormalizedActionproject
ObjectIdproject
ObjectTypeproject
Tenantproject
UpdatedOnproject
UserNameproject
ipAddressproject
AccountNameextend
UPNSuffixextend