Skip Navigation
Get a Demo
 
 
 
 
 
 
 
 
 
Resources Blog Threat detection

ChatGPT in your inbox? Investigating Entra apps that request unexpected permissions

ChatGPT in your inbox? Investigating Entra apps that request unexpected permissions

How to observe, detect, investigate and mitigate against overly permissive Entra app consent

Matt Graeber
Originally published . Last modified .

As Red Canary continues to observe OAuth application attacks in the wild, our Threat Research team is pivoting off real-world tradecraft to anticipate new innovations in attack techniques.
The following research breaks down a hypothetical OAuth attack in Entra ID that leverages ChatGPT to ultimately gain access to a user’s email account. Using the framework we apply when analyzing data sources for detection, we’ll investigate detection and remediation strategies that can be applied more generally to OAuth consent attacks.

What could happen

In this scenario, the following event occurs:

At 2025-12-02T20:22:16, within Entra ID tenant ID 747930ee-9a33-43c0-9d5d-470b3fb855e7, TestUser@ContosoCorp.onmicrosoft.com (ID: 1daac687-c3b3-4aad-8111-4bac9568a064) added a new third-party service principal, ChatGPT (App ID: e0476654-c1d5-430b-ab80-70cbd947616a) and consented as a non-admin to the following Microsoft Graph (App ID: 00000003-0000-0000-c000-000000000000) OAuth permissions: Mail.Read offline_access profile openid. This action was performed from the following IP address: 3.89.177.26.

This ChatGPT application is indeed the legitimate OpenAI application that was investigated due to its use of one or more OAuth permissions that are frequently abused, in this case, Mail.Read. In the end, this investigation resulted in a benign classification but the investigation steps followed a similar sequence to an incident we observed in the wild.

Summarized at a higher level, this means that the ChatGPT app has access to read the emails of TestUser@ContosoCorp.onmicrosoft.com. The following questions may arise in the course of an investigation:

  1. Did the legitimate user behind the TestUser@ContosoCorp.onmicrosoft.com identity actually mean to use this application? Was the user somehow coerced into this consent?
  2. Is it authorized for this app to read this user’s email?
  3. Is the ChatGPT app actually from OpenAI or is it merely posing as a legitimate looking application?
  4. Is this app actually sanctioned within this tenant?

In order to answer these questions, we need data, specifically, the following Log Analytics AuditLogs events are required and are correlated via the CorrelationId property:

  1. OperationName: Consent to application
  2. OperationName: Add service principal

Here is the actual (albeit, obfuscated) data that was generated for this case study:

{
   "TenantId": "52672484-b4e1-402d-934c-a8e2fd9b05d1",
   "SourceSystem": "Azure AD",
   "TimeGenerated": "2025-12-02T20:22:16.1185371Z",
   "ResourceId": "/tenants/747930ee-9a33-43c0-9d5d-470b3fb855e7/providers/Microsoft.aadiam",
   "OperationName": "Add service principal",
   "OperationVersion": "1.0",
   "Category": "ApplicationManagement",
   "ResultType": "",
   "ResultSignature": "None",
   "ResultDescription": "",
   "DurationMs": "0",
   "CorrelationId": "f540cbd8-9ec4-4d0e-855c-86e8916c3a1b",
   "Resource": "Microsoft.aadiam",
   "ResourceGroup": "Microsoft.aadiam",
   "ResourceProvider": "",
   "Identity": "Azure ESTS Service",
   "Level": "4",
   "Location": "",
   "AdditionalDetails": [
     {
       "key": "User-Agent",
       "value": "EvoSTS"
     },
     {
       "key": "AppId",
       "value": "e0476654-c1d5-430b-ab80-70cbd947616a"
     },
     {
       "key": "AppOwnerOrganizationId",
       "value": "a48cca56-e6da-484e-a814-9c849652bcb3"
     }
   ],
   "Id": "Directory_f540cbd8-9ec4-4d0e-855c-86e8916c3a1b_XC60C_97530478",
   "InitiatedBy": {
     "user": {
       "displayName": "Azure ESTS Service",
       "id": "1daac687-c3b3-4aad-8111-4bac9568a064",
       "userPrincipalName": "TestUser@ContosoCorp.onmicrosoft.com",
       "ipAddress": "3.89.177.26",
       "roles": []
     }
   },
   "LoggedByService": "Core Directory",
   "Result": "success",
   "ResultReason": "",
   "TargetResources": {
     "id": "07ec4c16-2cc4-4cd7-b6e3-95a9ba007a21",
     "displayName": "ChatGPT",
     "type": "ServicePrincipal",
     "modifiedProperties": [
       {
         "displayName": "AccountEnabled",
         "oldValue": [],
         "newValue": [true]
       },
       {
         "displayName": "AppAddress",
         "oldValue": [],
         "newValue": [
           {
             "AddressType": 0,
             "Address": "http://localhost:5000/hermes/connectors/oauth",
             "ReplyAddressClientType": 1,
             "ReplyAddressIndex": null,
             "IsReplyAddressDefault": false
           },
           {
             "AddressType": 0,
             "Address": "https://platform.api.openai.org/hermes/connectors/oauth",
             "ReplyAddressClientType": 1,
             "ReplyAddressIndex": null,
             "IsReplyAddressDefault": false
           },
           {
             "AddressType": 0,
             "Address": "https://platform.openai.com/hermes/connectors/oauth",
             "ReplyAddressClientType": 1,
             "ReplyAddressIndex": null,
             "IsReplyAddressDefault": false
           },
           {
             "AddressType": 0,
             "Address": "https://tailor.openai.com/api/v1/oauth/callback",
             "ReplyAddressClientType": 1,
             "ReplyAddressIndex": null,
             "IsReplyAddressDefault": false
           },
           {
             "AddressType": 0,
             "Address": "https://connectors.api.openai.com/connector/oauth_callback/ios_relay",
             "ReplyAddressClientType": 1,
             "ReplyAddressIndex": null,
             "IsReplyAddressDefault": false
           },
           {
             "AddressType": 0,
             "Address": "https://chatgpt.com/ccc/o365connector-business-dac4c231-bc0f-4d07-8b4c-3e1f3ee122ae/oauth/callback",
             "ReplyAddressClientType": 1,
             "ReplyAddressIndex": null,
             "IsReplyAddressDefault": false
           },
           {
             "AddressType": 0,
             "Address": "https://chatgpt.com/ccc/o365connector-personal-b9ce8873-ed1f-405d-97b4-51ca6b2a4f3f/oauth/callback",
             "ReplyAddressClientType": 1,
             "ReplyAddressIndex": null,
             "IsReplyAddressDefault": false
           },
           {
             "AddressType": 0,
             "Address": "https://chatgpt.com/connector_platform_oauth_redirect",
             "ReplyAddressClientType": 1,
             "ReplyAddressIndex": null,
             "IsReplyAddressDefault": false
           }
         ]
       },
       {
         "displayName": "AppPrincipalId",
         "oldValue": [],
         "newValue": ["e0476654-c1d5-430b-ab80-70cbd947616a"]
       },
       {
         "displayName": "DisplayName",
         "oldValue": [],
         "newValue": ["ChatGPT"]
       },
       {
         "displayName": "ServicePrincipalName",
         "oldValue": [],
         "newValue": ["e0476654-c1d5-430b-ab80-70cbd947616a","api://e0476654-c1d5-430b-ab80-70cbd947616a"]
       },
       {
         "displayName": "Credential",
         "oldValue": [],
         "newValue": [{
           "CredentialType": 2,
           "KeyStoreId": "291154f0-a9f5-45bb-87be-9c8ee5b6d62c",
           "KeyGroupId": "291154f0-a9f5-45bb-87be-9c8ee5b6d62c"
         }]
       },
       {
         "displayName": "ServicePrincipalTag",
         "oldValue": [],
         "newValue": ["WindowsAzureActiveDirectoryIntegratedApp","apiConsumer","webApp"]
       },
       {
         "displayName": "Included Updated Properties",
         "oldValue": null,
         "newValue": "AccountEnabled, AppAddress, AppPrincipalId, DisplayName, ServicePrincipalName, Credential, ServicePrincipalTag"
       },
       {
         "displayName": "TargetId.ServicePrincipalNames",
         "oldValue": null,
         "newValue": "e0476654-c1d5-430b-ab80-70cbd947616a;api://e0476654-c1d5-430b-ab80-70cbd947616a"
       }
     ],
     "administrativeUnits": []
   },
   "AADTenantId": "747930ee-9a33-43c0-9d5d-470b3fb855e7",
   "ActivityDisplayName": "Add service principal",
   "ActivityDateTime": "2025-12-02T20:22:16.1185371Z",
   "AADOperationType": "Add",
   "Type": "AuditLogs"
 },
 {
   "TenantId": "52672484-b4e1-402d-934c-a8e2fd9b05d1",
   "SourceSystem": "Azure AD",
   "TimeGenerated": "2025-12-02T20:22:16.2365366Z",
   "ResourceId": "/tenants/747930ee-9a33-43c0-9d5d-470b3fb855e7/providers/Microsoft.aadiam",
   "OperationName": "Consent to application",
   "OperationVersion": "1.0",
   "Category": "ApplicationManagement",
   "ResultType": "",
   "ResultSignature": "None",
   "ResultDescription": "",
   "DurationMs": "0",
   "CorrelationId": "f540cbd8-9ec4-4d0e-855c-86e8916c3a1b",
   "Resource": "Microsoft.aadiam",
   "ResourceGroup": "Microsoft.aadiam",
   "ResourceProvider": "",
   "Identity": "Azure ESTS Service",
   "Level": "4",
   "Location": "",
   "AdditionalDetails": [
     {
       "key": "User-Agent",
       "value": "EvoSTS"
     },
     {
       "key": "AppId",
       "value": "e0476654-c1d5-430b-ab80-70cbd947616a"
     },
     {
       "key": "AppOwnerOrganizationId",
       "value": "a48cca56-e6da-484e-a814-9c849652bcb3"
     }
   ],
   "Id": "Directory_f540cbd8-9ec4-4d0e-855c-86e8916c3a1b_XC60C_97530533",
   "InitiatedBy": {
     "user": {
       "displayName": "Azure ESTS Service",
       "id": "1daac687-c3b3-4aad-8111-4bac9568a064",
       "userPrincipalName": "TestUser@ContosoCorp.onmicrosoft.com",
       "ipAddress": "3.89.177.26",
       "roles": []
     }
   },
   "LoggedByService": "Core Directory",
   "Result": "success",
   "ResultReason": "",
   "TargetResources": {
     "id": "07ec4c16-2cc4-4cd7-b6e3-95a9ba007a21",
     "displayName": "ChatGPT",
     "type": "ServicePrincipal",
     "modifiedProperties": [
       {
         "displayName": "ConsentContext.IsAdminConsent",
         "oldValue": null,
         "newValue": "False"
       },
       {
         "displayName": "ConsentContext.IsAppOnly",
         "oldValue": null,
         "newValue": "False"
       },
       {
         "displayName": "ConsentContext.OnBehalfOfAll",
         "oldValue": null,
         "newValue": "False"
       },
       {
         "displayName": "ConsentContext.Tags",
         "oldValue": null,
         "newValue": "WindowsAzureActiveDirectoryIntegratedApp"
       },
       {
         "displayName": "ConsentAction.Permissions",
         "oldValue": null,
         "newValue": "[] => [[Id: FkzsB8Qs10y245WpugB6IUdjEXl8YehProtQM5deXYiHxqods8OtSoERS6yVaKBk, ClientId: 07ec4c16-2cc4-4cd7-b6e3-95a9ba007a21, PrincipalId: 1daac687-c3b3-4aad-8111-4bac9568a064, ResourceId: 79116347-617c-4fe8-ae8b-5033975e5d88, ConsentType: Principal, Scope:  Mail.Read offline_access profile openid, CreatedDateTime: , LastModifiedDateTime ]]; "
       },
       {
         "displayName": "TargetId.ServicePrincipalNames",
         "oldValue": null,
         "newValue": "e0476654-c1d5-430b-ab80-70cbd947616a;api://e0476654-c1d5-430b-ab80-70cbd947616a"
       }
     ],
     "administrativeUnits": []
   },
   "AADTenantId": "747930ee-9a33-43c0-9d5d-470b3fb855e7",
   "ActivityDisplayName": "Consent to application",
   "ActivityDateTime": "2025-12-02T20:22:16.2365366Z",
   "AADOperationType": "Assign",
   "Type": "AuditLogs"
 }

Who

This corresponds to the actor that performed the action.

TestUser@ContosoCorp.onmicrosoft.com (ID: 1daac687-c3b3-4aad-8111-4bac9568a064)

Field derivation

OperationFieldValueDescription
Consent to applicationInitiatedBy.user.userPrincipalNameTestUser@ContosoCorp.onmicrosoft.comThe human-readable name for the identity
Consent to applicationInitiatedBy.user.id1daac687-c3b3-4aad-8111-4bac9568a064The object ID of the identity which is the unique identifier for the identity

What

This corresponds to the resource that was affected and the specifics of the action that was performed on it.

“successfully added a new third-party service principal, ChatGPT (App ID: e0476654-c1d5-430b-ab80-70cbd947616a) and consented as a non-admin to the following OAuth permissions: Mail.Read offline_access profile openid.”

Field derivation

OperationFieldValueDescription
Add service principalResult“successfully”When Result is “success”, it indicates that the service principal
was successfully added to the tenant.
Add service principalOperationName“added a new … service principal”This indicates that the service principal was newly introduced to the tenant.
Add service principalAdditionalDetails['AppOwnerOrganizationId'] and AADTenantId“third-party”It is not an internally developed app because then
AdditionalDetails['AppOwnerOrganizationId']
would be the same as AADTenantId. It is not a first-party Microsoft app because AdditionalDetails['AppOwnerOrganizationId'] is neither
f8cdef31-a31e-4b4a-93e4-5f571e91255a
nor 72f988bf-86f1-41af-91ab-2d7cd011db47.
Since it is neither internal nor first-party, you can conclude that
it is a third-party application.
Consent to applicationTargetResources[0].displayNameChatGPTThe name of the service principal
Consent to applicationAdditionalDetails['AppId']e0476654-c1d5-430b-ab80-70cbd947616aThe globally unique identifier of the application
Consent to applicationOperationName“consented … to … OAuth permissions”Indicates that OAuth permissions were consented to
Consent to applicationTargetResources[0].modifiedProperties
['ConsentContext.IsAdminConsent'].newValue
“non-admin”A non-admin consent is performed when TargetResources[0].modifiedProperties
['ConsentContext.IsAdminConsent'].newValue

is “False”.
Consent to applicationTargetResources[0].modifiedProperties
['ConsentAction.Permissions'].newValue
Mail.Read offline_access profile openidThis property represents an oAuth2PermissionGrant instance which
indicates the specific OAuth permissions that were consented to.

Interpreting ConsentAction.Permissions values

Consider the following example from above:

[] => [[Id: FkzsB8Qs10y245WpugB6IUdjEXl8YehProtQM5deXYiHxqods8OtSoERS6yVaKBk, ClientId: 07ec4c16-2cc4-4cd7-b6e3-95a9ba007a21, PrincipalId: 1daac687-c3b3-4aad-8111-4bac9568a064, ResourceId: 79116347-617c-4fe8-ae8b-5033975e5d88, ConsentType: Principal, Scope:  Mail.Read offline_access profile openid, CreatedDateTime: , LastModifiedDateTime ]]; 

The structure of this data is not documented by Microsoft but it is clear that it comprises an array of one or more oAuth2PermissionGrant instances. In its most basic form, it has the following structure:

[[PREVIOUS_OAUTH2PERMISSIONGRANT_1],[PREVIOUS_OAUTH2PERMISSIONGRANT_2]] => [[NEW_OAUTH2PERMISSIONGRANT_1],[NEW_OAUTH2PERMISSIONGRANT_2]];

The oAuth2PermissionGrant instance above is broken down as follows:

  • Id: FkzsB8Qs10y245WpugB6IUdjEXl8YehProtQM5deXYiHxqods8OtSoERS6yVaKBk
    • This corresponds to the unique identifier for the OAuth permission grant. This value is necessary for performing targeted remediation. This value is composed of the combined ClientId, ResourceId, and PrincipalId values (in that order), which is then Base-64 encoded. See the Remediation section below for more details.
  • ClientId: 07ec4c16-2cc4-4cd7-b6e3-95a9ba007a21
    • This is the object ID (not the application ID) of the service principal instance that the permission grant is applied to.
  • PrincipalId: 1daac687-c3b3-4aad-8111-4bac9568a064
    • This is the object ID of the identity to which the permission grant applies. When ConsentType is AllPrincipals, this value is not populated.
  • ResourceId: 79116347-617c-4fe8-ae8b-5033975e5d88
    • This is the object ID (not the application ID) of the resource app that implements the requested scope. In most cases, this will correspond to the Microsoft Graph resource app.
  • ConsentType: Principal
    • When this value is Principal, it means that the granted permissions apply only to the identity specified by PrincipalId. When this value is AllPrincipals, it means that the scopes were granted to all users.
  • ScopeMail.Read offline_access profile openid
    • This is a space-delimited list of the granted OAuth permissions.
  • CreatedDateTime:
    • This has never been observed to be populated.
  • LastModifiedDateTime
    • This has never been observed to be populated.

In the example above, the permission string begins with [] => , meaning that it is a new OAuth permission grant.

Note: The permission string can contain multiple oAuth2PermissionGrant instances. In those cases, the ResourceId value will be unique, as it will contain a set of Scope values that are implemented in that particular resource app.

When

This corresponds to the time in which the action occurred.

“At 2025-12-02T20:22:16”

Field derivation

OperationFieldValueDescription
Consent to application ActivityDateTime2025-12-02T20:22:16.2365366ZThe date and time that this event occurred.

Where

This corresponds to the environment in which the action occurred.

“within Entra ID tenant ID 747930ee-9a33-43c0-9d5d-470b3fb855e7

Field derivation

OperationFieldValueDescription
Consent to applicationAADTenantId747930ee-9a33-43c0-9d5d-470b3fb855e7The tenant in which the action took place.

Whence

From where did the action originate?

“This action was performed from the following IP address: 3.89.177.26.”

Field derivation

OperationFieldValueDescription
Consent to applicationInitiatedBy.user.ipAddress3.89.177.26The IP address from which the actor performed the action.

How

This corresponds to the means by which the action occurred.

Field derivation

OperationFieldValueDescription
Consent to applicationAdditionalDetails['User-Agent']EvoSTSThe user agent that performed the action.
EvoSTS indicates that the consent
was performed via the typical
graphical UI consent prompt.

Detection

The detection strategy you may consider employing will depend on how you scope the technique and what your particular detection objectives are.

Technique scope

  • Are you concerned about non-admin users being phished and consenting to unsanctioned applications that either violate policy or are malicious?
  • Are you concerned about admins being targeted and granting privileges for privileged OAuth scopes?
  • Are you concerned about threat actors who have already compromised an identity creating an internal application that is then used in a phishing campaign?

Detection objective

  • What is the detection of malicious applications versus unsanctioned applications?
  • What is your detection volume tolerance?
  • What is your tolerance for false positives?
  • Do you have the ability to perform application enrichment and assess prevalence?

Each combination of scope and objective will dictate the detection strategy. As a good starting point, we recommend the following detection strategy that minimizes volume.

Detect a non-admin permission grant for a new, third-party application that was granted one or more commonly abused permissions.

Such a strategy can be broken down as follows:

  1. We are assuming that non-admin users are more likely to be targeted and being less tech-savvy are more likely to fail to recognize a suspicious consent request.
  2. The application doesn’t already exist in the tenant. An application that already exists in the tenant is more likely to be sanctioned.
  3. A malicious, adversary-controlled application will not be a first-party, Microsoft application. By excluding internal and 1st-party applications, we can reduce detection volume drastically.
  4. As of this writing, Microsoft’s default app consent policy blocks the following permissions for non-admins that are known to be abused:
    Calendars.Read, Calendars.Read.Shared, Calendars.ReadBasic, Calendars.ReadWrite, Calendars.ReadWrite.Shared, Chat.Read, Chat.ReadWrite, Files.Read.All, Files.ReadWrite.All, Mail.Read, Mail.Read.Shared, Mail.ReadBasic, Mail.ReadBasic.Shared, Mail.ReadWrite, Mail.ReadWrite.Shared, MailboxFolder.Read, MailboxFolder.ReadWrite, MailboxItem.Read, MailboxSettings.Read, MailboxSettings.ReadWrite, OnlineMeetings.Read, OnlineMeetings.ReadWrite, Sites.Read.All, Sites.ReadWrite.All

 

This detection strategy can be implemented with the following pseudo-logic:

OperationFieldConditionValueDescription
Consent to applicationTargetResources[0].modifiedProperties
['ConsentAction.Permissions'].newValue
Starts with[] =>It is a new consent for the user.
Consent to applicationTargetResources[0].modifiedProperties
['ConsentAction.Permissions'].newValue
Contains one
or more
of the following
See the list of risky scopes
above, e.g. Mail.Read
One or more of the known
abused, non-privileged permissions
Consent to applicationTargetResources[0].modifiedProperties
['ConsentContext.IsAdminConsent'].newValue
EqualsFalseA non-admin consent occurred.
Consent to application
AND Add service principal
CorrelationIdEqualsThe same CorrelationId
value for both events
The user consented to an application
that wasn’t already present in the tenant.
Add service principalAdditionalDetails
['AppOwnerOrganizationId']

AND AADTenantId
Does not equalNEITHER AADTenantId
NOR
f8cdef31-a31e-4b4a-93e4-5f571e91255a
NOR
72f988bf-86f1-41af-91ab-2d7cd011db47
The service principal added is
neither an internal app
nor a first-party application,
therefore, it is a third-party application.

Remediation

If it has been determined that an OAuth consent grant was malicious, the following immediate actions can be performed:

1. Remove the consent grant.

Using the permission grant ID value from the TargetResources[0].modifiedProperties['ConsentAction.Permissions'].newValue field in the Consent to application event, the following Microsoft Graph command can be executed:

Remove-MgBetaOAuth2PermissionGrant -OAuth2PermissionGrantId FkzsB8Qs10y245WpugB6IUdjEXl8YehProtQM5deXYiHxqods8OtSoERS6yVaKBk

2. Remove the service principal that was added to the tenant.

If the app added to the tenant has been deemed malicious or unsanctioned, it can be removed using the service principal ID in the TargetResources[0].id field in the Consent to application event. In the example above, this value was 07ec4c16-2cc4-4cd7-b6e3-95a9ba007a21. Here is a sample PowerShell Graph command that would remove the service principal:

Remove-MgServicePrincipal -ServicePrincipalId 07ec4c16-2cc4-4cd7-b6e3-95a9ba007a21

Mitigations

Fortunately, Microsoft supplies ample opportunities to mitigate against this technique. As discussed this technique relies upon a victim to have permission to perform the following two actions:

  1. Add a service principal to the tenant.
  2. Consent to OAuth permissions that the identity is permitted to consent to.

As Microsoft states, “by default, all users are allowed to consent to applications for permissions that don’t require administrator consent. For example, by default, a user can consent to allow an app to access their mailbox but can’t consent to allow an app unfettered access to read and write to all files in your organization.”

Microsoft supplies three options to mitigate against non-admin users introducing unvetted applications and over-provisioning of OAuth permissions:

  1. Safest by default but incurs an administrative burden: “Do not allow user consent.” This option requires an administrator (i.e. Global Administrator or Privileged Role Administrator) to approve all consent requests.
  2. Balances security while easing administrative burden and grants users the ability to consent to pre-approved OAuth permissions for “higher-trusted” applications: “Allow user consent for apps from verified publishers, for selected permissions. All users can consent for permissions classified as “low impact”, for apps from verified publishers or apps registered in this organization.”
  3. Microsoft’s recommended option for balancing security and flexibility: “Let Microsoft manage your consent settings. Automatically update your organization to Microsoft’s current user consent guidelines.”

References

 

 

Moving up the Assemblyline: Exposing malicious code in browser extensions

 

Hunting for malicious OpenClaw AI in the modern enterprise

 

Breaking down a supply chain attack leveraging a malicious Google Workspace OAuth app

 

The million-dollar front door and the tailgater: Why strong auth could fail at SaaS session integrity

Subscribe to our blog

Security gaps? We got you.

Sign up for our monthly email newsletter for expert insights on MDR, threat intel, and security ops—straight to your inbox.


 
 
Back to Top