-
Notifications
You must be signed in to change notification settings - Fork 587
Description
Summary
We are missing detections for excessive secret or key retrieval from Azure Key Vault based on Azure Key Vault diagnostic logs (azure.platformlogs) that detail the specific operations called. As covered in the reference blog below, retrieval or listing is common post-identity compromise from a red teaming perspective. Although this could blend in with benign activity as well.
Ref: https://www.inversecos.com/2022/05/detection-and-compromise-azure-key.html
The following query was used on testing data from emulating this behavior in Azure. Note that we will need to add a setup guide about including Azure Key Vault diagnostic logs into the Event Hub that the Azure integration is enabled through. These logs are not enabled by default in Azure.
FROM logs-azure.platformlogs-* METADATA _id, _index
// Filter for Azure Key Vault read operations
| WHERE event.dataset == "azure.platformlogs"
AND event.action IN (
"VaultGet",
"KeyGet",
"KeyList",
"KeyListVersions",
"KeyGetDeleted",
"KeyListDeleted",
"SecretGet",
"SecretList",
"SecretListVersions",
"SecretGetDeleted",
"SecretListDeleted",
"CertificateGet",
"CertificateList",
"CertificateListVersions",
"CertificateGetDeleted",
"CertificateListDeleted",
"CertificatePolicyGet",
"CertificateContactsGet",
"CertificateIssuerGet",
"CertificateIssuersList"
)
// Truncate timestamps into 30-second windows
| EVAL time_window = DATE_TRUNC(1 minute, @timestamp)
// Aggregate identity, geo, resource, and activity info
| STATS
// Who
actor_name = VALUES(azure.platformlogs.identity.claim.upn),
actor_unique_count = COUNT_DISTINCT(azure.platformlogs.identity.claim.upn),
app_id = VALUES(azure.platformlogs.identity.claim.appid),
object_id = VALUES(azure.platformlogs.identity.claim.upn),
// Geo / Network
source_ip = VALUES(source.ip),
city_name = VALUES(geo.city_name),
region_name = VALUES(geo.region_name),
country_name = VALUES(geo.country_name),
as_org = VALUES(source.as.organization.name),
// What
actions = VALUES(event.action),
action_count = COUNT(*),
distinct_actions = COUNT_DISTINCT(event.action),
vaults_accessed = COUNT_DISTINCT(azure.resource.name),
vault_names = VALUES(azure.resource.name),
result_type = VALUES(azure.platformlogs.result_type),
cloud_region = VALUES(cloud.region),
// Metadata
agent_name = VALUES(agent.name),
subscription_id = VALUES(azure.subscription_id),
resource_group = VALUES(azure.resource.group),
resource_ids = VALUES(azure.resource.id)
BY
time_window,
azure.platformlogs.identity.claim.upn
// Optional filters for fidelity
| WHERE actor_unique_count == 1 and action_count >= 5 AND distinct_actions >= 2
| SORT time_window DESC