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 1-minute windows
| EVAL Esql.time_window.date_trunc = DATE_TRUNC(1 minute, @timestamp)
// Aggregate identity, geo, resource, and activity info
| STATS
Esql.azure.platformlogs.identity.claim.upn.values = VALUES(azure.platformlogs.identity.claim.upn),
Esql.azure.platformlogs.identity.claim.upn.count_unique = COUNT_DISTINCT(azure.platformlogs.identity.claim.upn),
Esql.azure.platformlogs.identity.claim.appid.values = VALUES(azure.platformlogs.identity.claim.appid),
Esql.azure.platformlogs.identity.claim.objectid.values = VALUES(azure.platformlogs.identity.claim.objectid),
Esql.source.ip.values = VALUES(source.ip),
Esql.geo.city.values = VALUES(geo.city_name),
Esql.geo.region.values = VALUES(geo.region_name),
Esql.geo.country.values = VALUES(geo.country_name),
Esql.network.as_org.values = VALUES(source.as.organization.name),
Esql.event.actions.values = VALUES(event.action),
Esql.event.count = COUNT(*),
Esql.event.action.count_distinct = COUNT_DISTINCT(event.action),
Esql.azure.resource.name.count_distinct = COUNT_DISTINCT(azure.resource.name),
Esql.azure.resource.name.values = VALUES(azure.resource.name),
Esql.azure.platformlogs.result_type.values = VALUES(azure.platformlogs.result_type),
Esql.cloud.region.values = VALUES(cloud.region),
Esql.agent.name.values = VALUES(agent.name),
Esql.azure.subscription_id.values = VALUES(azure.subscription_id),
Esql.azure.resource_group.values = VALUES(azure.resource.group),
Esql.azure.resource_id.values = VALUES(azure.resource.id)
BY Esql.time_window.date_trunc, azure.platformlogs.identity.claim.upn
// Keep relevant fields
| KEEP
Esql.time_window.date_trunc,
Esql.azure.platformlogs.identity.claim.upn.values,
Esql.azure.platformlogs.identity.claim.upn.count_unique,
Esql.azure.platformlogs.identity.claim.appid.values,
Esql.azure.platformlogs.identity.claim.objectid.values,
Esql.source.ip.values,
Esql.geo.city.values,
Esql.geo.region.values,
Esql.geo.country.values,
Esql.network.as_org.values,
Esql.event.actions.values,
Esql.event.count,
Esql.event.action.count_distinct,
Esql.azure.resource.name.count_distinct,
Esql.azure.resource.name.values,
Esql.azure.platformlogs.result_type.values,
Esql.cloud.region.values,
Esql.agent.name.values,
Esql.azure.subscription_id.values,
Esql.azure.resource_group.values,
Esql.azure.resource_id.values
// Filter for suspiciously high volume of distinct Key Vault reads by a single actor
| WHERE Esql.azure.platformlogs.identity.claim.upn.count_unique == 1 AND Esql.event.count >= 10 AND Esql.event.action.count_distinct >= 2
| SORT Esql.time_window.date_trunc DESC
Install detection rules in Elastic Security
Detect Excessive Secret or Key Retrieval from Azure Key Vault in the Elastic Security detection engine by installing this rule into your Elastic Stack.
To setup this rule, check out the installation guide for Prebuilt Security Detection Rules(opens in a new tab or window).