Microsoft 365 Copilot - Config change followed by abuse signal
Copilot Config To Abuse Chain
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 ascExplanation
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:
-
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.
-
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.
-
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.
-
Severity and Tactics: The alert generated by this query is considered high severity and is associated with tactics like persistence, defense evasion, and execution.
-
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.
-
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
Released: May 20, 2026
Tables
Keywords
Operators
Severity
HighTactics
Frequency: PT1H
Period: P1D