Azure Key Vault Tradecraft with BARK
Brief
This post details the existing and new functions in BARK that support adversarial tradecraft research relevant to the Azure Key Vault service. The latter part of the post shows an example of how a red team operator may use these commands during the course of an assessment.
Authentication
Azure Key Vault is one of the few services in Azure with a dedicated API for data plane operations. When performing calls to the Azure REST API and the Azure Key Vault REST API, you must provide authentication in the form of a bearer token. That token must have the correct audience.
BARK has the following functions for requesting tokens for use with the Azure REST API:
- Get-AzureRMTokenWithUsernamePassword
- Get-AzureRMTokenWithPortalAuthRefreshToken
- Get-AzureRMTokenWithClientCredentials
- Get-AzureRMTokenWithRefreshToken
BARK has the following functions for requesting tokens for use with the Azure Key Vault REST API:
- Get-AzureKeyVaultTokenWithUsernamePassword
- Get-AzureKeyVaultTokenWithClientCredentials
Enumeration
BARK has the following function for enumerating key vaults via the Azure REST API:
- Get-AllAzureRMKeyVaults
BARK has the following functions for enumerating key vault items via the Azure Key Vault REST API:
- Get-AzureRMKeyVaultSecrets
- Get-AzureRMKeyVaultSecretVersions
- Get-AzureRMKeyVaultKeys
- Get-AzureRMKeyVaultKeyVersions
- Get-AzureRMKeyVaultCertificates
Persistence
BARK has the following functions for manipulating permissions on key vaults and key vault items via the Azure REST API:
- New-AzureRMRoleAssignment
- New-AzureKeyVaultAccessPolicy
Collection/Credential Access
BARK has the following function for collecting key vault secret values via the Azure Key Vault REST API:
- Get-AzureRMKeyVaultSecretValue
Encryption/Decryption
BARK has the following functions for encrypting and decrypting data via the Azure Key Vault REST API:
- Protect-StringWithAzureKeyVaultKey
- Unprotect-StringWithAzureKeyVaultKey
An Example Walkthrough Showcasing these Functions
During a red team assessment, the operator may find they have read access into one or more Azure Resource Manager (ARM) subscriptions, giving them the ability to enumerate resources in the subscription(s). The operator wants to find all key vaults under a given subscription.
First they must request a token with ARM REST API as the audience. There are several ways to do this and all depend on what level of access the operator has. We will go with a simple example: the operator has plaintext credentials for a valid user. With those credentials, the operator can use BARK’s Get-AzureRMTokenWithUsernamePassword to request a token:
$ARMToken = (Get-AzureRMTokenWithUsernamePassword `
-Username "Username@contoso.onmicrosoft.com" `
-Password "PlainTextPasswordGoesHere" `
-TenantID "contoso.onmicrosoft.com").access_token
Next, the operator can identify all subscriptions they have read access into with BARK’s Get-AllAzureRMSubscriptions function:
$Subscriptions = Get-AllAzureRMSubscriptions -Token $ARMToken
To find all key vaults under each subscription, the operator can use PowerShell to loop through each subscription and pass its ID to BARK’s Get-AllAzureRMKeyVaults:
$KeyVaults = $Subscriptions | %{
Get-AllAzureRMKeyVaults -Token $ARMToken -SubscriptionID $_.subscriptionid
}
Now the operator can attempt to enumerate secrets, keys, and certificates under each key vault; however, the Azure Key Vault REST API serves these operations, so they must first get a token with the correct audience. They can do that with BARK’s Get-AzureKeyVaultTokenWithUsernamePassword:
$KeyVaultToken = (Get-AzureKeyVaultTokenWithUsernamePassword `
-Username "Username@contoso.onmicrosoft.com" `
-Password "PlainTextPassword" `
-TenantID "contoso.onmicrosoft.com").access_token
Now the operator can use that token in conjunction with BARK’s key vault item enumeration functions to list those items under each key vault:
$KeyVaultSecrets = $KeyVaults | %{
Get-AzureRMKeyVaultSecrets `
-KeyVaultURL $_.properties.vaultUri `
-Token $KeyVaultToken
}
$KeyVaultKeys = $KeyVaults | %{
Get-AzureRMKeyVaultKeys `
-KeyVaultURL $_.properties.vaultUri `
-Token $KeyVaultToken
}
$KeyVaultCertificates = $KeyVaults | %{
Get-AzureRMKeyVaultCertificates `
-KeyVaultURL $_.properties.vaultUri `
-Token $KeyVaultToken
}
An example of what these variables look like from our research environment:
PS /> $KeyVaultSecrets
contentType : application/x-pkcs12
id : https://keyvaultazurerbac.vault.azure.net/secrets/MyCertificate
managed : True
attributes : @{enabled=True; nbf=1731104193; exp=1733696793; created=1731104793; updated=1731104793; recoveryLevel=Recoverable+Purgeable; recoverableDays=90}
tags :
id : https://keyvaultazurerbac.vault.azure.net/secrets/Secret1
attributes : @{enabled=True; created=1728322075; updated=1728322075; recoveryLevel=Recoverable+Purgeable; recoverableDays=90}
PS /> $KeyVaultKeys | fl
kid : https://keyvaultazurerbac.vault.azure.net/keys/MyCertificate
attributes : @{enabled=True; nbf=1731104193; exp=1733696793; created=1731104793; updated=1731104793; recoveryLevel=Recoverable+Purgeable; recoverableDays=90}
tags :
managed : True
kid : https://keyvaultazurerbac.vault.azure.net/keys/MyKey
attributes : @{enabled=True; created=1731104478; updated=1731104478; recoveryLevel=Recoverable+Purgeable; recoverableDays=90; exportable=False}
tags :
PS /> $KeyVaultCertificates
id : https://keyvaultazurerbac.vault.azure.net/certificates/MyCertificate
x5t : ypNhbUwZJ1_9r1sc329hpgspReY
attributes : @{enabled=True; nbf=1731104193; exp=1733696793; created=1731104793; updated=1731104793}
tags :
subject :
The operator can attempt to read the value of a secret using BARK:
Get-AzureRMKeyVaultSecretValue `
-KeyVaultSecretID 'https://keyvaultazurerbac.vault.azure.net/secrets/Secret1' `
-Token $KeyVaultToken
Here is an example of what the output looks like from our research environment:
PS /Users/andyrobbins/Documents/SpecterOps/BHE/bloodhound-enterprise> Get-AzureRMKeyVaultSecretValue `
>> -KeyVaultSecretID 'https://keyvaultazurerbac.vault.azure.net/secrets/Secret1' `
>> -Token $KeyVaultToken | fl
value : secret1value
id : https://keyvaultazurerbac.vault.azure.net/secrets/Secret1/3d9ccebb6c7746a7a0a04ca92def08af
attributes : @{enabled=True; created=1728322075; updated=1728322075; recoveryLevel=Recoverable+Purgeable}
tags :
In the above example, “secret1value” is the plaintext value of the secret.
The operator can also attempt to encrypt data using the key vault keys with BARK:
Protect-StringWithAzureKeyVaultKey `
-InputString "Attackers think in graphs" `
-KeyVaultURL "https://keyvaultazurerbac.vault.azure.net" `
-KeyName "MyKey" `
-KeyVersion "5286277fc7d24293a8fe4119f9781804" `
-EncryptionAlgorithm "RSA-OAEP" `
-Token $KeyVaultToken
An example of the command running and its output:
PS /> Protect-StringWithAzureKeyVaultKey `
>> -InputString "Attackers think in graphs" `
>> -KeyVaultURL "https://keyvaultazurerbac.vault.azure.net" `
>> -KeyName "MyKey" `
>> -KeyVersion "5286277fc7d24293a8fe4119f9781804" `
>> -EncryptionAlgorithm "RSA-OAEP" `
>> -Token $KeyVaultToken
Y67LhebfHbz5i1kYTWLRxyIlx0Dz6UzDf93Lk3bqnTquUQFj4EOnq96FWPgLBX0ScToGce4B-rHzYluQax6xMMY47QnkS-biZ4-FDxLf1l1kMwcG0oC2iles2ykRkrz9aWiuQxtIoXovK9lQAra5LvomTv_8X7j7Ngd9UflpEKIE0HqeNoQ7lqM9-Jjhx4RPJNjNg6_gRaGlNTJhyx89U2WabUDuK9jlkRJIh95rWMsZ8WWsUCQI-DnXe051jVA-JM3QoUKTleOm9Lur_vPpkhbPey5dJbGc4eZH33ECbKiJHElNLtHFKZdAOFvD1o3fYrQquLyD0DEc-pXeVGZKlA
The operator can also decrypt this data or any other data encrypted using this particular key:
Unprotect-StringWithAzureKeyVaultKey `
-InputString "Y67LhebfHbz5i1kYTWLRxyIlx0Dz6UzDf93Lk3bqnTquUQFj4EOnq96FWPgLBX0ScToGce4B-rHzYluQax6xMMY47QnkS-biZ4-FDxLf1l1kMwcG0oC2iles2ykRkrz9aWiuQxtIoXovK9lQAra5LvomTv_8X7j7Ngd9UflpEKIE0HqeNoQ7lqM9-Jjhx4RPJNjNg6_gRaGlNTJhyx89U2WabUDuK9jlkRJIh95rWMsZ8WWsUCQI-DnXe051jVA-JM3QoUKTleOm9Lur_vPpkhbPey5dJbGc4eZH33ECbKiJHElNLtHFKZdAOFvD1o3fYrQquLyD0DEc-pXeVGZKlA" `
-KeyVaultURL "https://keyvaultazurerbac.vault.azure.net" `
-KeyName "MyKey" `
-KeyVersion "5286277fc7d24293a8fe4119f9781804" `
-EncryptionAlgorithm "RSA-OAEP" `
-Token $KeyVaultToken
An example of the command running and its output:
PS /> Unprotect-StringWithAzureKeyVaultKey `
>> -InputString "Y67LhebfHbz5i1kYTWLRxyIlx0Dz6UzDf93Lk3bqnTquUQFj4EOnq96FWPgLBX0ScToGce4B-rHzYluQax6xMMY47QnkS-biZ4-FDxLf1l1kMwcG0oC2iles2ykRkrz9aWiuQxtIoXovK9lQAra5LvomTv_8X7j7Ngd9UflpEKIE0HqeNoQ7lqM9-Jjhx4RPJNjNg6_gRaGlNTJhyx89U2WabUDuK9jlkRJIh95rWMsZ8WWsUCQI-DnXe051jVA-JM3QoUKTleOm9Lur_vPpkhbPey5dJbGc4eZH33ECbKiJHElNLtHFKZdAOFvD1o3fYrQquLyD0DEc-pXeVGZKlA" `
>> -KeyVaultURL "https://keyvaultazurerbac.vault.azure.net" `
>> -KeyName "MyKey" `
>> -KeyVersion "5286277fc7d24293a8fe4119f9781804" `
>> -EncryptionAlgorithm "RSA-OAEP" `
>> -Token $KeyVaultToken
Attackers think in graphs
Key Vault certificates store their public portion within the certificate object and their private portion within a secret. The operator can correlate the certificate and secret identifiers to identify certificate private keys:
PS /> $KeyVaultCertificates
id : https://keyvaultazurerbac.vault.azure.net/certificates/MyCertificate
x5t : ypNhbUwZJ1_9r1sc329hpgspReY
attributes : @{enabled=True; nbf=1731104193; exp=1733696793; created=1731104793; updated=1731104793}
tags :
subject :
PS /> $KeyVaultSecrets | ?{$_.id -Match "MyCertificate"}
contentType : application/x-pkcs12
id : https://keyvaultazurerbac.vault.azure.net/secrets/MyCertificate
managed : True
attributes : @{enabled=True; nbf=1731104193; exp=1733696793; created=1731104793; updated=1731104793; recoveryLevel=Recoverable+Purgeable; recoverableDays=90}
tags :
Once identified, the operator can attempt to extract the certificate’s private key with BARK’s Get-AzureRMKeyVaultSecretValue:
Get-AzureRMKeyVaultSecretValue `
-KeyVaultSecretID 'https://keyvaultazurerbac.vault.azure.net/secrets/MyCertificate' `
-Token $KeyVaultToken
An example of the command running and its output:
PS /> Get-AzureRMKeyVaultSecretValue ` >> -KeyVaultSecretID 'https://keyvaultazurerbac.vault.azure.net/secrets/MyCertificate' `
>> -Token $KeyVaultToken
value : MIIKSAIBAzCCCgQGCSqGSIb3DQEHAaCCCfUEggnxMIIJ7TCCBhYGCSqGSIb3DQEHAaCCBgcEggYDMIIF/zCCBfsGCyqGSIb3DQEMCgECoIIE/jCCBPowHAYKKoZIhvcNAQwBAzAOBAgYtLHg2kTiowICB9AEggTYE4dHk5e1
<...>
7gfFlVo75bhSgNP+lCwT2QBKaWjnJEVY3S2fBdNyJuOgN7jDNrbl3GB4x0+s3zskSfWmiYr4CjA7MB8wBwYFKw4DAhoEFGGwk3FEdu1wvJ0S9pBtgJnDRQTRBBRx1GZ32FASyOlSVmzLndPh8z0JQgICB9A=
contentType : application/x-pkcs12
id : https://keyvaultazurerbac.vault.azure.net/secrets/MyCertificate/d622d2372bf94f85b4752d0a54ae4679
managed : True
attributes : @{enabled=True; nbf=1731104193; exp=1733696793; created=1731104793; updated=1731104793; recoveryLevel=Recoverable+Purgeable}
tags :
kid : https://keyvaultazurerbac.vault.azure.net/keys/MyCertificate/d622d2372bf94f85b4752d0a54ae4679
Conclusion
We use these commands primarily to validate Microsoft’s documentation on how these APIs function, in particular how ARM and Azure Key Vault APIs make authorization decisions. Defenders can use and build upon these functions to automate key vault inventory and audit processes. Professional red team operators can use and build upon these functions to perform authorized assessment-related actions like reconnaissance, credential access, and payload encryption and decryption.
Azure Key Vault Tradecraft with BARK was originally published in Posts By SpecterOps Team Members on Medium, where people are continuing the conversation by highlighting and responding to this story.
The post Azure Key Vault Tradecraft with BARK appeared first on Security Boulevard.
>>More