Detection rules › Kusto

Cross-tenant Access Settings Organization Added

Status
available
Severity
medium
Time window
2d
Source
github.com/Azure/Azure-Sentinel

Organizations are added in the Cross-tenant Access Settings to control communication inbound or outbound for users and applications. This detection notifies when an Organization is added other than the list that is supposed to exist from the Microsoft Entra ID Cross-tenant Access Settings.

MITRE ATT&CK coverage

Telemetry coverage

Rules detecting the same action

These rules filter on the same operation.

Rule body

id: 757e6a79-6d23-4ae6-9845-4dac170656b5
name: Cross-tenant Access Settings Organization Added
description: |
  'Organizations are added in the Cross-tenant Access Settings to control communication inbound or outbound for users and applications. This detection notifies when an Organization is added other than the list that is supposed to exist from the Microsoft Entra ID Cross-tenant Access Settings.'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
queryFrequency: 2d
queryPeriod: 2d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - InitialAccess
  - Persistence
  - Discovery
relevantTechniques:
  - T1078.004
  - T1136.003
  - T1087.004
query: |
  // Tenants IDs can be found by navigating to Azure Active Directory then from menu on the left, select External Identities, then from menu on the left, select Cross-tenant access settings and from the list shown of Tenants
  let ExpectedTenantIDs = dynamic(["List of expected tenant IDs","Tenant ID 2"]);
  AuditLogs
  | where OperationName has "Add a partner to cross-tenant access setting"
  | mv-apply TargetResource = TargetResources on
    (
        where TargetResource.type =~ "Policy"
        | extend Properties = TargetResource.modifiedProperties
    )
  | mv-apply Property = Properties on
    (
        where Property.displayName =~ "tenantId"
        | extend ExtTenantIDAdded = trim('"',tostring(Property.newValue))
    )
  | where ExtTenantIDAdded !in (ExpectedTenantIDs)
  | extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
  | extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
  | extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
  | extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
  | extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
  | extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: InitiatingAppName
      - identifier: AadUserId
        columnName: InitiatingAppServicePrincipalId
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: InitiatingUserPrincipalName
      - identifier: Name
        columnName: InitiatingAccountName
      - identifier: UPNSuffix
        columnName: InitiatingAccountUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: InitiatingAadUserId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: InitiatingIpAddress
version: 1.1.1
kind: Scheduled

Stages and Predicates

Parameters

let ExpectedTenantIDs = dynamic(["List of expected tenant IDs","Tenant ID 2"]);

Stage 1: source

AuditLogs

Stage 2: where

| where OperationName has "Add a partner to cross-tenant access setting"

Stage 3: kusto:mv-apply

| mv-apply TargetResource = TargetResources on
  (
      where TargetResource.type =~ "Policy"
      | extend Properties = TargetResource.modifiedProperties
  )

Stage 4: kusto:mv-apply

| mv-apply Property = Properties on
  (
      where Property.displayName =~ "tenantId"
      | extend ExtTenantIDAdded = trim('"',tostring(Property.newValue))
  )

Stage 5: where

| where ExtTenantIDAdded !in (ExpectedTenantIDs)

Stage 6: extend (6 consecutive steps)

| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
ExtTenantIDAddedinList of expected tenant IDs, Tenant ID 2excludes:ExtTenantIDAdded field:"ExtTenantIDAdded" value:"List of expected tenant IDs" field:"ExtTenantIDAdded" value:"Tenant ID 2"

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
InitiatingAppNameextend
InitiatingAppServicePrincipalIdextend
InitiatingUserPrincipalNameextend
InitiatingAadUserIdextend
InitiatingIpAddressextend
InitiatingAccountNameextend
InitiatingAccountUPNSuffixextend