Protect Your Entra Apps

When companies implement the principle of least-privilege, the focus is typically narrowed to user identities. Recent attacks such as the Midnight Blizzard attack on Microsoft brought more attention to the importance of securing applications in an Entra tenant. Adversaries use applications to attempt to escalate privileges. Understanding how the technique works, and how to identify the potential attack vectors is an important step in protecting Entra. Evaluating the Entra attack surface isn’t as simple as defending user accounts holding administrative users, having an awareness of privileged applications is equally as important but far more complex.

A Focus on Permissions

Application permissions define the rights delegated to the application by the tenant. Applications are often configured with permissions for resources such as Exchange, SharePoint, Entra, etc. (via Graph) Practical365.com has discussed the need to monitor application permissions in other articles, such as this discussion about checking high-priority permissions granted to apps, and this article about creating an application permission inventory. Microsoft documentation also details all Graph permissions including warnings for some with the highest privileges.

The application permissions assigned to apps are viewable in the Entra Admin Center. However, there is no filter to view only applications with dangerous permissions. To identify a privileged application, the set of configured permissions for that application must be crosschecked against a list of privileges deemed to be highly privileged. Permissions that make an application “dangerous” vary from tenant to tenant. The below code sample shows how to use Microsoft Graph PowerShell SDK cmdlets to create a customizable array of highly privileged permissions. All application permissions in the tenant are then checked against this array. Every matching application permission is finally highlighted on screen. The full script can be found here. Using application permissions is the most common method to grant applications privileges, but applications can obtain permissions through other means. 

$highlyPrivilegedPermissions = @()
$highlyPrivilegedPermissions += [PSCustomObject]@{Permission = 'Application.ReadWrite.All'; Id = "1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9"}
$highlyPrivilegedPermissions += [PSCustomObject]@{Permission = 'AppRoleAssignment.ReadWrite.All'; Id = "06b708a9-e830-4db3-a914-8e69da51d44f"}
$highlyPrivilegedPermissions += [PSCustomObject]@{Permission = 'Calendars.ReadWrite'; Id = "ef54d2bf-783f-4e0f-bca1-3210c0444d99"}  
$highlyPrivilegedPermissions += [PSCustomObject]@{Permission = 'Directory.ReadWrite.All'; Id = "19dbc75e-c2e2-444c-a770-ec69d8559fc7"}
# Full permission list condensed for screenshot
$servicePrincipalArray = Get-MgServicePrincipal -All -Filter "tags/Any(x: x eq 'WindowsAzureActiveDirectoryIntegratedApp')"
foreach ($SP in $servicePrincipalArray) {
    [array]$roleAssignments = Get-MgServicePrincipalAppRoleAssignment -All -ServicePrincipalId $SP.Id
    foreach ($roleAssignment in $roleAssignments) {
        if ($highlyPrivilegedPermissions.Id -contains $roleAssignment.AppRoleId) {
            Write-Host $SP.DisplayName "is privileged due to this permission:" ($highlyPrivilegedPermissions | Where-Object {$_.Id -eq $roleAssignment.AppRoleId}).Permission
        }
    }
}

When the Service Principal of an application becomes a member of an Entra role, the application inherits the ability to perform all actions associated with the role. Microsoft lists all permissions for built-in roles here. Permissions applied in this way do not appear in the application section of the Entra admin center and are not discoverable when running the code above or other scripts focusing on application permissions. Detecting this type of permission requires looking in a completely different area of Entra.

Detecting Applications in Roles

In the simplest terms, Entra splits applications into two parts, an “application” and a “service principal”. Discussing the differences is unnecessarily complex for our purpose so let us assume the terminology is interchangeable. The concept needs to be introduced, as the “service principal” is the security object that can be a member of a role. Searching all roles in the Entra Admin Center for service principals is inefficient compared to using Graph PowerShell. The code below identifies service principals with membership in Entra roles. The full script can be found here.

$highlyPrivilegedRoles = @(
    [PSCustomObject]@{RoleName = "Application Administrator"; RoleID = "9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3" },
    [PSCustomObject]@{RoleName = "Application Developer"; RoleID = "cf1c38e5-3621-4004-a7cb-879624dced7c" },
    [PSCustomObject]@{RoleName = "Attribute Provisioning Administrator"; RoleID = "ecb2c6bf-0ab6-418e-bd87-7986f8d63bbe" },
    [PSCustomObject]@{RoleName = "Authentication Administrator"; RoleID = "c4e39bd9-1100-46d3-8c65-fb160da0071f" },
    [PSCustomObject]@{RoleName = "Authentication Extensibility Administrator"; RoleID = "25a516ed-2fa0-40ea-a2d0-12923a21473a" }
# Get all servicePrincipals in the tenant
$servicePrincipalArray = Get-MgServicePrincipal -All

# Get all assigned roles in the tenant. Returns ID for the principal object and role
$roleArray = Get-MgRoleManagementDirectoryRoleAssignment -All
foreach ($entry in $roleArray) {
    foreach ($role in $highlyPrivilegedRoles) {
        if ($role.RoleId -eq $entry.RoleDefinitionId) {
            # Found membership in a highly privileged role
            if ($servicePrincipalArray.Id -contains $entry.PrincipalId) {
                # Found membership in a highly privileged role that is a servicePrincipal
                $privSP = $servicePrincipalArray | Where-Object { $_.Id -eq $entry.PrincipalId }
                Write-Host "$($privSP.DisplayName) is a member of this privileged role: $($role.RoleName)"
                if ($privSP.ServicePrincipalType -eq "ManagedIdentity") {
                    # Found Managed Identity servicePrincipal
                    Write-Host "$($privSP.DisplayName) is a Managed Identity"
                }
            }
         }
     }
}

Now that we have an understanding of how applications gain elevated access within an Entra tenant, it’s important to recognize how attackers take advantage of these privileged applications. Applications can’t run code in a GUI session like most administrators might be used to, instead, they authenticate programmatically, often leveraging a client secret (aka password). See this article for information about connecting to Microsoft Graph including app-only sessions

Client Secrets

A client secret is one option for applications to prove their identity when authenticating. Although they are less secure than X.509 certificates (the recommended authentication method), application developers still commonly use these “password-like” credentials, even if app secrets shouldn’t be used in production environments. Client secrets have an expiration date specified upon creation. Microsoft recommends less than 12-month expiration but allows a maximum of two-year expiration when an app secret is created with the Entra Admin center. This limitation does not apply when programmatically creating a client secret; the below code demonstrates the creation of a client secret valid for 100 years. 

$graphApp = New-MGApplication -DisplayName 'Malicious App'
$graphSPParameter = @{"AppId" = "$($graphApp.AppId)"}
New-MgServicePrincipal -BodyParameter $graphSPParameter

$passwordCred = @{
    displayName = 'Application Persistence 4eva'
    endDateTime = (Get-Date).AddYears(100)
 }
Add-MgApplicationPassword -applicationId $graphApp.Id -PasswordCredential $passwordCred

When a client secret is created, the clear-text password is randomly generated and only displayed once during the creation process. The responsibility then falls upon the developer to securely store and share the secret for the next 100 years. Concerns around the expiration of client secrets is due to the effort of ensuring the secret stays “secret” and is properly maintained in the event of accidental disclosure, administrative turnover, or other operational changes throughout its lifetime. 

Knowledge of a valid client secret allows anyone to create an app-only session using the application, demonstrated via the Graph PowerShell below. Application authentication does not adhere to MFA requirements or Conditional Access restrictions. Individuals with access to or knowledge of these credentials can impersonate the application and therefore use its assigned permissions. 

$tenantID = '2b912474-447e-4f29-9dbe-ea93221ce601'
$appID = '1fc417b8-29ef-4c9c-97a2-71a4f6055bc2'
$clearTextPassword = 'dwc8Q~sFrFwLecsMdkS6FxxMi.aI.iUmwUvQ5eoS'

$securePassword = ConvertTo-SecureString -String $clearTextPassword -AsPlainText -Force

$loginCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $appID, $securePassword

Connect-MgGraph -TenantId $tenantID -ClientSecretCredential $loginCredential

Detecting Dangerous Client Secrets

Identifying client secrets in the Entra Admin Center is a near-impossible task. Even for organizations with only a handful of applications, the GUI requires searching each application one by one. Even more concerning is the fact that the service principals can also possess a client secret. Client secrets attached to a service principal are invisible in the GUI. Graph PowerShell comes to the rescue. This code can search all applications and report the application client secrets that exist in a tenant.

More to the Story

This exploration of application permissions and client secrets only begins to tell the full story. Entra applications are a complex concept. Getting started with Entra application security means knowing your attack surface. Review and run some of the code provided in this article to report on applications with privileged permissions, applications in privileged roles, and applications utilizing dangerous secrets. Being able to identify privileged applications and their dangerous secrets is the next step in securing Entra.

If you’re interested in future development, watch and star the GitHub repo. Also, keep an eye out for more Practical 365 articles that explore application security.

About the Author

Brandon Colley

Brandon Colley has fifteen years of experience administering and securing Active Directory and Windows environments. Brandon is a Senior Security Consultant for Trimarc Security and specializes in providing “reality-based” AD and Azure AD security assessments. He has a strong desire to provide content and insights to the infosec community through blogs and speaking engagements. Brandon delivers material in a humorous yet effective manner with a focus on content built for a Blue Team through a Red lens

Leave a Reply