azure-disk-des-rekey-escalation
A principal with CanModifyConfiguration(DES) can upgrade a CONDITIONAL(key_permission) CanReadData edge to ACTIVE by pointing the DES at an attacker-controlled Key Vault key and triggering VM lifecycle (re-encrypt under attacker key on next attach/detach).
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'principal': None}
CanModifyConfiguration {'des': None}
{'principal': None} CanExfiltrate {'disk': None}
where
node_type(?des) ==
EncryptionKey
node_type(?disk) in [BlockStorage, Snapshot]
?disk.encryption.type == EncryptionAtRestWithCustomerKey
?disk.encryption.diskEncryptionSetId == ?des.id
emit
| source type | Identity |
|---|---|
| target type | BlockStorage |
| source | ?principal |
| target | ?disk |
| permissions | Microsoft.Compute/diskEncryptionSets/write Microsoft.Compute/disks/beginGetAccess/action |
| conditions | service_state |
| state logic | CONDITIONAL(service_state): The attacker has CanModifyConfiguration(DES) and CanExfiltrate(disk). By repointing the DES to an attacker-controlled Key Vault key, the next VM lifecycle event re-wraps the DEK under the attacker's key, enabling decryption of the SAS-exported VHD. The condition is that the VM must be running and must undergo a lifecycle event (attach/detach/stop/start). |
Narrative
{principal.name} can update the DES {des.name} to point at an attacker-controlled Key Vault key (CanModifyConfiguration) and can export disk {disk.name} via SAS (CanExfiltrate). On the next VM lifecycle event (attach/detach/stop/start), the DEK is re-wrapped under the attacker's key, enabling decryption of the exported VHD.
Raw rule rules/derived/azure/disks.yaml
id: azure-disk-des-rekey-escalation
emits: CanReadData
description: A principal with CanModifyConfiguration(DES) can upgrade a CONDITIONAL(key_permission) CanReadData
edge to ACTIVE by pointing the DES at an attacker-controlled Key Vault key and triggering VM lifecycle
(re-encrypt under attacker key on next attach/detach).
match:
- - principal: null
- CanModifyConfiguration
- des: null
- - principal: null
- CanExfiltrate
- disk: null
where:
- node_type(?des) == EncryptionKey
- node_type(?disk) in [BlockStorage, Snapshot]
- ?disk.encryption.type == EncryptionAtRestWithCustomerKey
- ?disk.encryption.diskEncryptionSetId == ?des.id
emit:
source_type: Identity
target_type: BlockStorage
source: ?principal
target: ?disk
permissions:
- Microsoft.Compute/diskEncryptionSets/write
- Microsoft.Compute/disks/beginGetAccess/action
conditions:
- service_state
state_logic: 'CONDITIONAL(service_state): The attacker has CanModifyConfiguration(DES) and CanExfiltrate(disk).
By repointing the DES to an attacker-controlled Key Vault key, the next VM lifecycle event re-wraps
the DEK under the attacker''s key, enabling decryption of the SAS-exported VHD. The condition is that
the VM must be running and must undergo a lifecycle event (attach/detach/stop/start).'
confidence: 0.8
derived_from:
- ?principal CanModifyConfiguration ?des
- ?principal CanExfiltrate ?disk (with disk.encryption.diskEncryptionSetId == ?des.id property match)
false_positive_note: 'This path requires: 1) CanModifyConfiguration(DES) by the principal 2) CanExfiltrate(disk)
by the same principal 3) The disk linked to the DES (disk.encryption.diskEncryptionSetId == DES.id,
property-based match) 4) A running VM (service_state condition) for the re-wrap to occur If the VM
is permanently offline, the re-wrap never happens and this edge remains CONDITIONAL(service_state)
indefinitely. Once the VM lifecycle event occurs and re-wrapping completes, decrypt-gates-secret-read
(credential-chains.yaml) upgrades this to ACTIVE via CanDecrypt on the attacker-controlled key.'
narrative: '{principal.name} can update the DES {des.name} to point at an attacker-controlled Key Vault
key (CanModifyConfiguration) and can export disk {disk.name} via SAS (CanExfiltrate). On the next
VM lifecycle event (attach/detach/stop/start), the DEK is re-wrapped under the attacker''s key, enabling
decryption of the exported VHD.'