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
-
Identify Agent User OAuth logons
Query · scopingFind non-interactive sign-ins to Microsoft Teams that use the federated identity credentials specific to Agent User impersonation.
reads hb_auth_signinsqlSELECT 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.
-
Rare Graph Beta PowerShell scripts
Query · baselineIdentify rare script blocks that call the specific Beta cmdlets used for Teams messaging to find manual dispatch activity.
reads hb_script_activitysqlSELECT 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 < 5What a hit looks like. Rare script blocks containing the Teams dispatch cmdlets. Common automation scripts will be filtered out by the prevalence count.
-
Rare Graph API User-Agents
Query · baselineIdentify rare User-Agents hitting Microsoft Graph endpoints to isolate attacker-controlled PowerShell sessions.
reads hb_http_activitysqlSELECT 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 < 5What a hit looks like. A User-Agent string hitting Graph that is not part of the standard fleet automation baseline.
-
Triage agent impersonation
Agent triageCorrelate the OAuth identity flow with the specific script content and User-Agent to determine if the activity is malicious.
-
Route on verdict
DecisionDirect the hunt to containment if impersonation is confirmed.
-
Revoke sessions and purge messages
Response actionNeutralize the compromised identity and remove malicious content from Teams.
-
Analyst manual review
Analyst taskVerify the agent's findings and confirm remediation success.
-
Close out
Analyst taskFinal documentation and reporting.
Coverage
Scenario coverage
| Stage | Covered | How, 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
| Parameter | Type | Default | What it is |
|---|---|---|---|
lookback_days | number | 14 | Days of history to examine. |
scope_hosts | list[host] | — | Optional list of hosts to narrow behavioral queries. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
| Identity / sign-in telemetry | identity | identity |
| Web server / proxy logs | siem | network |
Source
---
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.