Occasionally, we find something in an environment that just looks off. It’s not always abundantly clear why it looks wrong, but clear that a deeper understanding is required. This was the case with seeing Base64 certificate data (“MII…” strings) stored with App Registration “manifests” in Azure Active Directory.
In this blog, we will share the technical details on how we found and reported CVE-2021-42306 (CredManifest) to Microsoft. In addition to Microsoft’s remediation guidance, we’ll explain the remediation steps organizations can take to protect their Azure environments.
So, what does this mean for your organization? Read our press release to explore the business impact of the issue.
Due to a misconfiguration in Azure, Automation Account “Run as” credentials (PFX certificates) were being stored in cleartext, in Azure Active Directory (AAD). These credentials were available to anyone with the ability to read information about App Registrations (typically most AAD users). These credentials could then be used to authenticate as the App Registration, typically as a Contributor on the subscription containing the Automation Account.
The Source of the Issue
This issue stems from the way the Automation Account “Run as” credentials are created when creating a new Automation Account in Azure. There appears to have been logic on the Azure side that stores the full PFX file in the App Registration manifest, versus the associated public key.
We can see this by creating a new Automation Account with a “Run as” account in our test tenant. As an integrated part of this process, we are assigning the Contributor role to the “Run as” account, so we will need to use an account with the Owner role to complete this step. We will use the “BlogExample” Automation Account as our example:
Take note that we are also selecting the “Create Azure Run As account” setting as “Yes” for this example. This will create a new service principal account that the Automation Account can use within running scripts. By default, this service principal will also be granted the Contributor role on the subscription that the Automation Account is created in.
We’ve previously covered Automation Accounts on the NetSPI technical blog, so hopefully this is all a refresher. For additional information on Azure Automation Accounts, read:
- Get-AzPasswords: Encrypting Automation Password Data
- Maintaining Azure Persistence via Automation Accounts
- Using Azure Automation Accounts to Access Key Vaults
Once the Automation and “Run as” Accounts are created, we can then see the new service principal in the App Registrations section of the Azure Active Directory blade in the Portal.
By selecting the display name, we can then see the details for the App Registration and navigate to the “Manifest” section. Within this section, we can see the “keyCredentials”.
The “value” parameter is the Base64 encoded string containing the PFX certificate file that can be used for authentication. Before we can authenticate as the App Registration, we will need to convert the Base64.
Manual Extraction of the Credentials
For the proof of concept, we will copy the certificate data out of the manifest and convert it to a PFX file.
This can be done with two lines of PowerShell:$testcred = “MIIJ/QIBAzCCC[Truncated]=”[IO.File]::WriteAllBytes(“$PWD\BlogCert.pfx”,[Convert]::FromBase64String($testcred))
This will decode the certificate data to BlogCert.pfx in your current directory.
Next, we will need to import the certificate to our local store. This can also be done with PowerShell (in a local administrator session):Import-PfxCertificate -FilePath “$PWD\BlogCert.pfx” -CertStoreLocation Cert:\LocalMachine\My
Finally, we can use the newly installed certificate to authenticate to the Azure subscription as the App Registration. This will require us to know the Directory (Tenant) ID, App (Client) ID, and Certificate Thumbprint for the App Registration credentials. These can be found in the “Overview” menu for the App Registration and the Manifest.
In this example, we’ve cast these values to PowerShell variables ($thumbprint, $tenantID, $appId).
With these values available, we can then run the Add-AzAccount command to authenticate to the tenant. Add-AzAccount -ServicePrincipal -Tenant $tenantID -CertificateThumbprint $thumbprint -ApplicationId $appId
As we can see, the results of Get-AzRoleAssignment for our App Registration shows that it has the Contributor role in the subscription.
Since we’re penetration testers and want to automate all our attacks, we wrote up a script to help identify additional instances of this issue in tenants. The PowerShell script uses the Graph API to gather the manifests from AAD and extract the credentials out to files.
The script itself is simple, but it uses the following logic:
- Get a token and query the following endpoint for App Registration information – “https://graph.microsoft.com/v1.0/myorganization/applications/”
- For each App Registration, check the “keyCredentials” value for data and write it to a file
- Use the Get-PfxData PowerShell function to validate that it’s a PFX file
- Delete any non-PFX files and log the display name and ID for affected App Registrations to a file for tracking
For the proof of concept that I submitted to MSRC for this vulnerability, I also created a new user (noaccess) in my AAD tenant. This user did not have any additional roles applied to it, and I was able to use the account to browse to the AAD menu in the Portal and view the manifest for the App Registration.
By gaining access to the App Registration credential, my new user could then authenticate to the subscription as the Contributor role for the subscription. This is an impactful privilege escalation, as it would allow any user in this environment to escalate to Contributor of any subscription with an Automation Account.
For additional reference, Microsoft’s documentation for default user permissions indicates that this is expected behavior for all member users in AAD: https://docs.microsoft.com/en-us/azure/active-directory/fundamentals/users-default-permissions.
Below is a detailed overview of how I remediated the issue in a client environment, prior to Microsoft’s fix:
When the “Run as” certificate has been renewed, the new certificate will have its own entry in the manifest. This value will be the public certificate of the new credential.
One important remediation step to note here is the removal of the previous certificate. If the previous credential has not yet expired it will still be viable for authentication. To fully remediate the issue, the new certificate must be generated, and the old certificate will need to be removed from the App Registration.
Now that our example has been remediated, we can see that the new manifest value decodes to the public key for the certificate.
I worked closely with the Microsoft Security Response Center (MSRC) to disclose and remediate the issue. You can read Microsoft’s disclosure materials online here.
A representative from MSRC shared the following details with NetSPI regarding the remediation steps taken by Microsoft:
- Impacted Azure services have deployed updates that prevent clear text private key data from being stored during application creation.
- Additionally, Azure Active Directory deployed an update that prevents access to private key data previously stored.
- Customers will be notified via Azure Service Health and should perform the mitigation steps specified in the notification to remediate any confirmed impacted Application and/or Service Principal.
Although Microsoft has updated the impacted Azure services, I recommend cycling any existing Automation Account “Run as” certificates. Because there was a potential exposure of these credentials, it is best to assume that the credentials may have been compromised.
This was one of the fastest issues that I’ve seen go through the MSRC pipeline. I really appreciate the quick turnaround and open lines of communication that we had with the MSRC team. They were really great to work with on this issue.
Below is the timeline for the vulnerability:
- 10/07/2021 – Initial report submitted to MSRC
- 10/08/2021 – MSRC assigns a case number to the submission
- October of 2021 – Back and forth emails and clarification with MSRC
- 10/29/2021 – NetSPI confirms initial MSRC remediation
- 11/17/2021 – Public Disclosure
While this was initially researched in a test environment, this issue was quickly escalated once we identified it in client environments. We want to extend special thanks to the clients that worked with us in identifying the issue in their environment and their willingness to let us do a spot check for an unknown vulnerability.
NetSPI initially discovered this issue with Automation Account “Run as” certificates. MSRC’s blog post details that two additional services were affected: Azure Migrate and Azure Site Recovery. These two services also create App Registrations in Azure Active Directory and were affected by the same issue which caused private keys to be stored in App Manifests. It is also possible that manually created Azure AD applications and Service Principals had private keys stored in the same manner.
We recommend taking the same remediation steps for any service principals associated with these services. Microsoft has published tooling to help identify and remediate the issue in each of these scenarios. Their guides and scripts are available here: https://github.com/microsoft/aad-app-credential-tools.