Query Details

Copilot Config To Abuse Chain

Query

id: 6f8e1a5c-0d7f-4e9f-c3a4-1b0f9e8d7c63
name: Microsoft 365 Copilot - Config change followed by abuse signal
description: |
  Correlates a Copilot administrative configuration change (settings,
  plugin, prompt-book) with subsequent abuse-class signals (Prompt
  Shield jailbreak verdict, XPIA / indirect prompt injection verdict,
  or DLP policy hit) by the same actor or on the same agent within a
  24-hour window.

  This is the chained kill-chain shape: an admin (or a compromised
  account) widens the attack surface, then either the same actor or
  any user of the changed agent immediately exercises it. Either side
  alone is already covered by CopilotSystemPromptOverride,
  CopilotJailbreakAttemptDetection, CopilotIndirectPromptInjection
  and CopilotSensitiveDataExposure; this rule surfaces only the chain.

  Limitation: the correlation key is AgentId where available on the
  config event, otherwise ActorUserId. If a plugin is installed
  tenant-wide and abused by a different actor on a different agent,
  this rule will not connect them.
severity: High
requiredDataConnectors:
- connectorId: MicrosoftCopilot
  dataTypes:
  - CopilotActivity
queryFrequency: PT1H
queryPeriod: P1D
triggerOperator: gt
triggerThreshold: 0
enabled: true
tactics:
- Persistence
- DefenseEvasion
- Execution
relevantTechniques:
- T1098
- T1556
- T1059
query: |
  let configRecordTypes = dynamic([
      "UpdateCopilotSettings",
      "CreateCopilotPlugin", "UpdateCopilotPlugin",
      "EnableCopilotPlugin", "DisableCopilotPlugin",
      "CreateCopilotPromptBook", "UpdateCopilotPromptBook",
      "DeleteCopilotPromptBook"
  ]);
  let window = 1d;
  let recentWindow = 1h;
  let configEvents =
      CopilotActivity
      | where TimeGenerated > ago(window)
      | where RecordType in (configRecordTypes)
      | project
          ConfigTime = TimeGenerated,
          ConfigKind = RecordType,
          ConfigAgentId = AgentId,
          ConfigAgentName = AgentName,
          ConfigActor = ActorName,
          ConfigActorUserId = ActorUserId,
          TenantId;
  let abuseEvents =
      CopilotActivity
      | where TimeGenerated > ago(recentWindow)
      | where RecordType == "CopilotInteraction"
      | extend
          ThreadId = tostring(LLMEventData.ThreadId),
          DlpDeferred = tobool(LLMEventData.DLPEvaluationDeferred)
      | mv-expand r = LLMEventData.AccessedResources, m = LLMEventData.Messages
      | extend
          XpiaHit = tobool(r.XPIADetected),
          DlpHit = (isnotempty(tostring(r.PolicyDetails))
                    and tostring(r.PolicyDetails) !in ("[]", "{}", "null"))
                    or DlpDeferred,
          JbHit = tobool(m.JailbreakDetected)
      | where XpiaHit or DlpHit or JbHit
      | summarize
          AbuseTime = max(TimeGenerated),
          AbuseKinds = make_set(case(XpiaHit, "XPIA", DlpHit, "DLP", JbHit, "Jailbreak", ""), 4),
          AbuseThreads = make_set(ThreadId, 8),
          AbuseSites = make_set(tostring(r.SiteUrl), 16)
          by AgentId, AgentName, ActorName, ActorUserId, TenantId;
  abuseEvents
  | join kind=inner (
      configEvents
      | project-rename
          ConfigAgentIdJ = ConfigAgentId,
          ConfigActorUserIdJ = ConfigActorUserId
  ) on $left.TenantId == $right.TenantId
  | where ConfigTime <= AbuseTime
      and AbuseTime - ConfigTime <= window
      and (
          (isnotempty(AgentId) and AgentId == ConfigAgentIdJ)
          or (isnotempty(ActorUserId) and ActorUserId == ConfigActorUserIdJ)
      )
  | extend ChainGapMin = datetime_diff('minute', AbuseTime, ConfigTime)
  | project
      ConfigTime, ConfigKind, ConfigActor, ConfigAgentName,
      AbuseTime, AbuseKinds, AbuseThreads, AbuseSites,
      AgentId, AgentName, ActorName, ActorUserId,
      ChainGapMin, TenantId
  | order by AbuseTime desc, ChainGapMin asc
entityMappings:
- entityType: CloudApplication
  fieldMappings:
  - identifier: Name
    columnName: AgentName
  - identifier: AppId
    columnName: AgentId
- entityType: Account
  fieldMappings:
  - identifier: Name
    columnName: ActorName
eventGroupingSettings:
  aggregationKind: SingleAlert
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT24H
    matchingMethod: Selected
    groupByEntities:
    - CloudApplication
    - Account
    groupByAlertDetails: []
    groupByCustomDetails: []
version: 1.0.0
kind: Scheduled
tags:
- Sentinel-As-Code
- Custom
- Copilot
- AI

Explanation

This query is designed to detect potential security threats related to Microsoft 365 Copilot by identifying a sequence of events that could indicate malicious activity. Here's a simplified breakdown:

  1. Purpose: The query looks for a pattern where an administrative change to Copilot's configuration (like settings, plugins, or prompt-books) is followed by suspicious activities (such as jailbreak attempts, indirect prompt injections, or data loss prevention policy hits) within a 24-hour period.

  2. How It Works:

    • It first identifies configuration changes made to Copilot.
    • Then, it checks for any abuse signals (suspicious activities) that occur after these changes within the same day.
    • The query correlates these events based on the same user or agent (device) to see if the same actor or another user on the same agent is exploiting the changes.
  3. Limitations: The correlation is primarily based on the agent ID or user ID. If a plugin is installed across the entire organization and abused by a different user on another device, this query might not catch it.

  4. Severity and Tactics: The alert generated by this query is considered high severity and is associated with tactics like persistence, defense evasion, and execution.

  5. Technical Details:

    • The query runs every hour and looks back over the past day for relevant events.
    • It uses specific types of data from Microsoft Copilot activity logs.
    • If it finds a match, it creates an incident for further investigation.
  6. Output: The query provides details about the configuration change and the subsequent abuse, including the time gap between them, and orders the results by the time of abuse.

This setup helps security teams quickly identify and respond to potential security incidents involving Microsoft 365 Copilot.

Details

David Alonso profile picture

David Alonso

Released: May 20, 2026

Tables

CopilotActivity

Keywords

MicrosoftCopilotActivityAgentActorTenantCloudApplicationAccountSentinelAsCodeCustomAI

Operators

letdynamicagoinprojectwhereextendmv-expandtoboolisnotemptytostringcasesummarizemake_setbyjoinkindonproject-renamedatetime_difforder by

Actions