Analytic rule catalog
URL Added to Application from Unknown Domain
Back
| Id | 017e095a-94d8-430c-a047-e51a11fb737b |
| Rulename | URL Added to Application from Unknown Domain |
| Description | Detects a URL being added to an application where the domain is not one that is associated with the tenant. The query uses domains seen in sign in logs to determine if the domain is associated with the tenant. Applications associated with URLs not controlled by the organization can pose a security risk. Ref: https://learn.microsoft.com/en-gb/entra/architecture/security-operations-applications#application-configuration-changes |
| Severity | High |
| Tactics | Persistence PrivilegeEscalation |
| Techniques | T1078.004 |
| Required data connectors | AzureActiveDirectory |
| Kind | Scheduled |
| Query frequency | 2h |
| Query period | 2h |
| Trigger threshold | 0 |
| Trigger operator | gt |
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/URLAddedtoApplicationfromUnknownDomain.yaml |
| Version | 1.1.1 |
| Arm template | 017e095a-94d8-430c-a047-e51a11fb737b.json |
let domains =
SigninLogs
| where ResultType == 0
| extend domain = split(UserPrincipalName, "@")[1]
| extend domain = tostring(split(UserPrincipalName, "@")[1])
| summarize by tolower(tostring(domain));
AuditLogs
| where Category =~ "ApplicationManagement"
| where Result =~ "success"
| where OperationName =~ 'Update Application'
| mv-expand TargetResources
| mv-expand TargetResources.modifiedProperties
| where TargetResources_modifiedProperties.displayName =~ "AppAddress"
| extend Key = tostring(TargetResources_modifiedProperties.displayName)
| extend NewValue = TargetResources_modifiedProperties.newValue
| extend OldValue = TargetResources_modifiedProperties.oldValue
| where isnotempty(Key) and isnotempty(NewValue)
| project-reorder Key, NewValue, OldValue
| extend NewUrls = extract_all('"Address":([^,]*)', tostring(NewValue))
| extend OldUrls = extract_all('"Address":([^,]*)', tostring(OldValue))
| extend AddedUrls = set_difference(NewUrls, OldUrls)
| where array_length(AddedUrls) > 0
| extend UserAgent = iif(tostring(AdditionalDetails[0].key) == "User-Agent", tostring(AdditionalDetails[0].value), "")
| 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 InitiatedBy = tostring(iff(isnotempty(InitiatingUserPrincipalName),InitiatingUserPrincipalName, InitiatingAppName))
| extend AppDisplayName = tostring(TargetResources.displayName)
| where isnotempty(AddedUrls)
| mv-expand AddedUrls
| extend AddedUrls = trim(@'"', tostring(AddedUrls))
| extend Domain = extract("^(?:https?:\\/\\/)?(?:[^@\\/\\n]+@)?(?:www\\.)?([^:\\/?\\n]+)/", 1, replace_string(tolower(AddedUrls), '"', ""))
| where isnotempty(Domain)
| extend Domain = strcat(split(Domain, ".")[-2], ".", split(Domain, ".")[-1])
| where Domain !in (domains)
| project-reorder TimeGenerated, AppDisplayName, AddedUrls, InitiatedBy, UserAgent, InitiatingIPAddress
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
name: URL Added to Application from Unknown Domain
triggerOperator: gt
query: |
let domains =
SigninLogs
| where ResultType == 0
| extend domain = split(UserPrincipalName, "@")[1]
| extend domain = tostring(split(UserPrincipalName, "@")[1])
| summarize by tolower(tostring(domain));
AuditLogs
| where Category =~ "ApplicationManagement"
| where Result =~ "success"
| where OperationName =~ 'Update Application'
| mv-expand TargetResources
| mv-expand TargetResources.modifiedProperties
| where TargetResources_modifiedProperties.displayName =~ "AppAddress"
| extend Key = tostring(TargetResources_modifiedProperties.displayName)
| extend NewValue = TargetResources_modifiedProperties.newValue
| extend OldValue = TargetResources_modifiedProperties.oldValue
| where isnotempty(Key) and isnotempty(NewValue)
| project-reorder Key, NewValue, OldValue
| extend NewUrls = extract_all('"Address":([^,]*)', tostring(NewValue))
| extend OldUrls = extract_all('"Address":([^,]*)', tostring(OldValue))
| extend AddedUrls = set_difference(NewUrls, OldUrls)
| where array_length(AddedUrls) > 0
| extend UserAgent = iif(tostring(AdditionalDetails[0].key) == "User-Agent", tostring(AdditionalDetails[0].value), "")
| 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 InitiatedBy = tostring(iff(isnotempty(InitiatingUserPrincipalName),InitiatingUserPrincipalName, InitiatingAppName))
| extend AppDisplayName = tostring(TargetResources.displayName)
| where isnotempty(AddedUrls)
| mv-expand AddedUrls
| extend AddedUrls = trim(@'"', tostring(AddedUrls))
| extend Domain = extract("^(?:https?:\\/\\/)?(?:[^@\\/\\n]+@)?(?:www\\.)?([^:\\/?\\n]+)/", 1, replace_string(tolower(AddedUrls), '"', ""))
| where isnotempty(Domain)
| extend Domain = strcat(split(Domain, ".")[-2], ".", split(Domain, ".")[-1])
| where Domain !in (domains)
| project-reorder TimeGenerated, AppDisplayName, AddedUrls, InitiatedBy, UserAgent, InitiatingIPAddress
| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
queryFrequency: 2h
description: |
'Detects a URL being added to an application where the domain is not one that is associated with the tenant.
The query uses domains seen in sign in logs to determine if the domain is associated with the tenant.
Applications associated with URLs not controlled by the organization can pose a security risk.
Ref: https://learn.microsoft.com/en-gb/entra/architecture/security-operations-applications#application-configuration-changes'
id: 017e095a-94d8-430c-a047-e51a11fb737b
triggerThreshold: 0
queryPeriod: 2h
version: 1.1.1
kind: Scheduled
relevantTechniques:
- T1078.004
tags:
- AADSecOpsGuide
severity: High
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
metadata:
categories:
domains:
- Security - Others
support:
tier: Community
author:
name: Microsoft Security Research
source:
kind: Community
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/URLAddedtoApplicationfromUnknownDomain.yaml
tactics:
- Persistence
- PrivilegeEscalation
entityMappings:
- fieldMappings:
- identifier: Url
columnName: AddedUrls
entityType: URL
- 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: Account
- fieldMappings:
- identifier: Address
columnName: InitiatingIPAddress
entityType: IP
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"apiVersion": "2024-01-01-preview",
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/017e095a-94d8-430c-a047-e51a11fb737b')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/017e095a-94d8-430c-a047-e51a11fb737b')]",
"properties": {
"alertRuleTemplateName": "017e095a-94d8-430c-a047-e51a11fb737b",
"customDetails": null,
"description": "'Detects a URL being added to an application where the domain is not one that is associated with the tenant.\n The query uses domains seen in sign in logs to determine if the domain is associated with the tenant.\n Applications associated with URLs not controlled by the organization can pose a security risk.\n Ref: https://learn.microsoft.com/en-gb/entra/architecture/security-operations-applications#application-configuration-changes'\n",
"displayName": "URL Added to Application from Unknown Domain",
"enabled": true,
"entityMappings": [
{
"entityType": "URL",
"fieldMappings": [
{
"columnName": "AddedUrls",
"identifier": "Url"
}
]
},
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "InitiatingAppName",
"identifier": "Name"
},
{
"columnName": "InitiatingAppServicePrincipalId",
"identifier": "AadUserId"
}
]
},
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "InitiatingUserPrincipalName",
"identifier": "FullName"
},
{
"columnName": "InitiatingAccountName",
"identifier": "Name"
},
{
"columnName": "InitiatingAccountUPNSuffix",
"identifier": "UPNSuffix"
}
]
},
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "InitiatingAadUserId",
"identifier": "AadUserId"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "InitiatingIPAddress",
"identifier": "Address"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/URLAddedtoApplicationfromUnknownDomain.yaml",
"query": "let domains =\n SigninLogs\n | where ResultType == 0\n | extend domain = split(UserPrincipalName, \"@\")[1]\n | extend domain = tostring(split(UserPrincipalName, \"@\")[1])\n | summarize by tolower(tostring(domain));\n AuditLogs\n | where Category =~ \"ApplicationManagement\"\n | where Result =~ \"success\"\n | where OperationName =~ 'Update Application'\n | mv-expand TargetResources\n | mv-expand TargetResources.modifiedProperties\n | where TargetResources_modifiedProperties.displayName =~ \"AppAddress\"\n | extend Key = tostring(TargetResources_modifiedProperties.displayName)\n | extend NewValue = TargetResources_modifiedProperties.newValue\n | extend OldValue = TargetResources_modifiedProperties.oldValue\n | where isnotempty(Key) and isnotempty(NewValue)\n | project-reorder Key, NewValue, OldValue\n | extend NewUrls = extract_all('\"Address\":([^,]*)', tostring(NewValue))\n | extend OldUrls = extract_all('\"Address\":([^,]*)', tostring(OldValue))\n | extend AddedUrls = set_difference(NewUrls, OldUrls)\n | where array_length(AddedUrls) > 0\n | extend UserAgent = iif(tostring(AdditionalDetails[0].key) == \"User-Agent\", tostring(AdditionalDetails[0].value), \"\")\n | extend InitiatingAppName = tostring(InitiatedBy.app.displayName)\n | extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)\n | extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)\n | extend InitiatingAadUserId = tostring(InitiatedBy.user.id)\n | extend InitiatingIPAddress = tostring(InitiatedBy.user.ipAddress)\n | extend InitiatedBy = tostring(iff(isnotempty(InitiatingUserPrincipalName),InitiatingUserPrincipalName, InitiatingAppName))\n | extend AppDisplayName = tostring(TargetResources.displayName)\n | where isnotempty(AddedUrls)\n | mv-expand AddedUrls\n | extend AddedUrls = trim(@'\"', tostring(AddedUrls))\n | extend Domain = extract(\"^(?:https?:\\\\/\\\\/)?(?:[^@\\\\/\\\\n]+@)?(?:www\\\\.)?([^:\\\\/?\\\\n]+)/\", 1, replace_string(tolower(AddedUrls), '\"', \"\"))\n | where isnotempty(Domain)\n | extend Domain = strcat(split(Domain, \".\")[-2], \".\", split(Domain, \".\")[-1])\n | where Domain !in (domains)\n | project-reorder TimeGenerated, AppDisplayName, AddedUrls, InitiatedBy, UserAgent, InitiatingIPAddress\n | extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, \"@\")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, \"@\")[1])\n",
"queryFrequency": "PT2H",
"queryPeriod": "PT2H",
"severity": "High",
"subTechniques": [
"T1078.004"
],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"Persistence",
"PrivilegeEscalation"
],
"tags": [
"AADSecOpsGuide"
],
"techniques": [
"T1078"
],
"templateVersion": "1.1.1",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}