Suspicious modification of Global Administrator user properties
| Id | 48602a24-67cf-4362-b258-3f4249e55def | 
| Rulename | Suspicious modification of Global Administrator user properties | 
| Description | This query will detect if user properties of Global Administrator are updated by an existing user. Usually only user administrator or other global administrator can update such properties. Investigate if such user change is an attempt to elevate an existing low privileged identity or rogue administrator activity  | 
| Severity | Medium | 
| Tactics | PrivilegeEscalation | 
| Techniques | T1078.004 | 
| Required data connectors | AzureActiveDirectory BehaviorAnalytics  | 
| Kind | Scheduled | 
| Query frequency | 1h | 
| Query period | 14d | 
| Trigger threshold | 0 | 
| Trigger operator | gt | 
| Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Detections/MultipleDataSources/SuspiciousModificationofGlobalAdminProperties.yaml | 
| Version | 1.0.4 | 
| Arm template | 48602a24-67cf-4362-b258-3f4249e55def.json | 
let query_frequency = 1h;
let query_period = 14d;
IdentityInfo
| where TimeGenerated > ago(query_period)
| where set_has_element(AssignedRoles, "Global Administrator")
| distinct AccountUPN, AccountObjectId
| join kind=inner (
    AuditLogs
    | where TimeGenerated > ago(query_frequency)
    | where OperationName=~ "Update user" and Result =~ "success"
    // | where isnotempty(InitiatedBy["user"])
    | mv-expand TargetResource = TargetResources
    | where TargetResource["type"] == "User"
    | extend AccountObjectId = tostring(TargetResource["id"])
    | where tostring(TargetResource["modifiedProperties"]) != "[]"
    | mv-apply modifiedProperty = TargetResource["modifiedProperties"] on (
        summarize modifiedProperties = make_bag(
            bag_pack(tostring(modifiedProperty["displayName"]),
                bag_pack("oldValue", trim(@'[\"\s]+', tostring(modifiedProperty["oldValue"])),
                    "newValue", trim(@'[\"\s]+', tostring(modifiedProperty["newValue"])))))
    )
    | where not(tostring(modifiedProperties["Included Updated Properties"]["newValue"]) in ("LastDirSyncTime", ""))
    | where not(tostring(modifiedProperties["Included Updated Properties"]["newValue"]) == "StrongAuthenticationPhoneAppDetail" and isnotempty(modifiedProperties["StrongAuthenticationPhoneAppDetail"]) and tostring(array_sort_asc(extract_all(@'\"Id\"\:\"([^\"]+)\"', tostring(modifiedProperties["StrongAuthenticationPhoneAppDetail"]["newValue"])))) == tostring(array_sort_asc(extract_all(@'\"Id\"\:\"([^\"]+)\"', tostring(modifiedProperties["StrongAuthenticationPhoneAppDetail"]["oldValue"])))))
    | extend
        Initiator = iif(isnotempty(InitiatedBy["app"]), tostring(InitiatedBy["app"]["displayName"]), tostring(InitiatedBy["user"]["userPrincipalName"])),
        InitiatorId = iif(isnotempty(InitiatedBy["app"]), tostring(InitiatedBy["app"]["servicePrincipalId"]), tostring(InitiatedBy["user"]["id"])),
        IPAddress = tostring(InitiatedBy[tostring(bag_keys(InitiatedBy)[0])]["ipAddress"])
) on AccountObjectId
| project TimeGenerated, Category, Identity, Initiator, IPAddress, OperationName, Result, AccountUPN, InitiatedBy, AdditionalDetails, TargetResources, AccountObjectId, InitiatorId, CorrelationId
| extend
    InitiatorName = tostring(split(Initiator, "@")[0]),
    InitiatorUPNSuffix = tostring(split(Initiator, "@")[1]),
    AccountName = tostring(split(AccountUPN, "@")[0]),
    AccountUPNSuffix = tostring(split(AccountUPN, "@")[1])
kind: Scheduled
entityMappings:
- entityType: Account
  fieldMappings:
  - columnName: Initiator
    identifier: FullName
  - columnName: InitiatorName
    identifier: Name
  - columnName: InitiatorUPNSuffix
    identifier: UPNSuffix
- entityType: Account
  fieldMappings:
  - columnName: AccountUPN
    identifier: FullName
  - columnName: AccountName
    identifier: Name
  - columnName: AccountUPNSuffix
    identifier: UPNSuffix
- entityType: IP
  fieldMappings:
  - columnName: IPAddress
    identifier: Address
description: |
  'This query will detect if user properties of Global Administrator are updated by an existing user. Usually only user administrator or other global administrator can update such properties.
  Investigate if such user change is an attempt to elevate an existing low privileged identity or rogue administrator activity'  
severity: Medium
queryFrequency: 1h
triggerThreshold: 0
relevantTechniques:
- T1078.004
metadata:
  support:
    tier: Community
  source:
    kind: Community
  author:
    name: Microsoft Security Research
  categories:
    domains:
    - Security - Others
    - Identity
tactics:
- PrivilegeEscalation
name: Suspicious modification of Global Administrator user properties
id: 48602a24-67cf-4362-b258-3f4249e55def
query: |
  let query_frequency = 1h;
  let query_period = 14d;
  IdentityInfo
  | where TimeGenerated > ago(query_period)
  | where set_has_element(AssignedRoles, "Global Administrator")
  | distinct AccountUPN, AccountObjectId
  | join kind=inner (
      AuditLogs
      | where TimeGenerated > ago(query_frequency)
      | where OperationName=~ "Update user" and Result =~ "success"
      // | where isnotempty(InitiatedBy["user"])
      | mv-expand TargetResource = TargetResources
      | where TargetResource["type"] == "User"
      | extend AccountObjectId = tostring(TargetResource["id"])
      | where tostring(TargetResource["modifiedProperties"]) != "[]"
      | mv-apply modifiedProperty = TargetResource["modifiedProperties"] on (
          summarize modifiedProperties = make_bag(
              bag_pack(tostring(modifiedProperty["displayName"]),
                  bag_pack("oldValue", trim(@'[\"\s]+', tostring(modifiedProperty["oldValue"])),
                      "newValue", trim(@'[\"\s]+', tostring(modifiedProperty["newValue"])))))
      )
      | where not(tostring(modifiedProperties["Included Updated Properties"]["newValue"]) in ("LastDirSyncTime", ""))
      | where not(tostring(modifiedProperties["Included Updated Properties"]["newValue"]) == "StrongAuthenticationPhoneAppDetail" and isnotempty(modifiedProperties["StrongAuthenticationPhoneAppDetail"]) and tostring(array_sort_asc(extract_all(@'\"Id\"\:\"([^\"]+)\"', tostring(modifiedProperties["StrongAuthenticationPhoneAppDetail"]["newValue"])))) == tostring(array_sort_asc(extract_all(@'\"Id\"\:\"([^\"]+)\"', tostring(modifiedProperties["StrongAuthenticationPhoneAppDetail"]["oldValue"])))))
      | extend
          Initiator = iif(isnotempty(InitiatedBy["app"]), tostring(InitiatedBy["app"]["displayName"]), tostring(InitiatedBy["user"]["userPrincipalName"])),
          InitiatorId = iif(isnotempty(InitiatedBy["app"]), tostring(InitiatedBy["app"]["servicePrincipalId"]), tostring(InitiatedBy["user"]["id"])),
          IPAddress = tostring(InitiatedBy[tostring(bag_keys(InitiatedBy)[0])]["ipAddress"])
  ) on AccountObjectId
  | project TimeGenerated, Category, Identity, Initiator, IPAddress, OperationName, Result, AccountUPN, InitiatedBy, AdditionalDetails, TargetResources, AccountObjectId, InitiatorId, CorrelationId
  | extend
      InitiatorName = tostring(split(Initiator, "@")[0]),
      InitiatorUPNSuffix = tostring(split(Initiator, "@")[1]),
      AccountName = tostring(split(AccountUPN, "@")[0]),
      AccountUPNSuffix = tostring(split(AccountUPN, "@")[1])  
requiredDataConnectors:
- dataTypes:
  - AuditLogs
  connectorId: AzureActiveDirectory
- dataTypes:
  - IdentityInfo
  connectorId: BehaviorAnalytics
version: 1.0.4
triggerOperator: gt
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/MultipleDataSources/SuspiciousModificationofGlobalAdminProperties.yaml
queryPeriod: 14d
{
  "$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/48602a24-67cf-4362-b258-3f4249e55def')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/48602a24-67cf-4362-b258-3f4249e55def')]",
      "properties": {
        "alertRuleTemplateName": "48602a24-67cf-4362-b258-3f4249e55def",
        "customDetails": null,
        "description": "'This query will detect if user properties of Global Administrator are updated by an existing user. Usually only user administrator or other global administrator can update such properties.\nInvestigate if such user change is an attempt to elevate an existing low privileged identity or rogue administrator activity'\n",
        "displayName": "Suspicious modification of Global Administrator user properties",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "Initiator",
                "identifier": "FullName"
              },
              {
                "columnName": "InitiatorName",
                "identifier": "Name"
              },
              {
                "columnName": "InitiatorUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "AccountUPN",
                "identifier": "FullName"
              },
              {
                "columnName": "AccountName",
                "identifier": "Name"
              },
              {
                "columnName": "AccountUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "IPAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/MultipleDataSources/SuspiciousModificationofGlobalAdminProperties.yaml",
        "query": "let query_frequency = 1h;\nlet query_period = 14d;\nIdentityInfo\n| where TimeGenerated > ago(query_period)\n| where set_has_element(AssignedRoles, \"Global Administrator\")\n| distinct AccountUPN, AccountObjectId\n| join kind=inner (\n    AuditLogs\n    | where TimeGenerated > ago(query_frequency)\n    | where OperationName=~ \"Update user\" and Result =~ \"success\"\n    // | where isnotempty(InitiatedBy[\"user\"])\n    | mv-expand TargetResource = TargetResources\n    | where TargetResource[\"type\"] == \"User\"\n    | extend AccountObjectId = tostring(TargetResource[\"id\"])\n    | where tostring(TargetResource[\"modifiedProperties\"]) != \"[]\"\n    | mv-apply modifiedProperty = TargetResource[\"modifiedProperties\"] on (\n        summarize modifiedProperties = make_bag(\n            bag_pack(tostring(modifiedProperty[\"displayName\"]),\n                bag_pack(\"oldValue\", trim(@'[\\\"\\s]+', tostring(modifiedProperty[\"oldValue\"])),\n                    \"newValue\", trim(@'[\\\"\\s]+', tostring(modifiedProperty[\"newValue\"])))))\n    )\n    | where not(tostring(modifiedProperties[\"Included Updated Properties\"][\"newValue\"]) in (\"LastDirSyncTime\", \"\"))\n    | where not(tostring(modifiedProperties[\"Included Updated Properties\"][\"newValue\"]) == \"StrongAuthenticationPhoneAppDetail\" and isnotempty(modifiedProperties[\"StrongAuthenticationPhoneAppDetail\"]) and tostring(array_sort_asc(extract_all(@'\\\"Id\\\"\\:\\\"([^\\\"]+)\\\"', tostring(modifiedProperties[\"StrongAuthenticationPhoneAppDetail\"][\"newValue\"])))) == tostring(array_sort_asc(extract_all(@'\\\"Id\\\"\\:\\\"([^\\\"]+)\\\"', tostring(modifiedProperties[\"StrongAuthenticationPhoneAppDetail\"][\"oldValue\"])))))\n    | extend\n        Initiator = iif(isnotempty(InitiatedBy[\"app\"]), tostring(InitiatedBy[\"app\"][\"displayName\"]), tostring(InitiatedBy[\"user\"][\"userPrincipalName\"])),\n        InitiatorId = iif(isnotempty(InitiatedBy[\"app\"]), tostring(InitiatedBy[\"app\"][\"servicePrincipalId\"]), tostring(InitiatedBy[\"user\"][\"id\"])),\n        IPAddress = tostring(InitiatedBy[tostring(bag_keys(InitiatedBy)[0])][\"ipAddress\"])\n) on AccountObjectId\n| project TimeGenerated, Category, Identity, Initiator, IPAddress, OperationName, Result, AccountUPN, InitiatedBy, AdditionalDetails, TargetResources, AccountObjectId, InitiatorId, CorrelationId\n| extend\n    InitiatorName = tostring(split(Initiator, \"@\")[0]),\n    InitiatorUPNSuffix = tostring(split(Initiator, \"@\")[1]),\n    AccountName = tostring(split(AccountUPN, \"@\")[0]),\n    AccountUPNSuffix = tostring(split(AccountUPN, \"@\")[1])\n",
        "queryFrequency": "PT1H",
        "queryPeriod": "P14D",
        "severity": "Medium",
        "subTechniques": [
          "T1078.004"
        ],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "PrivilegeEscalation"
        ],
        "techniques": [
          "T1078"
        ],
        "templateVersion": "1.0.4",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}