Checking Audit Logs for Azure AD Consent Permission Grants

Looking for Azure AD Consent Permission Grants for High-Priority Permissions

This week, I wrote about Azure AD 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 Consents

If you examine the information returned by Azure AD for an app, you’ll see that although we can figure out the permissions assigned to the app, but Azure AD 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 for Azure AD Consent Permission Grant Audit Records

One way to discover the last time when an adjustment occurred to app permissions is to search the Azure AD 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 Azure AD 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.


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

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

Azure AD sends its audit data to the Office 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 Azure AD, checking the Office 365 audit log can deliver a result. The downside of using the Office 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 Azure AD 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.

A Script to Update

Now that we know how to query the Azure AD 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.

Leave a Reply

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