Detection rules › Kusto

full_access_as_app Granted To Application

Status
available
Severity
medium
Time window
1h
Source
github.com/Azure/Azure-Sentinel

This detection looks for the full_access_as_app permission being granted to an OAuth application with Admin Consent. This permission provide access to all Exchange mailboxes via the EWS API can could be exploited to access sensitive data by being added to a compromised application. The application granted this permission should be reviewed to ensure that it is absolutely necessary for the applications function. Ref: https://learn.microsoft.com/graph/auth-limit-mailbox-access

MITRE ATT&CK coverage

Event coverage

Rules detecting the same action

Other rules on this platform that filter on the same API call or operation.

Rule body kusto

id: 54e22fed-0ec6-4fb2-8312-2a3809a93f63
name: full_access_as_app Granted To Application
description: |
  'This detection looks for the full_access_as_app permission being granted to an OAuth application with Admin Consent.
  This permission provide access to all Exchange mailboxes via the EWS API can could be exploited to access sensitive data by being added to a compromised application. The application granted this permission should be reviewed to ensure that it is absolutely necessary for the applications function.
  Ref: https://learn.microsoft.com/graph/auth-limit-mailbox-access'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1550.001
query: |
  AuditLogs
  | where LoggedByService =~ "Core Directory"
  | where Category =~ "ApplicationManagement"
  | where OperationName =~ "Consent to application"
  | where TargetResources has "full_access_as_app"
  | mv-expand TargetResources
  | extend OAuthAppName = TargetResources.displayName
  | extend ModifiedProperties = TargetResources.modifiedProperties 
  | mv-apply Property = ModifiedProperties on 
    (
        where Property.displayName =~ "ConsentContext.isAdminConsent"
        | extend AdminConsent = tostring(Property.newValue)
    )
  | mv-apply Property = ModifiedProperties on 
    (
        where Property.displayName =~ "ConsentAction.Permissions"
        | extend Permissions = tostring(Property.newValue)
    )
  | mv-apply Property = ModifiedProperties on 
    (
        where Property.displayName =~ "TargetId.ServicePrincipalNames"
        | extend AppId = tostring(Property.newValue)
    )
  | mv-apply Property = AdditionalDetails on 
    (
        where Property.key =~ "User-Agent"
        | extend InitiatingUserAgent = replace('"', '', tostring(Property.value))
    )
  | project-away Property
  | parse Permissions with * "ConsentType: " GrantConsentType ", Scope: " GrantScope1 "," *
  | where GrantScope1 =~ "full_access_as_app"
  | 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))
  | project-reorder TimeGenerated, OAuthAppName, AppId, AdminConsent, Permissions, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, InitiatingUserAgent, GrantScope1, GrantConsentType
  | extend GrantInitiatedBy = tostring(iff(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName))
  | extend Name = split(InitiatingUserPrincipalName, "@")[0], UPNSuffix = split(InitiatingUserPrincipalName, "@")[1]
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: InitiatingUserPrincipalName
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: InitiatingAadUserId
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: InitiatingAppServicePrincipalId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: InitiatingIpAddress
customDetails:
  OAuthApplication: OAuthAppName
  OAuthAppId: AppId
  UserAgent: InitiatingUserAgent
alertDetailsOverride:
  alertDisplayNameFormat: User or App {{GrantInitiatedBy}} granted full_access_as_app to {{OAuthAppName}}
  alertDescriptionFormat: |
    This detection looks for the full_access_as_app permission being granted to an OAuth application with Admin Consent.
    This permission provide access to all Exchange mailboxes via the EWS API can could be exploited to access sensitive data 
    by being added to a compromised application. The application granted this permission should be reviewed to ensure that it 
    is absolutely necessary for the applications function.
    In this case {{GrantInitiatedBy}} granted full_access_as_app to {{OAuthAppName}} from {{InitiatingIpAddress}}
    Ref: https://learn.microsoft.com/graph/auth-limit-mailbox-access
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

AuditLogs

Stage 2: where

| where LoggedByService =~ "Core Directory"

Stage 3: where

| where Category =~ "ApplicationManagement"

Stage 4: where

| where OperationName =~ "Consent to application"

Stage 5: where

| where TargetResources has "full_access_as_app"

Stage 6: mv-expand

| mv-expand TargetResources

Stage 7: extend

| extend OAuthAppName = TargetResources.displayName

Stage 8: extend

| extend ModifiedProperties = TargetResources.modifiedProperties

Stage 9: kusto:mv-apply

| mv-apply Property = ModifiedProperties on 
  (
      where Property.displayName =~ "ConsentContext.isAdminConsent"
      | extend AdminConsent = tostring(Property.newValue)
  )

Stage 10: kusto:mv-apply

| mv-apply Property = ModifiedProperties on 
  (
      where Property.displayName =~ "ConsentAction.Permissions"
      | extend Permissions = tostring(Property.newValue)
  )

Stage 11: kusto:mv-apply

| mv-apply Property = ModifiedProperties on 
  (
      where Property.displayName =~ "TargetId.ServicePrincipalNames"
      | extend AppId = tostring(Property.newValue)
  )

Stage 12: kusto:mv-apply

| mv-apply Property = AdditionalDetails on 
  (
      where Property.key =~ "User-Agent"
      | extend InitiatingUserAgent = replace('"', '', tostring(Property.value))
  )

Stage 13: project-away

| project-away Property

Stage 14: parse

| parse Permissions with * "ConsentType: " GrantConsentType ", Scope: " GrantScope1 "," *

Stage 15: where

| where GrantScope1 =~ "full_access_as_app"

Stage 16: extend (5 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))

Stage 17: project-reorder

| project-reorder TimeGenerated, OAuthAppName, AppId, AdminConsent, Permissions, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, InitiatingUserAgent, GrantScope1, GrantConsentType

Stage 18: extend

| extend GrantInitiatedBy = tostring(iff(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName))

Stage 19: extend

| extend Name = split(InitiatingUserPrincipalName, "@")[0], UPNSuffix = split(InitiatingUserPrincipalName, "@")[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
  • ApplicationManagement
GrantScope1eq
  • full_access_as_app
LoggedByServiceeq
  • Core Directory
OperationNameeq
  • Consent to application
TargetResourcesmatch
  • full_access_as_app transforms: term
displayNameeq
  • ConsentAction.Permissions
  • ConsentContext.isAdminConsent
  • TargetId.ServicePrincipalNames
keyeq
  • User-Agent

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
OAuthAppNameextend
ModifiedPropertiesextend
InitiatingAppNameextend
InitiatingAppServicePrincipalIdextend
InitiatingUserPrincipalNameextend
InitiatingAadUserIdextend
InitiatingIpAddressextend
GrantInitiatedByextend
Nameextend
UPNSuffixextend