URL Added to Application from Unknown Domain
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://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-applications#application-configuration-changes |
Severity | High |
Tactics | Persistence PrivilegeEscalation |
Techniques | T1078.004 |
Required data connectors | AzureActiveDirectory |
Kind | Scheduled |
Query frequency | 1d |
Query period | 1d |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/URLAddedtoApplicationfromUnknownDomain.yaml |
Version | 1.0.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 AddingUser = iif(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)) , tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName), "")
| extend AddingApp = iif(isnotempty(tostring(parse_json(tostring(InitiatedBy.app)).servicePrincipalName)) , tostring(parse_json(tostring(InitiatedBy.app)).servicePrincipalName), "")
| extend AddedBy = iif(isnotempty(AddingUser), AddingUser, AddingApp)
| project-away AddingApp, AddingUser
| extend AppDisplayName = tostring(TargetResources.displayName)
| extend ipAddress = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
| where isnotempty(AddedUrls)
| mv-expand AddedUrls
| extend Domain = extract("^(?:https?:\\/\\/)?(?:[^@\\/\\n]+@)?(?:www\\.)?([^:\\/?\\n]+)/", 1, replace_string(tolower(tostring(AddedUrls)), '"', ""))
| where isnotempty(Domain)
| extend Domain = strcat(split(Domain, ".")[-2], ".", split(Domain, ".")[-1])
| where Domain !in (domains)
| project-reorder TimeGenerated, AppDisplayName, AddedUrls, AddedBy, UserAgent, ipAddress
severity: High
triggerThreshold: 0
metadata:
source:
kind: Community
support:
tier: Community
categories:
domains:
- Security - Others
author:
name: Pete Bryan
queryFrequency: 1d
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
id: 017e095a-94d8-430c-a047-e51a11fb737b
version: 1.0.1
name: URL Added to Application from Unknown Domain
kind: Scheduled
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 AddingUser = iif(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)) , tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName), "")
| extend AddingApp = iif(isnotempty(tostring(parse_json(tostring(InitiatedBy.app)).servicePrincipalName)) , tostring(parse_json(tostring(InitiatedBy.app)).servicePrincipalName), "")
| extend AddedBy = iif(isnotempty(AddingUser), AddingUser, AddingApp)
| project-away AddingApp, AddingUser
| extend AppDisplayName = tostring(TargetResources.displayName)
| extend ipAddress = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
| where isnotempty(AddedUrls)
| mv-expand AddedUrls
| extend Domain = extract("^(?:https?:\\/\\/)?(?:[^@\\/\\n]+@)?(?:www\\.)?([^:\\/?\\n]+)/", 1, replace_string(tolower(tostring(AddedUrls)), '"', ""))
| where isnotempty(Domain)
| extend Domain = strcat(split(Domain, ".")[-2], ".", split(Domain, ".")[-1])
| where Domain !in (domains)
| project-reorder TimeGenerated, AppDisplayName, AddedUrls, AddedBy, UserAgent, ipAddress
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/URLAddedtoApplicationfromUnknownDomain.yaml
queryPeriod: 1d
relevantTechniques:
- T1078.004
triggerOperator: gt
tactics:
- Persistence
- PrivilegeEscalation
tags:
- AADSecOpsGuide
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://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-applications#application-configuration-changes'
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: AddedUrls
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AddedBy
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ipAddress
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/017e095a-94d8-430c-a047-e51a11fb737b')]",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/017e095a-94d8-430c-a047-e51a11fb737b')]",
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
"kind": "Scheduled",
"apiVersion": "2022-11-01",
"properties": {
"displayName": "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.\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://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-applications#application-configuration-changes'\n",
"severity": "High",
"enabled": true,
"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 AddingUser = iif(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)) , tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName), \"\")\n | extend AddingApp = iif(isnotempty(tostring(parse_json(tostring(InitiatedBy.app)).servicePrincipalName)) , tostring(parse_json(tostring(InitiatedBy.app)).servicePrincipalName), \"\")\n | extend AddedBy = iif(isnotempty(AddingUser), AddingUser, AddingApp)\n | project-away AddingApp, AddingUser\n | extend AppDisplayName = tostring(TargetResources.displayName)\n | extend ipAddress = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)\n | where isnotempty(AddedUrls)\n | mv-expand AddedUrls\n | extend Domain = extract(\"^(?:https?:\\\\/\\\\/)?(?:[^@\\\\/\\\\n]+@)?(?:www\\\\.)?([^:\\\\/?\\\\n]+)/\", 1, replace_string(tolower(tostring(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, AddedBy, UserAgent, ipAddress\n",
"queryFrequency": "P1D",
"queryPeriod": "P1D",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0,
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"Persistence",
"PrivilegeEscalation"
],
"techniques": [
"T1078.004"
],
"alertRuleTemplateName": "017e095a-94d8-430c-a047-e51a11fb737b",
"customDetails": null,
"entityMappings": [
{
"fieldMappings": [
{
"columnName": "AddedUrls",
"identifier": "Url"
}
],
"entityType": "URL"
},
{
"fieldMappings": [
{
"columnName": "AddedBy",
"identifier": "FullName"
}
],
"entityType": "Account"
},
{
"fieldMappings": [
{
"columnName": "ipAddress",
"identifier": "Address"
}
],
"entityType": "IP"
}
],
"tags": [
"AADSecOpsGuide"
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/URLAddedtoApplicationfromUnknownDomain.yaml",
"templateVersion": "1.0.1"
}
}
]
}