Bloodhound

Managed Identity Attack Paths, Part 3: Function Apps

Intro and Prior Work

In this three part blog series we have explored attack paths that emerge out of Managed Identity assignments in three Azure services: Automation Accounts, Logic Apps, and Function Apps.

In Part 1 we looked at attack paths that emerge out of Automation Account Managed Identity Assignments and Run As configurations. In Part 2 we looked at Logic Apps. Now in Part 3 we are looking at Function Apps.

Managed Identity assignments are an extremely effective security control that prevent the accidental exposure of credentials by removing this requirement to store or use credentials in the first place. Instead of storing and sending credentials, Azure knows that your script is allowed to authenticate as a specific Service Principal.

You should absolutely be using Managed Identity assignments in Azure instead of storing or accessing credentials.

But Managed Identities introduce a new problem: they can quickly create identity-based attack paths in Azure that may lead to escalation of privilege opportunities. In this series we will explore how those attack paths emerge, how they can be practically abused by an attacker, and how we as defenders can discover, mitigate, and prevent the future emergence of those attack paths.

I am not aware of any prior work specific to abuse Function App role assignments or Managed Identity assignments. If you have prior work in this area please contact me and I will add that here.

What are Function Apps?

Functions are one of several services falling under the umbrella of “Azure Automation”. Azure admins can create functions using a variety of language (C#, Java, PowerShell, etc.), then run those functions on-demand in Azure. Functions are hosted and grouped together in Azure using Function Apps.

Let’s look at a very simple example. In my example Function App called “MyCoolFunctionApp”, I’ve created a simple function called “HttpTrigger1”:

If we click into HttpTrigger1 and click on “Code + Test”, we can see what this function is actually doing. In this very basic example it is simply running whoami to tell us what user this script is running as:

We can execute this function on the same page by clicking “Test/Run”, then clicking “Run” and observing the output:

As you can see, we are running as the user iis apppoolmawsfnplaceholder105_f_v4_powershell_7_x86.

Function Apps and Service Principals

Function Apps can of course do basically anything, and that includes performing actions against APIs that require authentication and authorization. You may want to have a Function App that automates the creation of a service principal and grants that service principal some sort of privilege in AzureAD. This is a common use-case for MSSPs.

Enter Managed Identities. As discussed in the introduction of this blog post, Managed Identities are a fantastic way to securely, automatically authenticate as a Service Principal without needing to store or retrieve credentials. Enabling a Managed Identity for a Function App couldn’t be easier. Just click “Identity” under “Account Settings” and toggle the “Status” option from “Off” to “On”, then click “Save”:

Let’s start thinking of these things in the form of a graph and how the various objects fit into a hierarchy. Our Function App, “MyCoolFunctionApp”, has a Managed Identity assignment to the service principal whose object ID starts with “8eea…”:

As you can see, the Function App finds itself within the greater hierarchy of AzureRM and AzureAD. The Service Principal associated with the Function App does not have any privileges by default. Let’s give this Service Principal some privileges and try to stay without the bounds of least privilege by giving it “Contributor” access on the resource group the Function App resides in. This would let the Service Principal create and manage resources in this resource group:

As configured, this setup doesn’t introduce any privilege escalation opportunities: if an attacker gains control of either the Service Principal or Function App, they’re just stuck in a loop. Let’s flesh this environment out a bit more by adding another Function App in the same Resource Group:

Let’s also grant “My Second Very Cool Function App” an identity it can authenticate as. But this time we’re going to also give this Service Principal an MS Graph App role of AppRoleAssignment.ReadWrite.All:

But the MS Graph App Role of “AppRoleAssignment.ReadWrite.All” has a very dangerous implication: it allows the Service Principal to escalate itself to Global Admin. You can read about how that’s possible in this blog post.

Now we have created a privilege escalation opportunity — if an attacker gains control of “My Cool Function App” or its associated Service Principal, they will be able to escalate up to Global Admin, gaining control of everything in this Azure environment.

Out of these discrete configurations emerges an attack path leading from the original Function App all the way to Global Admin:

Abusing Function App Managed Identity Assignments

If an attacker has sufficient privilege to create or edit an existing function, they can turn that into control of the Service Principal, gaining whatever privileges the Service Principal holds. These Azure role assignments allow for creating or editing a function:

  • Owner
  • Contributor

At time of writing, the resource-specific contributor role called “Website Contributor” cannot add functions to function apps.

Additionally, the following privilege allows one to grant themselves any of the above role assignments against the Function App:

  • User Access Administrator

There are several ways to tackle this problem, but for me the most straight-forward abuse is to extract a JSON Web Token (JWT) for the Service Principal, then use that JWT to authenticate as the Service Principal outside the scope of the Function App. Using the Azure Portal GUI, we will modify an existing function to run this PowerShell script:

using namespace System.Net
param($Request, $TriggerMetadata)
$resourceURI = “https://graph.microsoft.com/"
$tokenAuthURI = $env:MSI_ENDPOINT + “?resource=$resourceURI&api-version=2017–09–01”
$tokenResponse = Invoke-RestMethod -Method Get -Headers @{“Secret”=”$env:MSI_SECRET”} -Uri $tokenAuthURI
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::OK
Body = $tokenResponse
})

We can use the function “Test/Run” feature in the Azure GUI to run the function and see the output, which will include the JWT for the Service Principal associated with this Function App:

We can then use this JWT to authenticate as the Service Principal to, in this example, the Microsoft Graph APIs.

Prevention

There are several steps you should take, as a defender, to ensure these attack paths do not exist in your Azure environment:

Step 1: Audit and Remove Privileges Held by Service Principals

Your first step should be to find any service principals that have been granted the most dangerous privileges in Azure. Audit both the active and eligible assignments for the following AzureAD admin roles:

  • Global Administrator
  • Privileged Role Administrator
  • Privileged Authentication Administrator

You should also audit for any Service Principals that have been granted any of the following MS Graph app roles:

  • RoleManagement.ReadWrite.Directory
  • AppRoleAssignment.ReadWrite.All

If any service principal has been granted any of the above roles in AzureAD or MS Graph, you should immediately investigate that service principal for existing signs of misuse. You should also remove those role assignments from the service principals, if possible.

Step 2: Audit Privileges Held by Other Principals

Unfortunately you may not be able to easily or immediately remove privileges that have been granted to a service principal. Your next step then will be to limit the exposure of those highly privileged service principals by auditing the users, groups, and service principals that have been granted any of the following AzureAD admin roles:

  • Application Administrator (including those scoped specifically to the Service Principal)
  • Cloud Application Administrator (including those scoped specifically to the Service Principal)
  • Directory Synchronization Accounts
  • Hybrid Identity Administrator
  • Partner Tier1 Support
  • Partner Tier2 Support

You should also audit the explicit owners of service principals you identified in Step 1 where you cannot easily or immediately remove privileges.

You should also audit other service principals that have been granted any of the following MS Graph app roles:

  • Application.ReadWrite.All
  • ServicePrincipalEndpoint.ReadWrite.All

Any user, group, or service principal that has been granted any of the above AzureAD admin roles, explicit ownership, or MS Graph app roles will be able to take over the service principals identified in Step 1. If possible, and if necessary, remove all of these privileges.

Step 3: Audit Privileges Held Against the Function App

Unfortunately, you may not be able to immediately or easily remove privileges held by service principals associated with a Function App through a Managed Identity assignment. In that case, you can prevent the emergence of a privilege escalation opportunity by removing these Azure role assignments against the Function App where those principals have less privilege than the service principal associated with the Function App:

  • Owner
  • Contributor
  • User Access Administrator

Detection

Azure logs come in handy several different ways here:

Detecting Function App Edits

An attacker may choose to edit an existing function or add their own malicious function. Either way, the “Update Web Apps Functions” log will fire, telling you who changed the function and at what time:

Unfortunately, these logs do not tell you what has changed in an existing function, so these may turn out to be very high-noise, low-signal logs to alert on.

Detecting New, Dangerous Privileges Granted to Service Principals

Once you’ve verified that no service principals have the most dangerous privileges in AzureAD, you will want to put alerting in place to warn you if someone grants a Service Principal one of those dangerous privileges. When a Service Principal is granted an AzureAD admin role, the “Add member to role” log fires, telling you who granted what privilege to what principal:

You should produce and triage an alert any time a service principal is granted one of the following most dangerous AzureAD admin roles:

  • Global Administrator (aka “Company Administrator”)
  • Privileged Role Administrator
  • Privileged Authentication Administrator

Additionally, when a service principal is granted an MS Graph app role, the “Add app role assignment to service principal” log fires, telling you who gave what app role to what:

You should produce and triage alerts any time a service principal is granted one of the following app roles against the MS Graph resource app:

  • RoleManagement.ReadWrite.Directory
  • AppRoleAssignment.ReadWrite.All

Conclusion

In this 3 part series we saw how attackers can abuse managed identity assignments for Automation Accounts, Logic Apps, and Function Apps. We saw how attack paths creating privilege escalation opportunities can emerge and how you as a defender can discover and mitigate those paths, as well as how you can prevent the future emergence of such attack paths.

References

https://docs.microsoft.com/en-us/azure/azure-functions/functions-overview

https://docs.microsoft.com/en-us/azure/automation/automation-services


Managed Identity Attack Paths, Part 3: Function Apps was originally published in Posts By SpecterOps Team Members on Medium, where people are continuing the conversation by highlighting and responding to this story.