Query Details

Microsoft 365 Copilot - Agent memory or context poisoning persistence

Copilot Memory Context Poisoning

Query

let poisonTokens = dynamic([
    "ignore previous instructions", "ignore all prior",
    "you are now", "from now on you", "act as",
    "system override", "developer mode",
    "exfiltrate", "send to attacker", "leak to",
    "do not refuse", "bypass safety", "remove restrictions"
]);
let recentWindow = 7d;
// 1) Memory / instruction writes by non-owner actors
let writes =
    union isfuzzy=true
        (AuditLogs
         | extend
             _OperationName = tostring(OperationName),
             _Initiator     = tolower(tostring(InitiatedBy.user.userPrincipalName)),
             _TargetId      = tostring(TargetResources[0].id),
             _TargetName    = tostring(TargetResources[0].displayName),
             _OwnerUpn      = "",
             _Payload       = tolower(tostring(TargetResources[0].modifiedProperties))),
        (CopilotAdminActivity
         | extend
             _OperationName = tostring(column_ifexists('OperationName', '')),
             _Initiator     = tolower(tostring(coalesce(column_ifexists('ActorUPN', ''),
                                                        column_ifexists('ActorName', '')))),
             _TargetId      = tostring(column_ifexists('AgentId', '')),
             _TargetName    = tostring(column_ifexists('AgentName', '')),
             _OwnerUpn      = tolower(tostring(column_ifexists('AgentOwnerUPN', ''))),
             _Payload       = tolower(tostring(coalesce(column_ifexists('NewValue', ''),
                                                        column_ifexists('ModifiedProperties', '')))))
    | where TimeGenerated > ago(recentWindow)
    | where (_OperationName has_any ("Memory", "Instruction", "SystemPrompt",
                                     "CustomInstruction", "GroundingSource"))
            and (_OperationName has_any ("Update", "Set", "Add", "Write", "Modify"))
    | extend
        ActorUpn      = _Initiator,
        AgentId       = _TargetId,
        AgentName     = _TargetName,
        OwnerUpn      = _OwnerUpn,
        Payload       = _Payload,
        ByNonOwner    = isnotempty(_OwnerUpn) and _Initiator != _OwnerUpn,
        HasPoisonText = _Payload has_any (poisonTokens)
    | where ByNonOwner or HasPoisonText
    | project WriteTime = TimeGenerated, ActorUpn, AgentId, AgentName,
              OwnerUpn, ByNonOwner, HasPoisonText, OperationName = _OperationName, Payload;
// 2) Repeated suspicious phrase across distinct conversations of one agent
let carryOver =
    CopilotActivity
    | where TimeGenerated > ago(recentWindow)
    | where RecordType == "CopilotInteraction"
    | extend
        ResponseLower = tolower(tostring(LLMEventData.Response)),
        ConvId        = tostring(LLMEventData.ConversationId)
    | mv-apply tok = poisonTokens to typeof(string) on (
          where ResponseLower has tok
        | project Token = tok
      )
    | summarize
        DistinctConvs = dcount(ConvId),
        AffectedActors = make_set(tostring(coalesce(column_ifexists('ActorUPN', ''), ActorName)), 50),
        SampleConvs    = make_set(ConvId, 10),
        Hits           = count()
        by AgentId, AgentName, Token
    | where DistinctConvs >= 3;
union
    (writes | extend Source = 'instruction-write'),
    (carryOver | extend Source = 'cross-conversation-carryover',
                 WriteTime = now(),
                 ActorUpn = '<see AffectedActors>',
                 OwnerUpn = '',
                 ByNonOwner = false,
                 HasPoisonText = true,
                 OperationName = strcat('carryover token: ', Token),
                 Payload = strcat('hits=', Hits, ' convs=', DistinctConvs))
| project WriteTime, Source, AgentId, AgentName, ActorUpn, OwnerUpn,
          ByNonOwner, HasPoisonText, OperationName, Payload
| order by WriteTime desc

Explanation

This query is designed to detect potential "memory poisoning" in Microsoft 365 Copilot agents. Memory poisoning is a technique where an AI agent's memory or context is altered maliciously to influence its behavior in future interactions. Here's a simplified breakdown of what the query does:

  1. Identify Poisonous Tokens: It defines a list of suspicious phrases or tokens that might indicate an attempt to manipulate the agent's memory or instructions, such as "ignore previous instructions" or "bypass safety".

  2. Monitor Recent Activity: It looks at activities within the last seven days to find:

    • Unauthorized Memory Writes: Checks for memory or instruction updates made by users who are not the owner of the agent. It flags these if they contain any of the suspicious tokens.
    • Repeated Suspicious Phrases: Searches for the same suspicious phrase appearing in multiple distinct conversations with the same agent, suggesting that the phrase is being carried over due to memory poisoning.
  3. Combine and Report Findings: The query combines results from both checks and presents them in a unified format, highlighting the time of the event, the source of the suspicious activity, the agent involved, and details about the operation.

  4. Security Context: The query is associated with tactics like persistence and defense evasion, and techniques such as T1546 (event-triggered execution) and T1556 (compromise of credentials).

Overall, this query helps in identifying and analyzing potential security threats related to AI agent memory manipulation in Microsoft 365 Copilot.

Details

David Alonso profile picture

David Alonso

Released: May 20, 2026

Tables

AuditLogsCopilotAdminActivityCopilotActivity

Keywords

AuditLogsCopilotAdminActivityCopilotActivityAgentMemoryContextPoisoningPersistenceOperationNameInitiatedByUserUserPrincipalNameTargetResourcesIdDisplayNameOwnerUpnPayloadTimeGeneratedInstructionSystemPromptCustomInstructionGroundingSourceUpdateSetAddWriteModifyActorUpnAgentIdAgentNameByNonOwnerHasPoisonTextRecordTypeCopilotInteractionLLMEventDataResponseConversationIdDistinctConvsAffectedActorsSampleConvsHitsSourceCrossConversationCarryoverTacticsDefenseEvasionTechniquesT1546T1556TagsSentinelAsCodeCustomCopilotAI

Operators

letdynamictostringtolowerunionisfuzzyextendcolumn_ifexistscoalesceagohas_anyisnotemptyprojectwheremv-applysummarizedcountmake_setcountstrcatorder by

Tactics

PersistenceDefenseEvasion

MITRE Techniques

Actions

GitHub