Disclaimer: This article is provided strictly for educational purposes and authorized security testing. Only run these techniques against systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction and can carry severe penalties.
Introduction
Azure Key Vault centralizes the storage of secrets, encryption keys, and TLS certificates that applications need at runtime, which makes it one of the highest-value targets in an Azure tenant — a single over-permissioned principal on a vault can mean database connection strings, storage account keys, and signing certificates for an entire environment. Authorization to a vault’s data plane is controlled by one of two mutually exclusive models: the legacy vault Access Policy model, or the newer Azure RBAC model. Which one is active is a single boolean on the vault, enableRbacAuthorization, and that switch determines everything about how permissions are granted, scoped, and audited.
The two models are not just different syntax for the same thing — they have materially different security properties. Access Policies are coarse, vault-wide, and invisible to Azure AD’s role-assignment tooling; Azure RBAC is fine-grained, integrates with Privileged Identity Management (PIM) and Conditional Access, and is auditable through the same az role assignment surface used for every other Azure resource. Environments that migrated from classic vaults, or that were built by teams unaware of the RBAC option, routinely carry forward Access Policy grants that are far broader than intended — and because Access Policies don’t show up in the standard IAM blade reviews teams run for RBAC, that over-permissioning tends to survive audits indefinitely.
Attack Prerequisites
Exploiting Key Vault authorization is about finding and using an identity that already has more data-plane access than it needs. For this to be viable:
- Some foothold identity — a compromised user, a leaked service principal client secret/certificate, a managed identity accessible via SSRF or code execution on a resource, or a CI/CD pipeline credential.
- That identity (or a group it belongs to) holds broad vault permissions — either via an over-scoped Access Policy or an RBAC role like
Key Vault Secrets Officer/Key Vault Administratorassigned at too high a scope. - Network access to the vault’s data-plane endpoint — trivial if the vault firewall is set to allow public access from all networks, which is still the default for many vaults.
- No compensating control such as Conditional Access, PIM just-in-time activation, or Private Link forcing traffic through a specific VNet.
How It Works
The Access Policy model grants permissions per security principal, per plane — keys, secrets, and certificates each get their own list of allowed operations (get, list, set, delete, backup, restore, purge, recover, and more for keys). Critically, an Access Policy applies to the *entire vault*: there is no way to scope a policy to a single secret or certificate. If a principal is granted get/list on secrets, it can read every secret in that vault, present and future. Access Policies also have no concept of deny assignments, no PIM eligibility/activation workflow, and no native Conditional Access enforcement at the data-plane call — a stolen credential with a valid token can call the vault directly.
Azure RBAC authorization instead treats Key Vault data-plane operations as ordinary Azure resource actions, governed by built-in roles such as Key Vault Secrets User (read secret values), Key Vault Secrets Officer (manage secrets), Key Vault Certificates Officer, Key Vault Crypto User/Officer, Key Vault Administrator (full data-plane control), and Key Vault Reader (metadata only, no secret values). Role assignments made with az role assignment create can be scoped at the management group, subscription, resource group, vault, or — because Key Vault supports sub-resource RBAC scoping — even at an individual key, secret, or certificate object. Because these are standard Azure AD role assignments, they participate in PIM eligible/active assignment, Conditional Access policies (e.g. requiring MFA or a compliant device to activate), and appear in every az role assignment list or Azure AD Access Reviews sweep.
The security gap in practice is migration debt. enableRbacAuthorization is set once on a vault; setting it to true does not delete existing Access Policies, but they simply stop being evaluated. Teams that flip the switch later without cleaning up old policies leave stale documentation behind, and teams that never flip it keep relying on all-or-nothing Access Policy grants that a standard Azure RBAC/IAM audit will never surface, because those policies live in properties.accessPolicies on the vault object, not in the tenant’s role-assignment graph.
Vulnerable Code / Configuration
A vault still on the legacy model, with a broad Access Policy granted to an Azure AD group (which may contain far more members than the vault owner realizes) and left network-open:
# Vault created without RBAC authorization (legacy Access Policy model)
az keyvault create \
--name kv-prod-payments \
--resource-group rg-payments \
--enable-rbac-authorization false \
--public-network-access Enabled
# Over-broad policy: entire 'DevTeam-All' AD group gets full secret,
# key, and certificate management on every object in the vault.
az keyvault set-policy \
--name kv-prod-payments \
--object-id <DevTeam-All-group-object-id> \
--secret-permissions get list set delete backup restore purge recover \
--key-permissions get list create delete sign verify backup restore purge \
--certificate-permissions get list create delete managecontacts
BashThe equivalent ARM/JSON access policy embedded in a vault template shows the same problem — one entry, no per-secret scoping, applying to the whole vault:
{
"tenantId": "<tenant-id>",
"objectId": "<DevTeam-All-group-object-id>",
"permissions": {
"secrets": ["get", "list", "set", "delete", "backup", "restore", "purge"],
"keys": ["get", "list", "create", "delete", "sign", "backup", "restore"],
"certificates": ["get", "list", "create", "delete", "managecontacts"]
}
}
JSONCompounding this, the vault’s network rule set allows public access from any network, so any principal that authenticates to Azure AD and holds a valid token for https://vault.azure.net can reach the data plane directly, with no requirement to be on a corporate network or a peered VNet.
Walkthrough / Exploitation
With a compromised service principal or user token in hand, the first step is discovering which vaults it can reach and what it’s actually allowed to do. Access Policies are visible directly on the vault object:
az login --service-principal -u <client-id> -p <client-secret> --tenant <tenant-id>
az keyvault list --query "[].{name:name, rg:resourceGroup}" -o table
az keyvault show --name kv-prod-payments \
--query "properties.{rbac:enableRbacAuthorization, policies:accessPolicies, network:networkAcls}"
BashIf enableRbacAuthorization is false and the caller’s object ID (or a group it belongs to) appears in accessPolicies with get/list on secrets, the data plane is open. Enumerate and dump everything the policy allows:
az keyvault secret list --vault-name kv-prod-payments -o table
az keyvault secret show --vault-name kv-prod-payments --name sql-connstring \
--query value -o tsv
az keyvault certificate list --vault-name kv-prod-payments -o table
az keyvault key list --vault-name kv-prod-payments -o table
BashHarvested secrets are rarely the end goal in themselves — they’re pivot material. A SQL connection string yields direct database access; a second service principal’s client secret stored as a Key Vault secret (a common pattern for app-to-app auth) lets the attacker az login as that identity and repeat the enumeration against whatever it has access to, walking laterally across subscriptions. Tools such as AzureHound (BloodHound’s Azure collector) and the Az.KeyVault PowerShell module (Get-AzKeyVaultSecret, Get-AzKeyVault) are commonly used to enumerate vaults and access policies at scale across a tenant rather than one vault at a time.
Note: A vault can be RBAC-enabled and still be over-permissioned — assigning
Key Vault AdministratororKey Vault Secrets Officerat the subscription scope instead of the individual vault is functionally identical to a broad Access Policy, just expressed differently. Migrating models fixes the auditability problem, not the least-privilege problem, unless role scopes are also tightened.
Opsec:
az keyvault secret showcalls are logged individually to the vault’sAuditEventdiagnostic category when logging is enabled, including the caller’s object ID, IP address, and the specific secret name accessed — a burst ofSecretGetcalls across many secret names from one principal in a short window is a strong signal, so operators should expect this activity to be recoverable in an incident response timeline even if the vault owner never notices in real time.
Detection and Defense
The durable fix is moving off the Access Policy model entirely and treating vault access like any other Azure RBAC surface, with tight scoping:
- Migrate to RBAC authorization — set
enableRbacAuthorization: trueand replace Access Policy entries with least-privilege built-in roles (Key Vault Secrets Userfor read-only apps, notSecrets OfficerorAdministrator). - Scope role assignments narrowly — assign at the individual vault or object level rather than subscription/resource-group scope, and use PIM eligible assignments plus Conditional Access for any human access to
Administrator– orOfficer-level roles. - Restrict network access — move from ‘Allow public access from all networks’ to selected virtual networks/IP ranges or Private Link, using
az keyvault network-rule add/--default-action Deny. - Enable diagnostic logging — send the
AuditEventcategory to a Log Analytics workspace so everySecretGet,SecretList,KeyGet, andCertificateGetcall is queryable, and enable Microsoft Defender for Key Vault for anomaly alerting. - Keep soft-delete and purge protection enabled (defaults on new vaults) so deleted secrets/keys can’t be permanently destroyed to cover tracks.
- Periodically audit
properties.accessPolicieson every vault directly — it will never appear in a standard Azure AD/RBAC role-assignment review.
Real-World Impact
Key Vault over-permissioning is one of the most common findings in Azure cloud security assessments, precisely because Access Policies are easy to grant broadly during initial setup and rarely revisited afterward, and because many organizations still run vaults created years before RBAC authorization was generally available. Since vaults frequently hold the credentials that connect applications to databases, storage, and other Azure services, a single over-permissioned identity reaching a production vault is a routine path from an initial low-privilege foothold to broad lateral movement across a subscription.
Conclusion
Access Policies and Azure RBAC solve the same problem — who can read and manage vault contents — with very different blast radii and auditability. Legacy Access Policies grant vault-wide, plane-wide access that is invisible to standard RBAC reviews, while RBAC roles are scopable, PIM- and Conditional-Access-aware, and show up in the same audit trail as every other Azure permission. Migrating to enableRbacAuthorization: true, assigning narrowly scoped built-in roles, restricting network access, and logging every data-plane call closes off one of the quieter, high-value paths into an Azure environment.
You Might Also Like
If you found this useful, these related deep-dives cover adjacent techniques and their defenses:



Comments