Checking Audit Logs for Consent Permission Grants

Looking for Consent Permission Grants for High-Priority Permissions

This week, I wrote about applications with high-priority permissions, defined as Microsoft Graph and other permissions that attackers could exploit to access, update, and exfiltrate data. For example, if an app holds the Mail.ReadWrite application permission, it can read and write all mailboxes in the tenant. The script generates a report that’s posted to a Teams channel to allow administrators to review the applications holding the specified permissions.

Illicit Consent Permission Grants

If you examine the information returned by Entra ID (Azure AD) for an app, you’ll see that although we can figure out the permissions assigned to the app, but Entra ID doesn’t tell you who consented to the permission and when they consented. This could be an important sign that an attacker has managed to achieve an illicit consent grant, defined as occurring when:

the attacker creates an Azure-registered application that requests access to data such as contact information, email, or documents. The attacker then tricks an end user into granting that application consent to access their data either through a phishing attack.”

An illicit consent grant for access to an individual user’s data is bad. One that results in an administrator granting consent to an application for a high-priority permission that the attacker can subsequently leverage could be catastrophic.

Checking Audit Records for Consent Permission Grants

One way to discover the last time when an adjustment occurred to app permissions is to search the Entra ID audit logs for records for the Consent to application action, filtering the set to find those matching the service principal identifier for an app. I didn’t do this in the script described in the article, Let’s explore how a consent check might work.

First, let’s assume that the script has run and detected an application holding some high-priority permissions. Its properties might look like this:

DisplayName        : MalwareExample
ServicePrincipalId : 6df52e04-63b2-4007-af69-40430ee5a1d1
Publisher          : Office 365 for IT Pros
Permissions        : Mail.ReadWrite, Mail.Send
SPType             : Application
CreatedDate        : 12/09/2022 22:41
RecentApp          : True

To scan the Entra ID audit logs for any consent granted records for this app, we could use a command like this to see if an audit record exists. The search goes back 30 days.

[array]$AuditRecords = Get-MgAuditLogDirectoryAudit -Filter "activityDisplayName eq 'Consent to application' AND result eq 'Success' AND targetResources/any(tr:tr/id eq '$($app.serviceprincipalid)')" -top 1

If an audit record is found, it will look like this:

ActivityDateTime     : 12/09/2022 21:42:49
ActivityDisplayName  : Consent to application
AdditionalDetails    : {User-Agent, AppId}
Category             : ApplicationManagement
CorrelationId        : 005cc13f-9fd5-4b95-89ce-19802a7a785f
Id                   : Directory_005cc13f-9fd5-4b95-89ce-19802a7a785f_72CWK_111329857
InitiatedBy          : Microsoft.Graph.PowerShell.Models.MicrosoftGraphAuditActivityInitiator1
LoggedByService      : Core Directory
OperationType        : Assign
Result               : success
ResultReason         :
TargetResources      : {6df52e04-63b2-4007-af69-40430ee5a1d1}
UserAgent            :
AdditionalProperties : {}

We can see that the consent was granted a minute after the creation date for the app. That could be a suspicious sign, but it might also be the result of granting permissions immediately after creating an app.

The InitiatedBy property is a complex object. Parsing it out, we can eventually discover who granted consent.

$AuditRecords.InitiatedBy.user.UserPrincipalName
Admin@Office365itpros.com

Unfortunately, that’s about all we can find from the audit log using the Get-MgAuditLogDirectoryAudit cmdlet. Some additional information is available in the Entra ID admin center (Figure 1).

Audit Log detail for a Azure AD consent permission grant
Figure 1: Audit Log detail for a consent permission grant

Entra ID sends its audit data to the Microsoft 365 audit log and you can also search there using a command like this:

[array]$records = Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date).AddDays(1) -Formatted -ResultSize 5000 -Operations "Consent to application"

The Office 365 audit log stores information for 90 days (for accounts with Office 365 E3 licenses) or 365 days (accounts with Office 365 E5 licenses). If you don’t find audit records in Entra ID checking the Office 365 audit log can deliver a result. The downside of using the Microsoft 365 audit log is that it’s likely going to be slower to find any records because there’s more data to search and a specific search filter isn’t available as it when using Get-MgAuditLogDirectoryAudit to check Entra ID audit records.

The audit data is useful information that could help identify any problematic consent grants that might turn out to be illicit, but the data are only effective if people pay attention to permissions granted to apps, especially the high-profile permissions.

Updating Code to Check for Consent Permission Grants

Now that we know how to query the Entra ID audit logs to find records for consent grants for an app, it would be easy to update the script to include the check in the code. The hardest part is probably the update to include the audit information in the HTML body of the message to post to Teams. I’ll leave the script update as an exercise for the reader!

The key point is that Azure apps shouldn’t be left unsupervised. Whatever method you choose to check these apps, make sure it happens regularly and that someone is responsible for reviewing the reports and other outputs to detect any problems.


Learn more about how the Office 365 applications really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

8 Replies to “Checking Audit Logs for Consent Permission Grants”

  1. Hi Tony,
    I have a question for you, and I was hoping you could help me out. How can I generate a report of Admin Consent Requests received by Azure AD? I’m specifically looking for information such as who sent the consent request, which application was involved, what API permissions the application requested, and how many users have already requested the consent. I didn’t have much luck with googling.

      1. Hi Tony,

        Thank you for your response. I have been trying to extract the data from the portal using PowerShell or Graph since the data is visible on the Portal Itself, but I haven’t found a method yet. I humbly appreciate your reply. Wishing you a Merry Christmas and a Happy New Year! I hope you have some time off to spend with your loved ones. Cheers!

        PS: If you hear from Paul Cunningham, please let him know that his fans miss him 😊.

      2. What page is the data visible on? Have you tried using Fiddler to see what API calls the page makes to retrieve the data? The point is that not everything is available through public APIs.

        As to Paul Cunningham, I don’t think I have seen him since 2018…

  2. Hi Tony,
    Thanks again for your reply. I wish I could add a screenshot. The data is visible in the following location AzureAD(EntraID)->Enterprise Applications->Admin Consent Requests under My Pending Tab. If you click on any app, it has the data Requested by.

  3. Hi Chief,

    I have figured it out. The code is as follows. Both V1 and Beta work.

    ==============================================================
    $Report = [System.Collections.Generic.List[Object]]::new()

    $ConsentRequestApps = Get-MgBetaIdentityGovernanceAppConsentRequest -All

    foreach ($ConsentRequestApp in $ConsentRequestApps) {

    $RequestedUsers = $Null
    Write-Verbose -Verbose $($ConsentRequestApp.AppDisplayName)

    $RequestedUsers = Get-MgBetaIdentityGovernanceAppConsentRequestUserConsentRequest -AppConsentRequestId $ConsentRequestApp.Id

    foreach ($RequestedUser in $RequestedUsers) {

    $Reportline = [PSCustomObject]@{
    AppDisplayName = $ConsentRequestApp.AppDisplayName
    AppID = $ConsentRequestApp.AppId
    ConsentID = $ConsentRequestApp.Id
    PendingScopes = $ConsentRequestApp.PendingScopes.DisplayName -join ” ;”
    RequestStatus = $RequestedUser.Status
    CreatedDateTime = $RequestedUser.CreatedDateTime
    CreatedBy = $RequestedUser.CreatedBy.User.DisplayName
    CreatedByID = $RequestedUser.CreatedBy.User.Id

    }
    $Report.Add($Reportline)

    }

    }
    $Report | Export-Csv C:\Build\AppConsentRequests-Extended.csv -NoTypeInformation -Append -Encoding UTF8

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.