ConsentFix and EvilTokens: How OAuth Consent Phishing Beats MFA Entirely

Proactive Security for the AI Era
NodeZero continuously and autonomously pentests infrastructure, identity, cloud, and now web applications, chaining weaknesses across every domain the way real attackers do. Every finding ships with replayable proof showing exploitable business impact, not theoretical risk.
Most identity defenses are built around one assumption: the attacker needs the victim's password, and if they also need a second factor, the MFA prompt stops them. OAuth consent phishing breaks that assumption completely. The victim never types a password into a fake page, and the attacker never has to defeat MFA, because the victim who is doing everything correctly, typing their real password into the real Microsoft login page and approving their real MFA prompt, is the one who hands the attacker access.
The attacker's payload is not a credential. It is a live OAuth token: an authorization code, an access token, or a refresh token, obtained because the victim clicked approve on a consent screen or copied a code from one browser tab to another. That token is bound to the OAuth grant, not to the user's password. Resetting the password does not touch it. Forcing a password change, which is the default first move in almost every incident response playbook, leaves the attacker's session running.
This is not a theoretical gap. Push Security documented ConsentFix, a browser-native phishing kit that abuses the Azure CLI's implicit trust in every Entra ID tenant to steal an OAuth authorization code without a single credential-harvesting page. Separately, a phishing-as-a-service platform called EvilTokens weaponized Microsoft's OAuth device code flow and, per Cloud Security Alliance research, reached more than 340 Microsoft 365 organizations across seven countries within about five weeks of going live in February 2026. Microsoft has separately documented ShinyHunters using vishing calls to walk employees through approving a malicious OAuth app disguised as a Salesforce Data Loader tool, then reusing the resulting token against connected SaaS applications, the same vishing-driven extortion pattern we covered in ShinyHunters' vishing campaign against Charter and 400 other organizations. Three different delivery mechanisms, one shared outcome: a token that outlives the password.
Why This Beats Password Resets and MFA, Not Just Weak Passwords
It helps to separate three attacker goals that security teams often collapse into one: getting the password, getting past MFA, and getting a working session. Traditional credential phishing targets the first two. OAuth consent phishing skips both and goes straight for the third.
In ConsentFix, the victim is routed through a fake Cloudflare Turnstile check on a compromised site, then told to click a Sign In button that opens the real Microsoft login page (login.microsoftonline.com), authenticate normally including MFA, and then copy a localhost redirect URL containing an authorization code back into the phishing page. The attacker exchanges that code for tokens using the Azure CLI's application ID, which is a first-party Microsoft app implicitly trusted across every tenant with no admin approval required and no ability to delete or block it like a third-party registration.
In the EvilTokens pattern, the victim is directed to enter a short device code at microsoft.com/devicelogin, part of the legitimate OAuth 2.0 Device Authorization Grant built for input-constrained devices like smart TVs and CLI tools. The victim completes their own MFA on the real Microsoft page. The attacker's device, not the victim's browser, receives the resulting refresh token.
In the ShinyHunters pattern Microsoft documented, a caller impersonating IT support talks the victim through the standard OAuth consent screen for a malicious app posing as a Salesforce Data Loader utility. The victim clicks Accept on a permission request that looks exactly like every other third-party app consent prompt they have approved before.
All three hand the attacker an OAuth refresh token, which is designed to be long-lived and to survive routine account maintenance. A password reset invalidates the password. It does not, by itself, invalidate a refresh token issued under an OAuth consent grant that is still standing in the directory.
Prerequisites
This guide assumes:
- Global Administrator or Privileged Role Administrator access in Microsoft Entra ID, plus Cloud Application Administrator or Application Administrator for the enterprise app review steps.
- An existing Conditional Access baseline (MFA for all users, legacy authentication blocked). If that baseline is not in place yet, build it first; see our guide on hardening Entra ID Conditional Access for Zero Trust. Consent restrictions are a second control layer, not a replacement for Conditional Access.
- Microsoft Entra ID P1 or P2 licensing for Identity Protection sign-in risk detections and audit log retention beyond the default window.
- The Microsoft Graph PowerShell SDK (
Microsoft.Graph.Identity.SignIns,Microsoft.Graph.Applications) or equivalent Microsoft Graph API access, since several of the steps below are not exposed in the Entra admin center UI at all. - A defined incident process for who is authorized to force a tenant-wide consent lockdown or a device sign-out, since both are disruptive and should not be a solo judgment call during an active incident.
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Step 1: Restrict Who Can Grant Consent in the First Place
The default Entra ID setting (microsoft-user-default-legacy) lets any user consent to any app requesting permissions that do not require admin approval. That is the door ConsentFix, EvilTokens, and the ShinyHunters vishing calls all walk through: none of them need an admin, they need one employee willing to click Accept.
Restrict this in Entra admin center > Identity > Applications > Enterprise apps > Consent and permissions > User consent settings, or via Microsoft Graph PowerShell using the authorization policy shown below.
Connect-MgGraph -Scopes "Policy.ReadWrite.Authorization"
# Move the tenant to verified-publisher, low-risk-permission consent only
$body = @{
"defaultUserRolePermissions" = @{
"permissionGrantPoliciesAssigned" = @(
"managePermissionGrantsForSelf.microsoft-user-default-low"
)
}
}
Update-MgPolicyAuthorizationPolicy -BodyParameter $body
Before this permission set is meaningful, classify which delegated permissions your tenant treats as low risk under Enterprise apps > Consent and permissions > Permission classifications. Microsoft's default low-risk set is conservative (basic profile read, sign-in), but review it against your own risk tolerance; do not assume the defaults already match what your organization considers safe to auto-approve.
Subscribe to unlock Remediation & Mitigation steps
Free subscribers unlock full IOC lists, Sigma detection rules, remediation steps, and every daily briefing.
Step 2: Turn on the Admin Consent Request Workflow
Restricting consent has a predictable side effect: users who need a legitimate app that requires admin-level permissions now hit a wall with no path forward except emailing IT. The admin consent workflow gives them a structured request path instead, and gives you a reviewable queue instead of ad hoc Slack messages asking someone to "just approve this real quick," which is exactly the social pressure a vishing caller relies on.
Configure it at Entra admin center > Identity > Applications > Enterprise apps > Consent and permissions > Admin consent settings: turn on "Users can request admin consent to apps they are unable to consent to," and assign specific reviewers (not a single admin who can be socially engineered alone; use a group with at least two reviewers). Set a reminder and expiration interval so requests do not sit unreviewed indefinitely, and require reviewers to record a business justification, which forces a moment of actual scrutiny instead of a reflexive approve.
Critically, train reviewers on what a malicious request looks like: an app name that mimics a known tool ("Salesforce Data Loader," "Zoom Client Extension"), a publisher that is unverified, or a permission scope (mail read/send, full directory read, offline_access) that is far broader than the app's stated purpose would require.
Step 3: Alert on New OAuth Grants Before They Are Used
Consent restrictions reduce the attack surface; they do not eliminate it, since first-party Microsoft apps like Azure CLI bypass consent policy entirely and a socially engineered admin can still approve a request-workflow ticket. You need detection on the grant event itself, not just prevention.
Every consent grant and every new service principal creation is logged in the Entra ID audit log. Route these to Microsoft Sentinel, Microsoft Defender for Cloud Apps, or your SIEM of choice and alert on the patterns below rather than trying to review every single grant manually.
Subscribe to unlock Remediation & Mitigation steps
Free subscribers unlock full IOC lists, Sigma detection rules, remediation steps, and every daily briefing.
Step 4: Revoke a Stolen Token When You Find One
This is the step most incident response runbooks get wrong, because they stop at "reset the password." A stolen OAuth token is not tied to the password; it is tied to the consent grant and the refresh token issued under it. Both must be revoked separately, and revocation is not instantaneous or complete without doing all of the following.
- Revoke the user's sign-in sessions. This invalidates refresh tokens and session cookies issued to the user's own interactive sessions and forces re-authentication everywhere.
Connect-MgGraph -Scopes "User.RevokeSessions.All"
Invoke-MgRevokeUserSignInSession -UserId "user@yourtenant.com"
- Remove the malicious app's OAuth grant and role assignments. Revoking the user's own session does not touch a separate OAuth2PermissionGrant held by a third-party app; that grant has its own refresh token lineage and keeps working until it is removed directly.
Connect-MgGraph -Scopes "Application.ReadWrite.All","DelegatedPermissionGrant.ReadWrite.All","AppRoleAssignment.ReadWrite.All"
$sp = Get-MgServicePrincipal -Filter "displayName eq 'Suspicious App Name'"
Get-MgOauth2PermissionGrant -All | Where-Object { $_.ClientId -eq $sp.Id } |
ForEach-Object { Remove-MgOauth2PermissionGrant -OAuth2PermissionGrantId $_.Id }
Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -All |
Where-Object { $_.PrincipalType -eq "ServicePrincipal" } |
ForEach-Object { Remove-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $sp.Id -AppRoleAssignmentId $_.Id }
Microsoft's own documentation for this procedure is titled around revoking permissions and invalidating refresh tokens issued under that grant; removing the grant is what actually kills the attacker's refresh token, not the password reset.
-
Disable or delete the service principal entirely if the app has no legitimate purpose in your tenant, via Enterprise apps > [app] > Properties > Delete, so it cannot be re-consented to accidentally.
-
Rotate anything the token could have reached. A mailbox-scoped grant means checking for newly created inbox rules, forwarding rules, and OAuth grants the attacker may have added for persistence before you revoke them. Check Exchange transport rules and mailbox forwarding separately; token revocation does not undo actions the attacker already took while the token was valid.
-
Confirm the user was not also compromised via the vishing pretext itself. If a helpdesk-impersonation call was involved, verify no separate password reset or MFA method registration happened during the same call, since ShinyHunters' documented playbook often chains a consent grant with a helpdesk-assisted MFA re-registration.
Validation: Prove the Controls Actually Work
Do not assume a policy change took effect the way you configured it. Test both halves: that restriction actually blocks an unapproved app, and that revocation actually kills a token.
To test consent restriction, register a throwaway test app in a separate tenant or use an existing low-privilege third-party app that is not from a verified publisher, and attempt to consent to it as a standard test user. Confirm the user is blocked and, if the admin consent workflow is enabled, that a request is generated for reviewers instead of the consent silently failing with no path forward (a silent dead end pushes frustrated users toward workarounds, which is its own risk).
To test revocation, grant a test app consent as a test user, capture that the app can call Microsoft Graph successfully (a simple GET /me call using the app's issued token confirms this), then run the Step 4 revocation commands and attempt the same Graph call again using the same previously issued access token. It should still succeed until that specific access token's natural expiry, typically under 90 minutes, since access tokens are bearer tokens not re-validated in real time. Then attempt to mint a new access token using the app's refresh token; this should now fail, confirming the grant removal took effect even though the outstanding access token has not yet expired. If your monitoring cannot distinguish between these two states, your incident timeline will overstate how quickly the attacker actually lost access.
Failure Cases: Where This Still Breaks Down
Every control above has a documented gap. Treat these as things to compensate for, not reasons to skip the control.
Subscribe to unlock Remediation & Mitigation steps
Free subscribers unlock full IOC lists, Sigma detection rules, remediation steps, and every daily briefing.
Security Tradeoffs: Friction vs. Risk Reduction
Restricting user consent is not free. Moving to verified-publisher-only consent and turning on the admin consent workflow shifts real work onto both users and reviewers: legitimate app adoption slows down, and a reviewer queue is only a security improvement if someone actually reviews it critically instead of rubber-stamping requests to clear a backlog.
The tradeoff is worth making because the alternative, permissive default consent, is precisely the gap that made a 340-plus-organization campaign possible in five weeks with a $299-per-month commodity kit. But size the friction to your risk tolerance: a tenant with a mature admin consent workflow and multiple trained reviewers can afford verified-publisher-only consent broadly; a smaller organization without dedicated reviewer capacity may need to start with "do not allow user consent" for high-risk permission categories only, and accept a slower rollout of new third-party integrations until reviewer capacity catches up. What is not an acceptable tradeoff is leaving the legacy default in place because reviewing consent requests feels like overhead, since that overhead is the entire control.
The bottom line
Password resets and MFA do not stop OAuth consent phishing because the attacker never needed the password or the MFA code; they needed the victim to approve a consent screen, copy an authorization code, or enter a device code, any of which hands over a refresh token that is bound to the grant, not to the credential. Restrict user consent to verified publishers with low-risk permissions only (or admin-only for higher-risk tenants), turn on the admin consent workflow with multiple reviewers, alert on new OAuth grants requesting offline_access or other high-risk scopes, and when you find a malicious grant, revoke it at the OAuth2PermissionGrant level with Microsoft Graph PowerShell, not just at the user's password. Test both halves before trusting either one: that an unapproved app is actually blocked, and that a previously issued access token keeps working until its natural expiry even after you revoke the grant behind it.
Frequently asked questions
Why does resetting a user's password not stop an OAuth consent phishing attack?
A password reset invalidates the password used to authenticate, but it does not touch a separate OAuth refresh token that was already issued under a consent grant to a malicious application. The refresh token is bound to that OAuth grant in the directory, not to the user's credential, so it keeps working after the password changes. Stopping the attacker requires directly revoking the OAuth2PermissionGrant and the associated app role assignments, in addition to resetting the password and revoking the user's own sign-in sessions.
What is the difference between ConsentFix and EvilTokens?
ConsentFix, documented by Push Security, is a browser-native attack that abuses the Azure CLI's implicit first-party trust in Entra ID tenants, tricking a victim into copying a localhost redirect URL containing an OAuth authorization code back to a phishing page. EvilTokens is a phishing-as-a-service platform that instead weaponizes Microsoft's legitimate OAuth device code flow, directing victims to enter a code at microsoft.com/devicelogin so the attacker's device receives the resulting token. Both skip password and MFA capture entirely and instead steal a live OAuth token, which is why both survive a password reset.
How many organizations has the EvilTokens phishing-as-a-service kit affected?
According to Cloud Security Alliance research, the EvilTokens kit compromised more than 340 Microsoft 365 organizations across seven countries within approximately five weeks of its February 2026 launch, and was sold on Telegram for $299 to $499 per month. That figure refers to organizations, not to a much larger claimed count of individual users or accounts; verify any secondhand restatement of this number against the original research before citing it.
What is the difference between allowing user consent and requiring admin consent in Entra ID?
Allowing user consent lets any employee approve an OAuth app's permission request themselves, which is the setting that makes consent phishing viable, since the attacker only needs to convince one user rather than an administrator. Requiring admin consent routes every such request to an administrator (or, with the admin consent workflow enabled, to a group of designated reviewers) before the app can access tenant data. Microsoft's recommended middle ground is `microsoft-user-default-low`, which allows user consent only for apps from verified publishers and only for permissions the tenant has classified as low risk.
Does deleting a malicious app's service principal immediately kill the attacker's access?
It stops the app from being used to request new tokens and, per Microsoft's documented revocation procedure, invalidates the refresh token issued under that app's OAuth2PermissionGrant when the grant is removed. It does not retroactively invalidate an access token the attacker already obtained before revocation, since access tokens are bearer tokens valid until their natural expiry, typically under 90 minutes, and are not re-checked against the directory in real time without Continuous Access Evaluation. Full remediation also requires checking for persistence the attacker may have set up while the token was valid, such as new mailbox forwarding rules.
Can OAuth consent phishing be blocked by Conditional Access alone?
No. Conditional Access governs how a user authenticates, including MFA requirements and device compliance, but the consent screen for a third-party OAuth app is a separate authorization step that occurs after the user has already authenticated successfully and satisfied Conditional Access. A user can pass every Conditional Access check correctly and still approve a malicious consent request afterward. Blocking this requires the consent restriction and admin consent workflow controls described in this guide, layered on top of, not instead of, an existing Conditional Access baseline.
Sources & references
- Microsoft Security Blog: Defending SaaS-based applications against ShinyHunters OAuth abuse
- Push Security: ConsentFix
- Cloud Security Alliance: OAuth Device Code Phishing Hits 340+ Microsoft 365 Organizations
- Microsoft Learn: Configure how users consent to applications
- Microsoft Learn: Configure the admin consent workflow
- Microsoft Learn: Review permissions granted to enterprise applications
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
