Monitoring entitlements for application identities - a look at Microsoft Azure AD
An attack surface does not consist only of network connected assets (servers, endpoints, etc.) but also identities having various degrees of access to an organization's IT environment.
Identities do not refer only to physical persons, but "robots" as well: in fact, as organizations automate processes, an increasing number of tasks with privileged access will invoke various internal and external APIs, access sensitive data, orchestrate everything from user onboarding to data integration between, say, ERP and CRM systems, etc.
These identities are too often hardcoded into scripts or programs as keys or passwords, and usually left unmanaged or even forgotten. Yet threat actors are actively searching for those once they gain a foothold into an environment: for ex. finding a powershell script performing backup might give an attacker access to credentials with access to a backup repository or another part of the IT infrastructure, thus enabling lateral movement and data destruction.
So it's crucial to have monitoring and governance around so called application identities.
Modern Identity and Access Management (IAM) solutions (often delivered as a service i.e. IDaaS), provide governance and identity management around users and applications, usually relying on OAuth2 concepts.
Azure Active Directory (now called Entra ID) is the IDaaS offering by Microsoft. It is increasingly being used as a complement to the old on-prem Active Directory in hybrid scenarios or even as standalone identity repository. It is also underpinning all Microsoft SaaS offerings, such as Microsoft365 applications.
Here we'll take a look on how to extract application identities from Entra ID i.e. the application identities used by various scripts and automated tasks, so we can keep tabs on this important aspect of the attack surface. We want to get:
a list of applications with credentials,
the associated permissions to other applications (scopes and roles),
any assigned built-in roles to Azure resources (for ex, an Azure Storage account, a DNS record or anything else in Azure).
Interestingly, there is no easy way to get this data synthesized via GUI (it's scattered around), so Powershell comes to the rescue. In this case, we will be using the AZ Powershell module, while the complete sample script is here.
First, we get a list of applications where secrets or application credentials are defined:
Get-AzADApplication | Where-Object -FilterScript {($_.KeyCredentials.Count -GT 0) -or ($_.PasswordCredentials.Count -GT 0)}
In this example, we get 2 applications having valid credentials:
DisplayName Id
----------- --
DNS LetsEncrypt Authorizer 087222c7-bdcc-4954-9111-e571b8b8dcb2
ERP Sync Client 111b781e-94fb-4b0d-8222-711e4b9577a5
Based on app names, the first one appears to perform automated Let's Encrypt certificate renewals/rotation, while the other is accessing our ERP system and perhaps reading data and exporting it into a data lake for BI consumption.
For each application, we can access the RequiredResourceAccess property which returns the permissions to other applications. These come in 2 forms: scopes and application roles. Each application in Azure Entra ID publishes a list of scope and role definitions: users and other apps can then request those scopes during access or get a role assigned by the administrator.
For example, a client task accessing an ERP application can request a scope to access financial data API endpoints. A role assigned to the client application might allow it to retrieve payroll accounting data.
In any case, we want to know which application has access to what data, so that we can manage the risks.
Finally, an application can have multiple Azure app roles assigned (many built-in are available, and custom one can be created), which define the level of access to particular Azure resources. For example, an app might be assigned the role "Storage Blob Data Contributor" to a particular block storage account within the Azure tenant. We get this data by using the command Get-AzRoleAssignment, and it's another piece of the puzzle which gives us context as to what data applications can reach.
When pieced together, the script might return something like the following:
AppDisplayName : DNS LetsEncrypt Authorizer
AppId : c56d44f1-1225-4899-ad37-8c28cf499bfd
AppCredentials : Used to renew certs for techinsights.pro (exp. 2025-03-27, Password Credential)
APIPermissions : Microsoft Graph: User.Read (Scope);
AzureRolesWithResources : DNS Zone Contributor: /subscriptions/73338222-fd5f-4d03-9dda-651524b50ca7/resourceGroups/CDN/providers/Microsoft.Network/dnszones/techinsights.pro
AppDisplayName : ERP Sync Client
AppId : eda810ce-0912-45fd-8fd2-e519d90d4f63
AppCredentials : Server sync client (exp. 2024-03-11, Password Credential)
APIPermissions : ERP: RWaccess (Scope); FullAccess (Role);
Microsoft Graph: User.Read (Scope);
AzureRolesWithResources :
For the first application above (DNS LetsEncrypt Authorizer) we can see it can consume Microsoft Graph API and it can also apparently write to a particular Azure resource: a DNS zone for a domain hosted in Azure. This is indeed a powerful privilege we want to know about (and also avoid expiration of the credential performing the certificate renewals: we don't want our web application to serve old TLS certificates!).
The second application has no access to Azure resources, but apparently has permission to access our ERP with full access role assigned - also a powerful permission.
Take a look at the full script here.