Detection rules › Kusto

BTP - Cloud Integration JDBC data source changes

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

Identifies deployment and undeployment of JDBC data source configurations in SAP Cloud Integration. JDBC data sources contain database connection credentials and configuration that enable integration flows to access backend databases. Unauthorized JDBC data source manipulation could indicate: - Attacker adding rogue database connections for data exfiltration - Credential theft by accessing stored database passwords - Modification of connection strings to redirect traffic to attacker-controlled systems

MITRE ATT&CK coverage

TacticTechniques
Credential AccessT1552 Unsecured Credentials
Lateral MovementT1021 Remote Services

Rule body kusto

id: b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e
kind: Scheduled
name: BTP - Cloud Integration JDBC data source changes
description: |
  Identifies deployment and undeployment of JDBC data source configurations in SAP Cloud Integration.
  JDBC data sources contain database connection credentials and configuration that enable
  integration flows to access backend databases.
  
  Unauthorized JDBC data source manipulation could indicate:
  - Attacker adding rogue database connections for data exfiltration
  - Credential theft by accessing stored database passwords
  - Modification of connection strings to redirect traffic to attacker-controlled systems
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SAPBTPAuditEvents
    dataTypes:
      - SAPBTPAuditLog_CL
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - LateralMovement
relevantTechniques:
  - T1552
  - T1021
query: |
  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)
  | where objectType == "Data Source"
  | where action in ("PasswordStore", "PasswordDelete")
  | extend normalizedAction = case(
      action == "PasswordStore", "deployed",
      action == "PasswordDelete", "undeployed",
      action
  )
  | extend MessageText = strcat("JDBC data source '", objectId, "' was ", normalizedAction)
  | project
      UpdatedOn,
      UserName,
      MessageText,
      DataSourceName = objectId,
      Action = action,
      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}}.
    
    JDBC data sources contain database connection credentials. Changes to these configurations
    should be carefully reviewed.
    
    This could indicate:
    - Legitimate database configuration management
    - Unauthorized database connection configuration
    - Attacker establishing lateral movement paths to backend systems
customDetails:
  DataSourceName: DataSourceName
  Action: Action
  SourceIP: ipAddress
version: 1.0.0

Stages and Predicates

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)

Stage 4: where

| where objectType == "Data Source"

Stage 5: where

| where action in ("PasswordStore", "PasswordDelete")

Stage 6: extend

| extend normalizedAction = case(
    action == "PasswordStore", "deployed",
    action == "PasswordDelete", "undeployed",
    action
)
normalizedAction =
ifaction == "PasswordStore""deployed"
elifaction == "PasswordDelete""undeployed"
elseaction

Stage 7: extend

| extend MessageText = strcat("JDBC data source '", objectId, "' was ", normalizedAction)

Stage 8: project

| project
    UpdatedOn,
    UserName,
    MessageText,
    DataSourceName = objectId,
    Action = action,
    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
  • PasswordDelete transforms: cased
  • PasswordStore transforms: cased
objectTypeeq
  • Data Source 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
DataSourceNameproject
MessageTextproject
Tenantproject
UpdatedOnproject
UserNameproject
ipAddressproject
AccountNameextend
UPNSuffixextend