In a recent incident response engagement, we identified a threat actor using a legitimate enterprise application to perform malicious email campaigns towards the affected environment. The application in question was a SaaS CRM system called Contactually*, allowing for total user enumeration of the environment with the click of a button, as well as reporting the malicious emails in terms of number of emails opened, links clicked, and unsuccessful attempts.
In this engagement the application was used to create persistence in the environment as the application has the permissions to sign in as the user. The actor then uploaded a malicious attachment to the OneDrive folder of the specific user and started sharing the link with other users within the company.
What Did the Attack Look Like?
The entire process can be outlined in the list below:
- The threat actor gained access of a user account due to compromised credentials.
- Overflowing the legitimate user with MFA prompts, and due to prompt fatigue the user finally accepted the malicious sign-in attempt.
- The threat actor added an Enterprise Application to the Azure AD environment, granting consent to the affected user using the API endpoints:
- Once the application was added, the actor had a back door installed and could upload a malicious pdf file to the affected users OneDrive and start sharing this file internally to other users.
The simplicity of this attack was interesting, as we have seen these kinds of “illicit consent grant” attack previously, but that has often been with a malicious application hosted in a threat actor environment.
As listed, this attack used a number of well known attack vectors in order to be successful such as:
- Credential compromise.
- MFA prompt overflow.
- Common misconfigurations in Azure AD, due to default settings.
The thing that we picked up on was the sign-in patterns for the enterprise application. There were several interactive sign-ins from a country with no prior activity from.
IoCs for this engagement:
AppID: 88fefdc6-a2c9-435c-9c90-44bc24cbc97e
IP address: 193.176.84.96
As showcased by the screenshot below:
How Did This Happen, What Are the Default Settings in Azure Active Directory?
The default setting in Azure AD is that all users can consent to Enterprise Application without the need for admin consent. This is really a setting more organizations should harden immediately. This setting allows for as we saw in our investigation addition of any application, the default settings in Azure AD are as follows:
A potential threat actor can leverage this setting to gain access to the environment and can access any data the app requests permissions for, allowing for data exfiltration, allowing for persistence, and for potential lateral movements in the cloud.
Preferred settings for Enterprise Applications are:
If You’ve Got an Application You Need to Revoke Permissions For, Use the Scripts Provided by Azure AD to Achieve This:
Remove User Assignments From the Application:
Connect-AzureAD # Get Service Principal using objectId $sp = Get-AzureADServicePrincipal -ObjectId "<objectid>" # Get Azure AD App role assignments using objectID of the Service Principal $assignments = Get-AzureADServiceAppRoleAssignment -ObjectId $sp.ObjectId -All $true # Remove all users and groups assigned to the application $assignments | ForEach-Object { if ($_.PrincipalType -eq "User") { Remove-AzureADUserAppRoleAssignment -ObjectId $_.PrincipalId -AppRoleAssignmentId $_.ObjectId } elseif ($_.PrincipalType -eq "Group") { Remove-AzureADGroupAppRoleAssignment -ObjectId $_.PrincipalId -AppRoleAssignmentId $_.ObjectId } }
Revoke API Permissions:
Connect-AzureAD # Get Service Principal using objectId $sp = Get-AzureADServicePrincipal -ObjectId "<objectid>" # Get all delegated permissions for the service principal $spOAuth2PermissionsGrants = Get-AzureADOAuth2PermissionGrant -All $true| Where-Object { $_.clientId -eq $sp.ObjectId } # Remove all delegated permissions $spOAuth2PermissionsGrants | ForEach-Object { Remove-AzureADOAuth2PermissionGrant -ObjectId $_.ObjectId } # Get all application permissions for the service principal $spApplicationPermissions = Get-AzureADServiceAppRoleAssignedTo -ObjectId $sp.ObjectId -All $true | Where-Object { $_.PrincipalType -eq "ServicePrincipal" } # Remove all delegated permissions $spApplicationPermissions | ForEach-Object { Remove-AzureADServiceAppRoleAssignment -ObjectId $_.PrincipalId -AppRoleAssignmentId $_.objectId }
Revoke Refresh Tokens:
Connect-AzureAD # Get Service Principal using objectId $sp = Get-AzureADServicePrincipal -ObjectId "<objectid>" # Get Azure AD App role assignments using objectID of the Service Principal $assignments = Get-AzureADServiceAppRoleAssignment -ObjectId $sp.ObjectId -All $true | Where-Object {$_.PrincipalType -eq "User"} # Revoke refresh token for all users assigned to the application $assignments | ForEach-Object { Revoke-AzureADUserAllRefreshToken -ObjectId $_.PrincipalId }
These scripts can be found in your Azure AD tenant at the following location:
YouTube Video on How These Attacks Can Look
My colleagues STÖK and Fabio created a YouTube video in February 2021 about these types of attacks, the difference here is that the actor used a legitimate app to achieve persistence. View the video:
*We have reached out to Contactually prior to publishing this to give them a chance to comment on the issue, but they have declined our request.