Adversaries may modify the application logout URL to intercept session termination and maintain persistence within the environment. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential session hijacking or persistence mechanisms that could evade standard detection methods.
KQL Query
AuditLogs
| where Category =~ "ApplicationManagement"
| where OperationName has_any ("Update Application", "Update Service principal")
| 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(InitiatedBy.user.ipAddress)
| extend TargetAppName = tostring(TargetResources[0].displayName)
| extend mod_props = TargetResources[0].modifiedProperties
| mv-expand mod_props
| extend Action = tostring(mod_props.displayName)
| where Action contains "Url"
| extend UpdatedBy = iif(isnotempty(InitiatingAppName), InitiatingAppName, InitiatingUserPrincipalName)
| extend OldURL = tostring(mod_props.oldValue)
| extend NewURL = tostring(mod_props.newValue)
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
| project-reorder TimeGenerated, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingAadUserId, InitiatingUserPrincipalName, InitiatingIPAddress, UpdatedBy
id: 492fbe35-cbac-4a8c-9059-826782e6915a
name: Changes to Application Logout URL
description: |
'Detects changes to an applications sign out URL.
Look for any modifications to a sign out URL. Blank entries or entries to non-existent locations would stop a user from terminating a session.
Ref: https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-applications#logout-url-modified-or-removed'
severity: Low
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1078.004
tags:
- AADSecOpsGuide
query: |
AuditLogs
| where Category =~ "ApplicationManagement"
| where OperationName has_any ("Update Application", "Update Service principal")
| 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(InitiatedBy.user.ipAddress)
| extend TargetAppName = tostring(TargetResources[0].displayName)
| extend mod_props = TargetResources[0].modifiedProperties
| mv-expand mod_props
| extend Action = tostring(mod_props.displayName)
| where Action contains "Url"
| extend UpdatedBy = iif(isnotempty(InitiatingAppName), InitiatingAppName, InitiatingUserPrincipalName)
| extend OldURL = tostring(mod_props.oldValue)
| extend NewURL = tostring(mod_props.newValue)
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
| project-reorder TimeGenerated, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingAadUserId, InitiatingUserPrincipalName, InitiatingIPAddress, UpdatedBy
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: InitiatingUserPrincipalName
- identifier: Name
columnName: InitiatingAccountName
- identifier: UPNSuffix
columnName: InitiatingAccountUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: InitiatingAadUserId
- entityType: URL
fieldMappings:
- identifier: Url
columnName: OldURL
- entityType: URL
fieldMappings:
- identifier: Url
columnName: NewURL
- entityType: IP
fieldMappings:
- identifier: Address
columnName: InitiatingIPAddress
version: 1.1.1
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Others" ]
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
Scenario: Scheduled Job Updates URL Configuration
Description: A scheduled job runs daily to update the logout URL in a configuration file (e.g., via Ansible or Puppet) as part of a routine maintenance task.
Filter/Exclusion: Exclude changes made by known configuration management tools (e.g., ansible, puppet) or by specific scheduled job names (e.g., daily_config_update).
Scenario: Admin Task to Redirect Users to a New Logout Page
Description: An admin manually updates the logout URL in a web application (e.g., via Apache or Nginx config files) to redirect users to a new internal portal.
Filter/Exclusion: Exclude changes made by admin users with specific roles (e.g., admin_user, security_admin) or from specific IP ranges (e.g., 10.0.0.0/24).
Scenario: CI/CD Pipeline Deploys New Configuration
Description: A CI/CD pipeline (e.g., Jenkins, GitLab CI) deploys a new version of an application that includes a modified logout URL.
Filter/Exclusion: Exclude changes made by CI/CD tools (e.g., jenkins, gitlab-ci) or during deployment pipelines (e.g., deploy_prod).
Scenario: User-Driven URL Change via Admin Console
Description: An admin uses the application’s management console (e.g., Okta, Azure AD) to update the logout URL for compliance or branding purposes.
Filter/Exclusion: Exclude changes made from known admin consoles (e.g., okta_admin, azure_portal) or by users with admin privileges (e.g., user_role=admin).
Scenario: Temporary URL Change for Testing Purposes
Description: A developer temporarily modifies the logout URL