Forum Discussion
Stolen credential detection in Azure Databricks starts in Microsoft Entra ID, not the workspace
An attacker holding a valid stolen credential doesn't trigger a single alarm looking at any one event. Token login, a listing call, reading a secret, creating a function, all of that happens constantly in any healthy workspace, done by legitimate people. What gives the attack away isn't any single event in isolation, it's the sequence happening out of normal order, coming from an identity that had never done any of it before.
Security research recently published by Databricks documents exactly this reasoning, organized around a six-stage attack model, without relying on a machine learning model or a complex anomaly score.
The six-stage model behind the attack
The documented sequence follows a well-defined progression: gain access with an already-valid credential, map the available environment, harvest additional credentials scattered across the workspace, find an execution path that takes advantage of those credentials, establish persistence to survive revocation of the original access, and finally exfiltrate data and attempt to clean up tracks. Each stage has its own signature in the audit log, but it only makes sense as an attack when read together with the neighboring stages, not in isolation.
Why sequence correlation beats isolated signature detection
The central point of the material is that no single event, looked at alone, separates legitimate use from an attack. What works is cross-referencing three things: the non-linear order of events (a token login followed by discovery, followed by reading a secret, followed by creating a function), the deviation from that identity's own history (the first time it lists a secret, the first time it creates a function, the first access denial from a given IP), and a simple volume threshold within a short window, like five different discovery API calls within a minute. A denial correlated with a later success on the same credential, and an identity switching compute right after a failure, also count as signal.
Hands-on: correlating a denied secret read with a successful one
A high-signal pattern cited in the research is a specific secret read being denied, followed shortly after by success on a related key, within the same session. A query that approximates this pattern directly against system.access.audit:
WITH secret_attempts AS (
SELECT
user_identity.email AS identity,
request_params.scope AS scope,
request_params.key AS key_name,
response.status_code AS status,
event_time
FROM system.access.audit
WHERE action_name = 'getSecret'
AND event_time >= current_timestamp() - INTERVAL 1 HOUR
)
SELECT
denied.identity,
denied.scope,
denied.event_time AS denied_at,
granted.key_name AS granted_key,
granted.event_time AS granted_at
FROM secret_attempts denied
JOIN secret_attempts granted
ON denied.identity = granted.identity
AND denied.scope = granted.scope
AND granted.event_time BETWEEN denied.event_time AND denied.event_time + INTERVAL 10 MINUTES
WHERE denied.status = 403
AND granted.status = 200
ORDER BY denied.event_time DESC;The logic behind this query is the same one underpinning the whole model: an isolated denial means nothing, but a denial followed by a related success, within the same short time window, is exactly the kind of pattern someone testing an access boundary would leave behind.
Diagram showing the six stages of the stolen-credential attack and where each prevention control breaks the chain, including the two points where the attacker can return as a new identity.
In Azure Databricks, the signal can arrive before the workspace
Much of the first stage, logging in with an already-valid credential, happens through federated authentication with Microsoft Entra ID before any event ever shows up in the workspace's system.access.audit. That means Entra ID Protection may already have flagged risk on the login session itself, before the attacker ever touches an Azure Databricks resource.
Two Entra ID Protection detectors matter specifically for the first stage: leaked credentials (leakedCredentials, an asynchronous calculation, available from Entra ID Free or P1, always classified as high risk) and impossible travel (unlikelyTravel, real-time detection, requires Entra ID P2). Neither depends on anything specific to Azure Databricks, but both cover exactly the moment the six-stage model treats as the entry point.
Entra ID Protection's risk detection API, via Microsoft Graph (GET /identityProtection/riskDetections, requiring the IdentityRiskEvent.Read.All permission and an Entra ID P1 or P2 license), returns, among other fields, riskEventType, riskLevel, userPrincipalName, and activityDateTime. Ingesting that result into your own table through a scheduled job lets you correlate login session risk with what happened afterward inside the workspace:
WITH token_logins AS (
SELECT
user_identity.email AS identity,
event_time AS login_at
FROM system.access.audit
WHERE action_name = 'tokenLogin'
AND event_time >= current_timestamp() - INTERVAL 24 HOURS
),
entra_id_risk AS (
SELECT
user_principal_name AS identity,
risk_event_type,
risk_level,
activity_date_time
FROM security.entra_id.risk_detections
WHERE risk_level IN ('high', 'medium')
)
SELECT
token_logins.identity,
token_logins.login_at,
entra_id_risk.risk_event_type,
entra_id_risk.risk_level
FROM token_logins
JOIN entra_id_risk
ON token_logins.identity = entra_id_risk.identity
AND entra_id_risk.activity_date_time
BETWEEN token_logins.login_at - INTERVAL 1 HOUR
AND token_logins.login_at + INTERVAL 1 HOUR
ORDER BY token_logins.login_at DESC;The security.entra_id.risk_detections table doesn't come from any ready-made connector, it's something the security team itself builds, calling the Microsoft Graph API periodically through a Lakeflow Job and writing the result as just another governed table. The payoff of cross-referencing both sources is seeing the window between "the login already came in risky" and "the credential is actually being used inside the workspace", which is exactly the interval where revoking access still prevents stage two onward.
Worth a note too on the credential-harvesting stage specifically on Azure: a secret read from a Unity Catalog schema backed by Azure Key Vault, a feature still in Beta, generates a Unity Catalog audit record with the identity of the Azure Databricks principal that did the read, so it lands inside the same system.access.audit already being monitored. The older secret scope model backed by Key Vault, where the entire vault is exposed to the workspace at once, doesn't have that coverage officially documented, worth testing directly before assuming anything, and turning on Key Vault's own diagnostic logging as an extra layer while that remains unconfirmed.
My take: the most honest detail in this material is acknowledging its own data limitation, function_info comes masked by default for anyone who isn't an account admin, and much of the command history only shows up with verbose logging turned on, which isn't enabled by default. That means the real effectiveness of this detection model depends on a configuration decision made before the attack happens, not just the right query written afterward. Worth auditing right now whether your workspace is already collecting the data you'll need when it's time to investigate.
What this doesn't solve on its own
The research doesn't report precision, recall, or false-positive rate metrics, the numbers given are just example thresholds and a ready detection count, over thirty, in the companion open-source app. An empty result on a detection query also doesn't prove the absence of an attack, it might just mean the time window is wrong or that verbose logging was off during that period. And the whole model is rule- and threshold-based, not statistical learning, which makes it easier to audit and explain, but also makes it easier for an attacker who knows the pattern to adjust their own pace to stay below the configured threshold.
On Azure Databricks specifically, it's worth confirming which log path your team actually monitors. The documentation is explicit that Azure Monitor diagnostic settings on the workspace don't cover account-level events, nor a few specific services, including Groups, cluster policy, Artifact Registry, and the Unity Gateway MCP, available only through the system table. A team that only shipped diagnostic logs to Log Analytics or Microsoft Sentinel, without ever querying system.access.audit directly, has a real coverage gap, not a hypothetical one, in exactly the services that underpin part of the six-stage model.
Is this pattern worth adopting?
The real value here isn't the specific list of APIs cited, it's the design principle, treating sequence and baseline deviation as the primary signal, not the isolated event. That's replicable on any platform that exposes structured audit logs, not just Azure Databricks. Before trusting this in production, confirm verbose logging is on where it matters, that notebook and job history is being retained long enough, and simulate at least one of the documented patterns against your own environment to see whether the query actually fires.
References
- Databricks Blog, "Detecting credential-driven attacks on Databricks": https://www.databricks.com/blog/detecting-credential-driven-attacks-databricks (https://www.databricks.com/blog/detecting-credential-driven-attacks-databricks)
- Databricks Docs, audit log system table: https://docs.databricks.com/aws/en/admin/system-tables/audit-logs (https://docs.databricks.com/aws/en/admin/system-tables/audit-logs)
- Microsoft Learn, "Audit log system table reference": https://learn.microsoft.com/en-us/azure/databricks/admin/system-tables/audit-logs (https://learn.microsoft.com/en-us/azure/databricks/admin/system-tables/audit-logs)
- Microsoft Learn, "Enable verbose audit logs": https://learn.microsoft.com/en-us/azure/databricks/admin/account-settings/verbose-logs (https://learn.microsoft.com/en-us/azure/databricks/admin/account-settings/verbose-logs)
- Microsoft Learn, "Context-based ingress control": https://learn.microsoft.com/en-us/azure/databricks/security/network/front-end/context-based-ingress (https://learn.microsoft.com/en-us/azure/databricks/security/network/front-end/context-based-ingress)
- Microsoft Learn, "Diagnostic log reference": https://learn.microsoft.com/en-us/azure/databricks/admin/account-settings/audit-logs (https://learn.microsoft.com/en-us/azure/databricks/admin/account-settings/audit-logs)
- Microsoft Learn, "Azure Key Vault secrets in Unity Catalog" (Beta): https://learn.microsoft.com/en-us/azure/databricks/security/secrets/external-secrets (https://learn.microsoft.com/en-us/azure/databricks/security/secrets/external-secrets)
- Microsoft Learn, "Workspace secret scopes" (legacy model): https://learn.microsoft.com/en-us/azure/databricks/security/secrets/workspace-secret-scopes (https://learn.microsoft.com/en-us/azure/databricks/security/secrets/workspace-secret-scopes)
- Microsoft Learn, "What are risk detections?" (Microsoft Entra ID Protection): https://learn.microsoft.com/en-us/entra/id-protection/concept-identity-protection-risks (https://learn.microsoft.com/en-us/entra/id-protection/concept-identity-protection-risks)
- Microsoft Graph, risk detection API (riskDetections): https://learn.microsoft.com/en-us/graph/api/riskdetection-list (https://learn.microsoft.com/en-us/graph/api/riskdetection-list)
- Open-source detection app repository: https://github.com/databricks-solutions/cybersec-workspace-detection-app (https://github.com/databricks-solutions/cybersec-workspace-detection-app)