Tutorials
Investigating a malicious OAuth app consent in Entra ID
A user approved an unknown app with access to their mail. How to find the consent, judge the app, see what it accessed and remove it, with KQL for Sentinel.
Published 3 min readReviewed by The Ledger EditorIntermediate
Most account compromise investigations end with a password reset. Consent phishing does not. Instead of stealing a password, the attacker gets the user to approve a malicious app, which then receives its own tokens to read mail or files. The password reset changes nothing, and MFA does not help, because the user completed it before clicking Accept. MITRE ATT&CK tracks the goal as Steal Application Access Token (T1528).
This tutorial follows one investigation from start to finish: find the consent, decide whether the app is malicious, find out what it did, remove it, and make the next attempt harder.
What you need
- Access to the Microsoft Entra admin center with at least the Cloud Application Administrator role to review and revoke app permissions.
- For the queries, Microsoft Sentinel (or a Log Analytics workspace) receiving Entra ID audit logs and sign-in logs, which land in the
AuditLogs,SigninLogsandAADNonInteractiveUserSignInLogstables. - Microsoft's own guidance, which this tutorial follows: Detect and remediate illicit consent grants and the app consent incident response playbook.
The investigation
Find the consent
Every consent is recorded in the Entra audit log as the activity Consent to application. In the admin center, open Entra ID → Monitoring & health → Audit logs and filter by that activity. In Sentinel, this query lists recent consents with the details you need:
app-consents.kql AuditLogs | where TimeGenerated > ago(30d) | where OperationName == "Consent to application" | extend User = tostring(InitiatedBy.user.userPrincipalName), AppName = tostring(TargetResources[0].displayName), AppObjectId = tostring(TargetResources[0].id) | mv-apply Prop = TargetResources[0].modifiedProperties on ( summarize Props = make_bag(bag_pack(tostring(Prop.displayName), tostring(Prop.newValue)))) | extend IsAdminConsent = tostring(Props["ConsentContext.IsAdminConsent"]), Permissions = tostring(Props["ConsentAction.Permissions"]) | project TimeGenerated, User, AppName, AppObjectId, IsAdminConsent, Permissions, Result | order by TimeGenerated descPay special attention to
IsAdminConsent. When it isTrue, someone granted the app access on behalf of the whole organization, which makes the incident far more serious.Decide whether the app is malicious
Open the app under Entra ID → Enterprise apps → All applications and check:
- Publisher: does it show a verified publisher? Malicious apps are rarely verified, and their names often imitate real products ("Office Document Viewer", "Secure PDF").
- Permissions: what did the user grant? Mail access (
Mail.Read,Mail.ReadWrite,Mail.Send), file access (Files.Read.All) andoffline_access, which lets the app keep refreshing its tokens, are the combination attackers want. - Who else consented: if several users approved the same unfamiliar app in a short time, it was almost certainly a phishing campaign.
- Context: did the user receive a phishing email just before the consent? Does anyone in IT recognize the app?
An app with broad mail or file permissions, no verified publisher and no business owner should be treated as malicious until proven otherwise.
Find out what the app did
Delegated apps act as the user, so their activity appears in the user's sign-in logs with the app's name. Most of it is non-interactive, because the app uses tokens in the background:
app-sign-ins.kql let appName = "Secure PDF Viewer"; // the AppName from step 1 union SigninLogs, AADNonInteractiveUserSignInLogs | where TimeGenerated > ago(30d) | where AppDisplayName == appName | summarize SignIns = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), IPs = make_set(IPAddress, 20) by UserPrincipalName, AppId | order by SignIns descTake the
AppIdfrom the result and keep it in your notes: it identifies the app more reliably than its display name. If you have Microsoft Purview audit or Defender for Cloud Apps, search there for the app's mail and file activity to estimate what was read or downloaded. That estimate decides whether this becomes a data breach.Contain it
Act in this order, so the app loses access immediately:
- Stop the app from signing in: on the app's Properties page, set Enabled for users to sign-in? to No and save. This blocks new tokens for everyone at once.
- Revoke the permission grants: admin-consented permissions can be revoked on the app's Permissions page. Permissions granted by individual users cannot be revoked in the portal; remove them with Microsoft Graph PowerShell (
Remove-MgOauth2PermissionGrant), as described in Review permissions granted to enterprise applications. - Revoke the user's sessions:
Revoke-MgUserSignInSessioninvalidates the user's refresh tokens, so any tokens already issued stop working when they next need refreshing. - Check the mailbox: look for inbox rules, forwarding or sent messages created while the app had access.
Keep the app disabled rather than deleting it until the investigation is finished, so the evidence remains.
Make the next attempt harder
The strongest fix is to decide who can approve apps. In Enterprise apps → Consent and permissions, Microsoft recommends allowing user consent only for apps from verified publishers and only for permissions you classify as low impact (Configure how users consent to applications). Everything else goes to administrators.
To avoid blocking legitimate work, turn on the admin consent workflow: users can request an app, and an administrator reviews it before anyone grants access. Combined with least privilege for the apps you do approve, this removes most of the attack surface.
Finally, review existing grants regularly. Apps approved years ago, with broad permissions and no owner, are worth removing even if they are not malicious.
Why it matters for a SOC analyst
Consent phishing gets around the controls people trust most: strong passwords, MFA and even Conditional Access on the sign-in itself. When you investigate any phished Microsoft 365 account, make "which apps has this user approved?" a standard question, right next to "which inbox rules exist?" A malicious app left in place keeps reading mail long after the incident was closed.
Terms in this article
- Consent phishing
- An attack that tricks a user into granting a malicious cloud app permission to their data, giving the attacker access without stealing a password.
- Phishing
- A message that impersonates someone trustworthy to trick the recipient into clicking a link, opening a file, sharing credentials or sending money.
- Multi-factor authentication
- Signing in with two or more different kinds of proof, such as a password plus a phone or security key, so a stolen password alone is not enough.
- KQL
- The read-only query language used by Microsoft Sentinel, Defender XDR advanced hunting, Azure Monitor and Azure Data Explorer.
- Conditional Access
- Microsoft Entra ID's policy engine that decides, at each sign-in, whether to allow access, require MFA or another control, or block.
- Incident response
- The organized process of detecting, analyzing, containing, eradicating and recovering from a security incident, then learning from it.
- Least privilege
- Giving every user, service and application only the access it needs to do its job, and only for as long as it needs it.
Last reviewed on by The Ledger Editor, Security Engineer