← Back to SOC feed Coverage →

AI Agents - Sharing expanded to organization-wide

kql MEDIUM Azure-Sentinel
IdentityInfo
huntingmicrosoftofficial
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-07-20T23:00:00Z · Confidence: medium

Hunt Hypothesis

This hunt detects adversaries who have escalated the scope of previously restricted AI agents to organization-wide access, potentially leveraging their integrated MCP servers and declared tools to exfiltrate sensitive data or execute unauthorized actions across the tenant. Proactively hunting for this behavior in Azure Sentinel is critical because such rapid permission expansion often bypasses standard change management controls, creating a high-risk window where compromised agents can silently pivot laterally before detection.

KQL Query

let lookback = 14d;
let recent = 2d;
let IdentityIdtoUPN = materialize(
    IdentityInfo
    | extend ResolvedAccountUpn = tostring(column_ifexists("AccountUpn", column_ifexists("AccountUPN", ""))),
             IdentityTimestamp = todatetime(column_ifexists("Timestamp", column_ifexists("TimeGenerated", datetime(null))))
    | where IdentityTimestamp >= ago(lookback)
    | where isnotempty(AccountObjectId) and isnotempty(ResolvedAccountUpn)
    | summarize arg_max(IdentityTimestamp, ResolvedAccountUpn) by AccountObjectId
    | project AccountObjectId = tostring(AccountObjectId), AccountUpn = ResolvedAccountUpn);
let CurrentState =
    AgentsInfo
    | where Timestamp > ago(recent)
    | summarize arg_max(Timestamp, *) by AgentId
    | where LifecycleStatus != "Deleted"
    | where set_has_element(coalesce(SharedWith, dynamic([])), "*")
    | extend McpServerCount = array_length(coalesce(McpServers, dynamic([]))),
             DeclaredToolCount = array_length(coalesce(DeclaredTools, dynamic([])))
    | project AgentId, Timestamp, Name, Platform, CreatedDateTime, Owners,
              SharedWith, McpServerCount, DeclaredToolCount;
let BaselineState =
    AgentsInfo
    | where Timestamp between (ago(lookback) .. ago(recent))
    | where LifecycleStatus != "Deleted"
    | summarize arg_max(Timestamp, *) by AgentId
    | where not(set_has_element(coalesce(SharedWith, dynamic([])), "*"))
    | project AgentId, PreviousTimestamp = Timestamp, PreviousSharedWith = SharedWith;
CurrentState
| join kind=inner BaselineState on AgentId
| extend HasElevatedCapabilities = McpServerCount > 0 or DeclaredToolCount > 0
| extend OwnerIds = iff(array_length(coalesce(Owners, dynamic([]))) > 0, Owners, dynamic([""]))
| mv-expand OwnerId = OwnerIds to typeof(string)
| join kind=leftouter IdentityIdtoUPN on $left.OwnerId == $right.AccountObjectId
| project-rename OwnerUpn = AccountUpn
| extend OwnerAccountName = tostring(split(OwnerUpn, "@")[0]),
         OwnerAccountUPNSuffix = tostring(split(OwnerUpn, "@")[1])
| project Timestamp, PreviousTimestamp, AgentId, Name, Platform, CreatedDateTime,
         PreviousSharedWith, SharedWith, McpServerCount, DeclaredToolCount,
         HasElevatedCapabilities, OwnerId, OwnerUpn, OwnerAccountName, OwnerAccountUPNSuffix
| sort by HasElevatedCapabilities desc, Timestamp desc

Analytic Rule Definition

id: dbdba9cc-d7a0-434d-9c3d-82d2203a79fd
name: AI Agents - Sharing expanded to organization-wide
description: |
  Identifies existing AI agents that were restricted in the baseline snapshot and are now shared organization-wide. Prioritize agents with MCP servers or declared tools for review. Run within 2 days of a change to retain coverage.
requiredDataConnectors: []
tactics: []
relevantTechniques: []
query: |
  let lookback = 14d;
  let recent = 2d;
  let IdentityIdtoUPN = materialize(
      IdentityInfo
      | extend ResolvedAccountUpn = tostring(column_ifexists("AccountUpn", column_ifexists("AccountUPN", ""))),
               IdentityTimestamp = todatetime(column_ifexists("Timestamp", column_ifexists("TimeGenerated", datetime(null))))
      | where IdentityTimestamp >= ago(lookback)
      | where isnotempty(AccountObjectId) and isnotempty(ResolvedAccountUpn)
      | summarize arg_max(IdentityTimestamp, ResolvedAccountUpn) by AccountObjectId
      | project AccountObjectId = tostring(AccountObjectId), AccountUpn = ResolvedAccountUpn);
  let CurrentState =
      AgentsInfo
      | where Timestamp > ago(recent)
      | summarize arg_max(Timestamp, *) by AgentId
      | where LifecycleStatus != "Deleted"
      | where set_has_element(coalesce(SharedWith, dynamic([])), "*")
      | extend McpServerCount = array_length(coalesce(McpServers, dynamic([]))),
               DeclaredToolCount = array_length(coalesce(DeclaredTools, dynamic([])))
      | project AgentId, Timestamp, Name, Platform, CreatedDateTime, Owners,
                SharedWith, McpServerCount, DeclaredToolCount;
  let BaselineState =
      AgentsInfo
      | where Timestamp between (ago(lookback) .. ago(recent))
      | where LifecycleStatus != "Deleted"
      | summarize arg_max(Timestamp, *) by AgentId
      | where not(set_has_element(coalesce(SharedWith, dynamic([])), "*"))
      | project AgentId, PreviousTimestamp = Timestamp, PreviousSharedWith = SharedWith;
  CurrentState
  | join kind=inner BaselineState on AgentId
  | extend HasElevatedCapabilities = McpServerCount > 0 or DeclaredToolCount > 0
  | extend OwnerIds = iff(array_length(coalesce(Owners, dynamic([]))) > 0, Owners, dynamic([""]))
  | mv-expand OwnerId = OwnerIds to typeof(string)
  | join kind=leftouter IdentityIdtoUPN on $left.OwnerId == $right.AccountObjectId
  | project-rename OwnerUpn = AccountUpn
  | extend OwnerAccountName = tostring(split(OwnerUpn, "@")[0]),
           OwnerAccountUPNSuffix = tostring(split(OwnerUpn, "@")[1])
  | project Timestamp, PreviousTimestamp, AgentId, Name, Platform, CreatedDateTime,
           PreviousSharedWith, SharedWith, McpServerCount, DeclaredToolCount,
           HasElevatedCapabilities, OwnerId, OwnerUpn, OwnerAccountName, OwnerAccountUPNSuffix
  | sort by HasElevatedCapabilities desc, Timestamp desc
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: OwnerAccountName
      - identifier: UPNSuffix
        columnName: OwnerA

Required Data Sources

Sentinel TableNotes
IdentityInfoEnsure this data connector is enabled

References

False Positive Guidance

Here are 5 specific false positive scenarios for the “AI Agents - Sharing expanded to organization-wide” detection rule, including suggested filters and exclusions:

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/AI Agents/AgentsInfoSharingExpandedToOrgWide.yaml