Query Details

Microsoft 365 Copilot - Plugin or connector install anomaly

Copilot Plugin Install Anomaly

Query

let lookback = 30d;
let recentWindow = 1d;
let pluginEvents =
    union isfuzzy=true
        (AuditLogs
         | extend
             _OperationName = tostring(OperationName),
             _RecordType    = "AuditLogs",
             _Initiator     = tolower(tostring(InitiatedBy.user.userPrincipalName)),
             _TargetName    = tostring(TargetResources[0].displayName)),
        (OfficeActivity
         | extend
             _OperationName = tostring(Operation),
             _RecordType    = tostring(RecordType),
             _Initiator     = tolower(tostring(UserId)),
             _TargetName    = tostring(coalesce(column_ifexists('ItemName', ''), Operation))),
        (CopilotAdminActivity
         | extend
             _OperationName = tostring(column_ifexists('OperationName', '')),
             _RecordType    = "CopilotAdminActivity",
             _Initiator     = tolower(tostring(coalesce(column_ifexists('ActorUPN', ''),
                                                        column_ifexists('ActorName', '')))),
             _TargetName    = tostring(coalesce(column_ifexists('PluginName', ''),
                                                column_ifexists('ResourceName', ''))))
    | where TimeGenerated > ago(lookback)
    | where (_OperationName has_any ("Install", "Add", "Enable")
             and _OperationName has_any ("Plugin", "Connector", "Agent", "App", "Extension"))
            or _RecordType in ("MicrosoftCopilotAdminAction", "CopilotAdminActivity")
    | project TimeGenerated, ActorUpn = _Initiator, PluginName = _TargetName,
              LowerPlugin = tolower(_TargetName), _OperationName, _RecordType;
let approved =
    _GetWatchlist('CopilotApprovedPlugins')
    | project ApprovedPlugin = tolower(tostring(column_ifexists('PluginName', '')))
    | where isnotempty(ApprovedPlugin);
let baseline =
    pluginEvents
    | where TimeGenerated between (ago(lookback) .. ago(recentWindow))
    | summarize BaselineInstalls = count() by ActorUpn;
let recent =
    pluginEvents
    | where TimeGenerated > ago(recentWindow)
    | summarize
        RecentInstalls   = count(),
        DistinctPlugins  = dcount(LowerPlugin),
        PluginSample     = make_set(PluginName, 25)
        by ActorUpn;
recent
| join kind=leftouter baseline on ActorUpn
| extend BaselineInstalls = coalesce(BaselineInstalls, 0)
| extend SpikeRatio = iff(BaselineInstalls > 0,
                          todouble(RecentInstalls) / todouble(BaselineInstalls),
                          todouble(RecentInstalls))
| where (BaselineInstalls == 0 and RecentInstalls >= 3)
     or SpikeRatio >= 5.0
     or DistinctPlugins >= 5
| join kind=leftouter (
      pluginEvents
      | where TimeGenerated > ago(recentWindow)
      | join kind=leftanti approved on $left.LowerPlugin == $right.ApprovedPlugin
      | summarize UnapprovedPlugins = make_set(PluginName, 25) by ActorUpn
  ) on ActorUpn
| project TimeGenerated = now(), ActorUpn, RecentInstalls, BaselineInstalls,
          SpikeRatio, DistinctPlugins, PluginSample, UnapprovedPlugins
| order by SpikeRatio desc, RecentInstalls desc

Explanation

This query is designed to detect unusual activity related to the installation of Microsoft 365 Copilot plugins or connectors. Here's a simplified breakdown of what it does:

  1. Purpose: It looks for anomalies in the installation of plugins or connectors, specifically focusing on:

    • New installations by users who haven't installed any before.
    • Installations occurring at a much higher rate than usual.
    • Installations of plugins not on an approved list.
    • User-consented installations in environments requiring admin consent.
  2. Data Sources: It gathers data from various logs, including AuditLogs, OfficeActivity, and CopilotAdminActivity, to track plugin-related operations over the past 30 days.

  3. Analysis:

    • Baseline Calculation: It calculates a baseline of plugin installations by each user over a 30-day period, excluding the most recent day.
    • Recent Activity: It examines plugin installations in the last day to identify recent activity.
    • Anomaly Detection: It identifies anomalies by comparing recent activity against the baseline:
      • Users with no prior installations but at least 3 recent ones.
      • A spike in installation rate (5 times the baseline).
      • Installation of 5 or more different plugins.
    • Unapproved Plugins: It checks if any installed plugins are not on the approved list.
  4. Output: The query outputs a list of users with suspicious plugin installation activity, including details like the number of recent installations, baseline installations, spike ratio, distinct plugins installed, and any unapproved plugins.

  5. Security Context: The query is tagged with tactics and techniques related to persistence and privilege escalation, indicating its relevance to security monitoring and threat detection.

Overall, this query helps identify potential security risks associated with unauthorized or unusual plugin installations in Microsoft 365 Copilot.

Details

David Alonso profile picture

David Alonso

Released: May 20, 2026

Tables

AuditLogsOfficeActivityCopilotAdminActivity

Keywords

MicrosoftCopilotPluginConnectorAgentAppExtensionAuditLogsOfficeActivityCopilotAdminActivityUserPluginNameResourceNameMicrosoftCopilotAdminActionCopilotApprovedPluginsActorUpn

Operators

letunionisfuzzyextendtostringtolowercoalescecolumn_ifexistswherehas_anyinprojectagoisnotemptybetweensummarizecountdcountmake_setjoinkindleftouterleftantionifftodoubleorder bydesc

Tactics

PersistencePrivilegeEscalation

MITRE Techniques

Actions

GitHub