Cloud Security Office Hours Banner

Entra ID Actor Token 2025

Step-by-step kill chain mapped to MITRE ATT&CK Cloud, sourced from official post-mortems and primary technical analyses.

July 2025 Critical Entra ID

Entra ID Actor Token - Undocumented S2S Token → Unsigned Impersonation JWT → Legacy Graph Skips the Tenant Check → Global Admin in Any Tenant

In July 2025 the security researcher Dirk-jan Mollema found that an undocumented Microsoft service-to-service credential called an Actor token, requested from a tenant he controlled, would be accepted by the legacy Azure AD Graph API in any other tenant, because that API never checked which tenant the token came from. The result was impersonation of any user in any Entra ID tenant on Earth, up to and including Global Administrator. CVE-2025-55241 was scored CVSS 10.0. This one belongs on this page for a different reason than the rest: it is not a breach. It was responsibly disclosed, Microsoft shipped a global fix three days after the report, and Microsoft's telemetry found no evidence anyone exploited it beforehand. It earns its place because of what it demonstrates. Every control this site recommends - MFA, Conditional Access, device compliance, least privilege - sat entirely outside this path, and so did nearly all of the logging. There was no customer-side configuration that mitigated it, in any tenant, at any price tier.

CVSS 10.0 - impersonate any user in any tenant, including Global Admin
3 days from report to global fix (July 14 to July 17, 2025)
Zero customer-side mitigations available while it was live
Not exploited: Microsoft found no evidence of prior abuse
📄 Dirk-jan Mollema - One Token to rule them all ↗ 📄 MSRC - CVE-2025-55241 ↗
🔬 Discovery - An Undocumented Token Class Nobody Was Watching
01
Actor tokens are service-to-service credentials issued by the Access Control Service, valid 24 hours, un-revocable, and issued without generating a single log entry
T1580 - Cloud Infrastructure Discovery

The research started somewhere unglamorous: hybrid Exchange. Microsoft's own back-end services need to act on a user's behalf when talking to Azure AD Graph, SharePoint, and Exchange Online, and the mechanism they use is an Actor token issued by the Access Control Service. Read the properties together and the risk is obvious in hindsight. Twenty-four hour validity. A trustedfordelegation claim marking the holder as allowed to act as anyone. No audit log when one is issued. No way to revoke one inside its validity window. And explicitly outside Conditional Access, because Conditional Access evaluates user sign-ins and this is not one. Each property is defensible in isolation as an internal service mechanism. Together they describe a bearer credential with no issuance record, no revocation, and no policy engine in front of it.

Issuer: Microsoft Access Control Service (ACS)
Purpose: Service-to-service calls where one Microsoft service acts on a user's behalf
Validity: 24 hours, and not revocable within that window
Key claim: trustedfordelegation, set true for Microsoft first-party apps
Telemetry on issuance: None
Actor TokenACSUndocumentedNo Logging
🔑 Credential Access - Requesting One From Your Own Tenant
02
An Actor token could be requested from a tenant the researcher fully controlled, including a free trial tenant, with no relationship to any target
T1528 - Steal Application Access Token

This is the step that sets the severity. The attacker does not need a foothold anywhere near the target. They need a tenant, and anyone can create one in minutes for nothing. The token is requested legitimately in the requester's own tenant, where they are already the administrator and entitled to it, and no unusual event is generated on either side because nothing unusual has happened yet. Everything that follows is a matter of what the requester then puts inside the next token, and which API is willing to read it without asking where it came from.

Prerequisite: Any Entra ID tenant the attacker controls, trial tenants included
Relationship to target: None required - no guest account, no federation, no prior access
Cost: Effectively zero
Signal generated in the attacker's tenant: A legitimate token request by its own administrator
Free Trial TenantNo Foothold NeededT1528
⬆️ Privilege Escalation - The Impersonation Token Is Unsigned
03
The impersonation JWT wrapping the Actor token carries no signature, so its tenant ID and target user could simply be edited
T1606 - Forge Web Credentials T1550.001 - Application Access Token

To impersonate a user, a Microsoft service builds a second JWT that contains the Actor token, the target tenant ID, and the target user's identifier, and it does not sign that outer token. The trust is meant to come from the embedded Actor token rather than from a signature over the whole thing. So the researcher edited it. Changing the tenant GUID to a different organization and the nameid to a different user produced a token he expected to be rejected instantly for the obvious tenant mismatch. Sending it to graph.windows.net returned a valid session instead. The UPN fields in the token turned out to be decorative; Azure AD Graph ignored them and keyed entirely off nameid.

Structure: Unsigned outer JWT containing the Actor token in an actortoken field
Attacker-editable fields: Target tenant GUID, target user nameid
Ignored by the API: UPN values, which could be dummies
Expected result: Access denied on tenant mismatch
Actual result: A working session as the impersonated user
Unsigned JWTTenant GUID SwapnameidT1606
Initial Access - A Legacy API That Never Checked the Tenant
04
The legacy Azure AD Graph API validated the user identifier but never confirmed that the token originated in the tenant it was being used against
T1078.004 - Valid Accounts: Cloud Accounts

The whole vulnerability reduces to one missing comparison. graph.windows.net, the deprecated predecessor to Microsoft Graph, checked that the nameid corresponded to a real user and then served the request. It never asked whether the Actor token authorizing that impersonation had been issued in the same tenant as the user being impersonated. A single boundary check, absent from an API old enough that most of its consumers had already been migrated away, converted a legitimate internal delegation mechanism into cross-tenant impersonation for the entire service. Legacy surfaces are dangerous precisely because they are quiet: nobody is adding features, so nobody is re-reviewing the assumptions, and the traffic is low enough that anomalies do not stand out.

Vulnerable API: Azure AD Graph, graph.windows.net (the deprecated pre-Microsoft-Graph API)
What it validated: That nameid mapped to a real user
What it never validated: That the Actor token came from the tenant being accessed
Blast radius: Every Entra ID tenant, since the flaw was in the shared service
CVE: CVE-2025-55241, CVSS 10.0
Azure AD GraphLegacy APIMissing Tenant CheckCVE-2025-55241
🔍 Discovery - Finding a Target User's netId Three Different Ways
05
Impersonation needed the target's netId, and netIds increment rather than randomize, so they could be brute-forced, read from B2B guest attributes, or lifted from any leaked token
T1087.004 - Account Discovery: Cloud Account

The one thing standing between an attacker and a given tenant was knowing a user's internal netId, and it turned out not to stand for long. Because netIds increment rather than being randomly assigned, they can be walked systematically, reducing "find a valid user in this tenant" to minutes or hours of automation. If the attacker had ever invited someone as a B2B guest, the guest object stored the home tenant user's netId in its alternativeSecurityIds attribute, readable by whoever issued the invitation. And any token that had ever leaked - expired, redacted in a bug report, pasted into a support ticket - carried a puid claim that yields a working netId. Values that are not secret, because nobody designed them to be secret, become the last remaining control the moment the boundary above them fails.

Method 1: Brute force - netIds increment rather than randomize, so they enumerate in minutes to hours
Method 2: B2B guest objects store the home tenant netId in alternativeSecurityIds, readable by the inviter
Method 3: Any leaked or expired token with a puid claim yields a usable netId
Design assumption broken: That an internal identifier does not need to be treated as sensitive
netIdEnumerable IdentifiersB2B GuestT1087.004
💥 Impact - Global Admin, and Every Control Bypassed by Construction
06
Impersonating a Global Administrator gave full directory access, and neither MFA, Conditional Access, device compliance, nor tenant hardening applied to any part of the path
T1098 - Account Manipulation

With a Global Administrator impersonated, the attacker holds the tenant: directory contents, group and role membership, application and service principal configuration, and the ability to grant themselves durable access through more conventional means. What makes this different from every other identity chain on this page is that no customer decision affected the outcome. Conditional Access never evaluated the request because there was no user sign-in to evaluate. MFA was never prompted because no authentication occurred at the user layer. Device compliance, sign-in risk policies, named locations, token protection - none of them sat on this path. As the researcher put it plainly, there was no setting that could have mitigated this, including for tenants that had done everything right.

Access obtained: Global Administrator equivalent, via impersonation rather than role assignment
Conditional Access: Not evaluated - no user sign-in occurs
MFA: Never prompted - no user-layer authentication
Hardened tenants: Equally affected; no configuration mitigated it
Duration of a stolen token's usefulness: 24 hours, un-revocable
Global AdminCA BypassMFA IrrelevantT1098
🕳 Defense Evasion - Almost Nothing Reached the Victim's Logs
07
Token issuance, impersonation, and every read produced no log in the target tenant; only modifications were recorded, and those with a misleading actor
T1562.008 - Impair Defenses: Disable or Modify Cloud Logs

Read the telemetry picture as a whole and the honest conclusion is that a victim organization could not have answered the question "were we accessed." Issuing the Actor token: no log. Crafting the impersonation token: no log. Reading the entire directory through Azure AD Graph: no log. Only write operations landed in the audit log, and even those arrived confusingly, recording the impersonated Global Administrator's UPN alongside the display name of Exchange - an artifact pattern that reads as a Microsoft service doing something odd rather than as an intrusion. This is the same failure that made Storm-0558 so painful two years earlier, and it is worth naming the general form: when authentication material is forged rather than stolen, logs are the only remaining detection, and a design that does not log issuance has removed the last one.

Actor token issuance: No log
Impersonation token creation: No log
Directory reads via Azure AD Graph: No log
Writes: Logged, but showing the impersonated user's UPN with Exchange's display name
Practical consequence: A victim could not determine whether they had been accessed
No TelemetryUnanswerable QuestionAudit ArtifactT1562.008
🛠 Resolution - Reported July 14, Fixed Globally July 17
08
Microsoft restored the tenant check and then blocked applications from requesting Actor tokens for Azure AD Graph at all

The disclosure timeline is the encouraging part of this entry, and worth recording accurately. Mollema reported to MSRC on July 14, 2025. Microsoft deployed a production fix globally on July 17, three days later. On August 6 it shipped a second mitigation preventing applications from obtaining Actor tokens for Azure AD Graph using service principal credentials, narrowing the mechanism to Microsoft's own internal services. CVE-2025-55241 was assigned September 4, and the research was published September 17. Microsoft's internal telemetry found no evidence of exploitation before the fix. Nothing here required customer action, which is the point: this was fixed on the provider's side because it could only be fixed on the provider's side.

July 14, 2025: Reported to the Microsoft Security Response Center
July 17, 2025: Production fix deployed globally - tenant validation restored
August 6, 2025: Applications blocked from requesting Actor tokens for Azure AD Graph
September 4, 2025: CVE-2025-55241 assigned, CVSS 10.0
September 17, 2025: Public disclosure
Responsible Disclosure3-Day FixProvider-Side Only

🛡 How to Defend Against This Chain

Accept honestly that you could not have stopped this one, and let that inform where you spend. There was no tenant setting, no licence tier, and no architecture that mitigated it. That is not a counsel of despair, it is an argument for the controls that limit consequences rather than prevent access: what an account can reach once it is inside, how quickly you would notice a change, and how fast you can revoke. Prevention has a ceiling that your provider sets; containment does not.
Retire legacy API surfaces on your side and push your vendors to retire theirs. The flaw lived in Azure AD Graph, an API already deprecated in favour of Microsoft Graph. Audit which applications in your tenant still call graph.windows.net, migrate them, and then block the legacy endpoint where you can. Every deprecated-but-reachable interface in your estate is carrying assumptions nobody has re-reviewed in years, and this is what that costs.
Treat internal identifiers as sensitive, because a boundary failure promotes them into the only control left. netIds were enumerable because nobody needed them to be unguessable. Apply the same suspicion to your own systems: object IDs, tenant GUIDs, and user identifiers that leak through APIs, error messages, support tickets, and screenshots. Redact them by default, and prefer non-sequential identifiers so enumeration is not free.
Log what your provider does not, and know where your telemetry actually stops. You cannot add logging inside Microsoft's service, but you can collect Entra ID audit and sign-in logs into your own SIEM with retention you control, so the record survives an attacker who reaches the tenant. Then write down which activities your provider does not log at all - that list is your blind spot inventory, and it belongs in your incident response plan rather than being discovered during an incident.
Hunt for the specific artifact pattern this produced. The only trace was a write operation where the initiator's UPN belonged to a user while the display name belonged to a Microsoft service such as Exchange. Build a detection for that mismatch generally - an audit event whose actor identity is internally inconsistent - because it generalizes past this particular CVE to any impersonation flaw with the same shape.
Reduce standing Global Administrator, since impersonation targets whoever holds the role. The attack impersonates an existing privileged user, so fewer permanent Global Admins is a smaller target set. Use Privileged Identity Management for just-in-time elevation, keep break-glass accounts minimal and monitored, and separate administrative identities from day-to-day ones so the high-value nameid values are not the ones that appear in every guest invitation and shared document.

Related defense topics