Uncategorized

First line of defence – Review Azure AD Gaps in Conditional Access with Log Analytics / Azure Sentinel

Backround

It is highly recommended especially (at time like this) to ensure, you are not giving easy access to your environment for possible malicious parties.

This post gives some examples to investigate possible gaps in your Conditional Access implementation:

  • This post does not detail further backround on permissions, general information about Conditional Access, or MFA for that matter. For such information excellent documentation exists at What is Conditional Access in Azure Active Directory? | Microsoft Docs
  • It is worth though saying, that you need to have Azure AD Sign-in logs export enabled to Log Analytics /Azure Sentinel before continuing.

Methods

  • Custom query based on MS workbooks to list example correlationID’s and policies with possible gaps
    • Lists bypassed policies (not based on network, or previous MFA /device context, as these are typically intentionally desired bypasses):
      • Policies
      • CorrelationID’s (first 5)
      • Device Platforms

Custom query – Guide (Interactive signins)

If you run large environment, begin with the following query and reduce the lookback value to 1 day (or even hours) to see possible performance expectations

  • This query includes only fresh events (interactive sign-in events)

Step 1 – run the query

let dateInDays = 7d;
let mass = materialize (SigninLogs 
| where tostring(AuthenticationDetails) !has "First factor requirement satisfied by claim in the token"
| where NetworkLocationDetails !has "trustedNamedLocation"
| where ConditionalAccessStatus == "success"
| where Status.additionalDetails != "MFA requirement satisfied by claim in the token" and Status.additionalDetails != "MFA requirement skipped due to remembered device" and Status.additionalDetails != "MFA completed in Azure AD"
| where AuthenticationRequirement has "singleFactorAuthentication"
| where TimeGenerated > now() - dateInDays);
let mass2 = mass
| extend identifier = iff(isempty(DeviceDetail.browser), UserAgent, DeviceDetail.operatingSystem)
| extend trueId = case(identifier contains "ios", "ios", identifier contains "Android", "Android", identifier);
let rs = materialize(mass2
| extend ID = strcat(trueId, '-',AppDisplayName)
| extend CaDetail = ConditionalAccessPolicies
|mv-expand CaDetail);
let stage2 = rs
| summarize correlation = make_set(strcat(CaDetail.result, '-',CorrelationId), 5) by ID, ClientAppUsed;
let stage3 = rs
| extend pols = strcat(CaDetail.result, "-",CaDetail.displayName, "-", CaDetail.enforcedGrantControls)
| summarize make_set(pols) by ID;
let stage4 =  rs
| summarize make_set(UserDisplayName) by ID
| extend usersCount = array_length(set_UserDisplayName)
| project-away set_UserDisplayName;
 stage2 
| join kind=inner  stage3 on ID
| project ID, correlation, set_pols
| join kind=innerunique  stage4 on ID
| project ID, usersCount, correlation, set_pols
| extend fullBypass = iff(tostring(set_pols) contains "Success", "false","True")
//

Step 2 – Review correlationId’s for full example event for bypassed policies and details

  • Bring up a new tab
  • Paste the correlation ID part from concantenated output from previous query in the new tab
SigninLogs
| where CorrelationId =="fb6325e7-7187-4670-a574-0b1aedb3116d"
  • Review full event data

Guide (Combined sign-ins)

Repeat previous steps, but instead use the queries below

Step 1 – run the query

Step 2 – Review correlationId’s for full example event for bypassed policies and details

let dateInDays = 7d;
let mass = materialize (union SigninLogs, AADNonInteractiveUserSignInLogs
| where tostring(AuthenticationDetails) !has "First factor requirement satisfied by claim in the token"
| where NetworkLocationDetails !has "trustedNamedLocation"
| where ConditionalAccessStatus == "success"
| extend Status = iff(isempty(Status_dynamic), parse_json(Status_string), Status_dynamic)
| where Status.additionalDetails != "MFA requirement satisfied by claim in the token" and Status.additionalDetails != "MFA requirement skipped due to remembered device" and Status.additionalDetails != "MFA completed in Azure AD"
| where AuthenticationRequirement has "singleFactorAuthentication"
| where TimeGenerated > now() - dateInDays);
let mass2 = mass
| extend DeviceDetail = iff(isempty(DeviceDetail_dynamic), parse_json(DeviceDetail_string), DeviceDetail_dynamic)
| extend identifier = iff(isempty(DeviceDetail.browser), UserAgent, DeviceDetail.operatingSystem)
| extend trueId = case(identifier contains "ios", "ios", identifier contains "Android", "Android", identifier);
let rs = materialize(mass2
| extend ConditionalAccessPolicies = iff(isempty(ConditionalAccessPolicies_dynamic), parse_json(ConditionalAccessPolicies_string), ConditionalAccessPolicies_dynamic)
| extend ID = strcat(trueId, '-',AppDisplayName)
| extend CaDetail = ConditionalAccessPolicies
|mv-expand CaDetail);
let stage2 = rs
| summarize correlation = make_set(strcat(CaDetail.result, '-',CorrelationId), 5) by ID, ClientAppUsed;
let stage3 = rs
| extend pols = strcat(CaDetail.result, "-",CaDetail.displayName, "-", CaDetail.enforcedGrantControls)
| summarize make_set(pols) by ID;
let stage4 =  rs
| summarize make_set(UserDisplayName) by ID
| extend usersCount = array_length(set_UserDisplayName)
| project-away set_UserDisplayName;
 stage2 
| join kind=inner  stage3 on ID
| project ID, correlation, set_pols
| join kind=innerunique  stage4 on ID
| project ID, usersCount, correlation, set_pols
| extend fullBypass = iff(tostring(set_pols) contains "Success", "false","True")
//
union AADNonInteractiveUserSignInLogs, SigninLogs
| where CorrelationId =="fb6325e7-7187-4670-a574-0b1aedb3116d"

0 comments on “First line of defence – Review Azure AD Gaps in Conditional Access with Log Analytics / Azure Sentinel

Jätä kommentti