Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

URL Added to Application from Unknown Domain

Back
Id017e095a-94d8-430c-a047-e51a11fb737b
RulenameURL Added to Application from Unknown Domain
DescriptionDetects 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
SeverityHigh
TacticsPersistence
PrivilegeEscalation
TechniquesT1078.004
Required data connectorsAzureActiveDirectory
KindScheduled
Query frequency2h
Query period2h
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/URLAddedtoApplicationfromUnknownDomain.yaml
Version1.1.1
Arm template017e095a-94d8-430c-a047-e51a11fb737b.json
Deploy To Azure
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])
entityMappings:
- entityType: URL
  fieldMappings:
  - identifier: Url
    columnName: AddedUrls
- 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
queryFrequency: 2h
name: URL Added to Application from Unknown Domain
severity: High
kind: Scheduled
tactics:
- Persistence
- PrivilegeEscalation
metadata:
  author:
    name: Microsoft Security Research
  source:
    kind: Community
  categories:
    domains:
    - Security - Others
  support:
    tier: Community
triggerThreshold: 0
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])  
triggerOperator: gt
queryPeriod: 2h
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/AuditLogs/URLAddedtoApplicationfromUnknownDomain.yaml
relevantTechniques:
- T1078.004
tags:
- AADSecOpsGuide
id: 017e095a-94d8-430c-a047-e51a11fb737b
requiredDataConnectors:
- connectorId: AzureActiveDirectory
  dataTypes:
  - AuditLogs
version: 1.1.1
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'  
{
  "$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"
    }
  ]
}