← All hunts high TLP:CLEAR

Entra ID Agent User Impersonation and Teams Abuse

An attacker uses the Entra ID Agent User OAuth flow to impersonate an AI agent and dispatch malicious content via Microsoft Teams using Graph API cmdlets.

Based on research by Red Canary 2026-09-20 9 steps · 3 queries T1059.001

Brief

Why hunt for Agent User identities?

Recent research from Red Canary, titled Investigating suspicious AI workflows in Microsoft Entra Agent ID, details how attackers can abuse the 'Agent User' OAuth flow. These identities allow AI agents to act on behalf of users without traditional interactive MFA. Because these accounts are often trusted by default and used for automated tasks, they provide a quiet path for attackers to send malicious Teams messages or access sensitive data via the Microsoft Graph API.

How the Hunt Flows

The hunt begins at the cloud authentication surface. The first step queries sign-in logs to identify any identities authenticating to Microsoft Teams using the federated identity credentials (user_fic) specific to Agent User impersonation. This provides a focused list of active agent identities and their source IP addresses to narrow the scope of behavioral analysis.

Once the hunt identifies active agent logons, it branches into two parallel behavioral queries. The first query searches for rare PowerShell script blocks on endpoints that call specific Microsoft Graph Beta cmdlets, such as New-MgBetaTeamChannelMessage. It uses prevalence counting to ignore common automation and isolate manual or new message dispatch activity. The second query examines HTTP telemetry for rare User-Agent strings hitting Graph API endpoints, specifically looking for PowerShell-based traffic that does not match the established fleet baseline.

Finally, the hunt uses an automated agent to correlate these signals. The agent looks for temporal proximity between the cloud sign-in and the endpoint execution. If a specific host executes messaging scripts within a short window of an Agent User logon, the hunt flags the activity for manual review and potential revocation.

Blind Spots and Limitations

This hunt relies on two critical visibility requirements. First, it requires PowerShell Script Block Logging (Event ID 4104) to see the content of the Beta cmdlets. If logging is disabled, the hunt cannot distinguish between different types of Graph API automation. Second, identifying rare User-Agents requires TLS decryption of traffic to graph.microsoft.com. Without decryption, the hunt must rely entirely on endpoint script logs, which an attacker might attempt to clear or bypass.

Steps

  1. Identify Agent User OAuth logons

    Query · scoping

    Find non-interactive sign-ins to Microsoft Teams that use the federated identity credentials specific to Agent User impersonation.

    reads hb_auth_signinsql
    SELECT actor_user_name, src_endpoint_ip, auth_protocol, dst_endpoint_name, time FROM hb_auth_signin WHERE provider = 'm365' AND LOWER(dst_endpoint_name) = 'microsoft teams' AND (LOWER(auth_protocol) LIKE '%user_fic%' OR LOWER(actor_user_name) LIKE '%agent%') AND time >= datetime('now', '-{{lookback_days}} days')

    What a hit looks like. Rows showing identities authenticating to Teams via the Agent User flow (user_fic). Silence suggests no such identities are active in the window.

  2. Rare Graph Beta PowerShell scripts

    Query · baseline

    Identify rare script blocks that call the specific Beta cmdlets used for Teams messaging to find manual dispatch activity.

    reads hb_script_activitysql
    SELECT script_content, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_script_activity WHERE (LOWER(script_content) LIKE '%new-mgbetateamchannelmessage%' OR LOWER(script_content) LIKE '%connect-mggraph%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY script_content HAVING hosts < 5

    What a hit looks like. Rare script blocks containing the Teams dispatch cmdlets. Common automation scripts will be filtered out by the prevalence count.

  3. Rare Graph API User-Agents

    Query · baseline

    Identify rare User-Agents hitting Microsoft Graph endpoints to isolate attacker-controlled PowerShell sessions.

    reads hb_http_activitysql
    SELECT user_agent, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_http_activity WHERE url_hostname LIKE '%graph.microsoft.com%' AND user_agent LIKE '%PowerShell/%' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY user_agent HAVING hosts < 5

    What a hit looks like. A User-Agent string hitting Graph that is not part of the standard fleet automation baseline.

  4. Triage agent impersonation

    Agent triage

    Correlate the OAuth identity flow with the specific script content and User-Agent to determine if the activity is malicious.

  5. Route on verdict

    Decision

    Direct the hunt to containment if impersonation is confirmed.

  6. Revoke sessions and purge messages

    Response action

    Neutralize the compromised identity and remove malicious content from Teams.

  7. Analyst manual review

    Analyst task

    Verify the agent's findings and confirm remediation success.

  8. Close out

    Analyst task

    Final documentation and reporting.

Coverage

Scenario coverage

StageCoveredHow, or why not
Agent User OAuth Flow Authentication
T1059.001
Yes identify-agent-user-logons
Teams Message Dispatch via Graph API
T1059.001
Yes rare-graph-beta-scripts, graph-api-user-agents

Blind spots

  • Needs hb_http_activity with TLS decryption. If the proxy does not decrypt Graph traffic, the User-Agent is invisible, forcing reliance on endpoint script logs. It would answer Can we see the User-Agent in Graph API requests?.
  • Needs PowerShell Script Block Logging (EID 4104). If script block logging is disabled, the specific commands used to dispatch messages cannot be recovered from hb_script_activity. It would answer Can we see the content of the mgbeta cmdlets?.

Parameters & data

Parameters

ParameterTypeDefaultWhat it is
lookback_daysnumber14Days of history to examine.
scope_hostslist[host]Optional list of hosts to narrow behavioral queries.

Telemetry

SourceCategoryTelemetry
Endpoint telemetry (hb_ surfaces)endpointendpoint
Identity / sign-in telemetryidentityidentity
Web server / proxy logssiemnetwork

Source

Download hunt.md Definition (JSON) An open hunt.md file; it runs anywhere that reads the format.
---
analysis: "This hunt uses three distinct surfaces\u2014Entra sign-ins, host-based\
  \ PowerShell script blocks, and HTTP gateway telemetry\u2014to identify a specific\
  \ impersonation flow that a single log source cannot fully contextualize. It specifically\
  \ uses prevalence counting to find rare User-Agents and scripts that standard rules\
  \ would miss."
blind_spots:
- id: no-graph-visibility
  question: Can we see the User-Agent in Graph API requests?
  requires: hb_http_activity with TLS decryption
  risk: If the proxy does not decrypt Graph traffic, the User-Agent is invisible,
    forcing reliance on endpoint script logs.
  stage: graph-api-teams-message-dispatch
- id: script-block-logging-disabled
  question: Can we see the content of the mgbeta cmdlets?
  requires: PowerShell Script Block Logging (EID 4104)
  risk: If script block logging is disabled, the specific commands used to dispatch
    messages cannot be recovered from hb_script_activity.
  stage: graph-api-teams-message-dispatch
coverage:
- stage: agent-user-oauth-authentication
  status: covered
  steps:
  - identify-agent-user-logons
- stage: graph-api-teams-message-dispatch
  status: covered
  steps:
  - rare-graph-beta-scripts
  - graph-api-user-agents
guardrails:
  claims: no_unsupported
  evidence: citation_required
  missing_data: not_benign
  telemetry: untrusted
hunt:
  applicability: campaign-specific
  handoff: keep-as-periodic-hunt
  justification: Agent User identities are trusted internal accounts that bypass standard
    interactive MFA; their use for Teams-based phishing represents a high-trust lateral
    movement risk.
  methodology: model-assisted
  trigger: intel-report
hypothesis: An attacker uses the Entra ID Agent User OAuth flow to impersonate an
  AI agent and dispatch malicious content via Microsoft Teams using Graph API cmdlets.
labels:
- hunt
- attack.t1059.001
name: Entra ID Agent User Impersonation and Teams Abuse
parameters:
  lookback_days:
    default: '14'
    description: Days of history to examine.
    type: number
  scope_hosts:
    default: []
    description: Optional list of hosts to narrow behavioral queries.
    type: list[host]
provenance:
  authors:
  - name: Huntbase hunt generation
    org: huntbase.io
  generated:
    by: huntbase-hunt-generation
    from: https://redcanary.com/blog/threat-detection/entra-id-ai-workflows-teams/
    gates:
    - dry-run
    - lint
    model: hb_google/gemini-3-flash-preview
rationale: Focus on identities using the 'user_fic' grant type; this is the primary
  indicator of the Agent User OAuth flow. Start with cloud sign-in logs to establish
  a list of active Agent Users before pivoting to endpoint script activity.
references:
- name: "Red Canary \u2014 Investigating suspicious AI workflows in Microsoft Entra\
    \ Agent ID"
  url: https://redcanary.com/blog/threat-detection/entra-id-ai-workflows-teams/
related:
- hunt: suspicious-microsoft-graph-api-activity
  reason: This hunt focuses on the Agent User OAuth flow, whereas the sibling hunt
    covers general Graph API abuse.
  relation: sibling
scenario:
  stages:
  - name: Agent User OAuth Flow Authentication
    observables:
    - login.microsoftonline.com
    - api://AzureADTokenExchange/.default
    - grant_type=user_fic
    - requested_token_use=on_behalf_of
    - user_federated_identity_credential
    - agent.agentSubjectType == agentIDuser
    - agent.agentType == agenticAppInstance
    slug: agent-user-oauth-authentication
    tactic: execution
    techniques:
    - T1059.001
  - name: Teams Message Dispatch via Graph API
    observables:
    - microsoft.graph.beta
    - Mozilla/5.0 (Macintosh; macOS 26.4.1; en-US) PowerShell/7.6.1
    - 51.3.97.221
    - 70.152.145.147
    - New-MgBetaTeamChannelMessage
    - https://domoarigato.ai/
    - domoarigato.ai
    slug: graph-api-teams-message-dispatch
    tactic: initial-access
    techniques:
    - T1059.001
  summary: An attacker abuses Microsoft Entra ID Agent User identities to distribute
    malicious links via Microsoft Teams. The attack involves executing a PowerShell
    script on a macOS host to perform a specialized OAuth flow, impersonating an agent
    user to call the Graph API and send messages to team channels.
severity: high
targets:
  analyst:
    name: Tier-2 analyst
    role: analyst
  endpoint:
    category: endpoint
    name: Endpoint telemetry (hb_ surfaces)
    telemetry:
    - endpoint
  hunter:
    agent: true
    name: Hunt agent
  identity:
    category: identity
    name: Identity / sign-in telemetry
    telemetry:
    - identity
  web:
    category: siem
    name: Web server / proxy logs
    telemetry:
    - network
tlp: clear
type: investigation
---


# Entra ID Agent User Impersonation and Teams Abuse

This hunt identifies unauthorized use of Entra ID Agent User identities by correlating non-interactive authentication patterns with endpoint PowerShell script execution. It targets the 'user_fic' grant type and the impersonation of identities with an 'agentIDuser' subject type. The hunt flows from cloud authentication logs to endpoint telemetry, looking for specific Graph Beta PowerShell cmdlets and rare User-Agent strings used to send messages to Teams channels. An agent evaluates the combined evidence to distinguish legitimate autonomous agent activity from manual attacker-driven impersonation, specifically checking for temporal proximity between the cloud logon and the execution of script blocks.

## identify-agent-user-logons
<!-- Identify Agent User OAuth logons -->
Find non-interactive sign-ins to Microsoft Teams that use the federated identity credentials specific to Agent User impersonation.

```sqlite target=identity role=scoping params=(lookback_days=lookback_days)
~~~yaml
expected: Rows showing identities authenticating to Teams via the Agent User flow
  (user_fic). Silence suggests no such identities are active in the window.
reads:
- actor_user_name
- src_endpoint_ip
- auth_protocol
- dst_endpoint_name
- time
silence: not_evidence_of_absence
source: hb_auth_signin
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT actor_user_name, src_endpoint_ip, auth_protocol, dst_endpoint_name, time FROM hb_auth_signin WHERE provider = 'm365' AND LOWER(dst_endpoint_name) = 'microsoft teams' AND (LOWER(auth_protocol) LIKE '%user_fic%' OR LOWER(actor_user_name) LIKE '%agent%') AND time >= datetime('now', '-{{lookback_days}} days')
```

## activity-fan-out
<!-- Parallel behavioral analysis -->
parallel:
- → rare-graph-beta-scripts
- → graph-api-user-agents
join: → triage-impersonation

## rare-graph-beta-scripts
<!-- Rare Graph Beta PowerShell scripts -->
Identify rare script blocks that call the specific Beta cmdlets used for Teams messaging to find manual dispatch activity.

```sqlite target=endpoint role=baseline params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
baseline:
  compare: first_seen
  window: '{{lookback_days}}d'
expected: Rare script blocks containing the Teams dispatch cmdlets. Common automation
  scripts will be filtered out by the prevalence count.
prevalence:
  by: device_hostname
  key:
  - script_content
  rare_below: 5
reads:
- device_hostname
- script_content
- time
silence: not_evidence_of_absence
source: hb_script_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT script_content, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_script_activity WHERE (LOWER(script_content) LIKE '%new-mgbetateamchannelmessage%' OR LOWER(script_content) LIKE '%connect-mggraph%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY script_content HAVING hosts < 5
```

## graph-api-user-agents
<!-- Rare Graph API User-Agents -->
Identify rare User-Agents hitting Microsoft Graph endpoints to isolate attacker-controlled PowerShell sessions.

```sqlite target=web role=baseline params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
baseline:
  compare: first_seen
  window: '{{lookback_days}}d'
expected: A User-Agent string hitting Graph that is not part of the standard fleet
  automation baseline.
prevalence:
  by: device_hostname
  key:
  - user_agent
  rare_below: 5
reads:
- device_hostname
- url_hostname
- user_agent
- time
silence: not_evidence_of_absence
source: hb_http_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT user_agent, COUNT(DISTINCT device_hostname) AS hosts, MIN(time) AS first_seen FROM hb_http_activity WHERE url_hostname LIKE '%graph.microsoft.com%' AND user_agent LIKE '%PowerShell/%' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY user_agent HAVING hosts < 5
```

## triage-impersonation
<!-- Triage agent impersonation -->
```agent target=hunter
cite: required
context:
- identify-agent-user-logons
- rare-graph-beta-scripts
- graph-api-user-agents
max_iterations: 4
objective: Determine if an Entra ID Agent User was used by an unauthorized process
  to send suspicious Teams messages. Explicitly check for temporal proximity, such
  as a 60-minute window, between the Agent User sign-in event and the endpoint script
  execution or HTTP traffic.
success_criteria: A verdict of malicious | suspicious | benign for each identity,
  citing the script blocks and timestamps.
tools:
- endpoint
- identity
- web
```

## route-response
<!-- Route on verdict -->
if~: "the triage verdict is malicious for at least one agent user" (confidence: high, judge=hunter)
then: → revoke-and-purge
indeterminate: → analyst-manual-review
unavailable: → analyst-manual-review (blind_spot: no-graph-visibility)
else: → close-out

## revoke-and-purge
<!-- Revoke sessions and purge messages -->
```action target=identity
~~~yaml
approval: required
~~~
Revoke all active OAuth refresh tokens for the identified Agent User and its parent Blueprint principal in Entra ID. Use the Teams Messaging Policy or Purview to identify and delete malicious messages sent by this agent user.
```
→ analyst-manual-review

## analyst-manual-review
<!-- Analyst manual review -->
```manual target=analyst
Review the cited script blocks and Graph API activity. Verify the revoked identity is no longer active and that reported messages have been successfully purged from Teams.
```
→ close-out

## close-out
<!-- Close out -->
```manual target=analyst
Record incident findings. If the rare PowerShell User-Agent was consistent, consider promoting the HTTP query to a permanent detection rule.
```
→ end

Run it

Take this hunt into your environment.

Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.

Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, then reviewed by a person.