Attempt to bypass conditional access rule in Microsoft Entra ID
Id | 3af9285d-bb98-4a35-ad29-5ea39ba0c628 |
Rulename | Attempt to bypass conditional access rule in Microsoft Entra ID |
Description | Identifies an attempt to Bypass conditional access rule(s) in Microsoft Entra ID. The ConditionalAccessStatus column value details if there was an attempt to bypass Conditional Access or if the Conditional access rule was not satisfied (ConditionalAccessStatus == 1). References: https://docs.microsoft.com/azure/active-directory/conditional-access/overview https://docs.microsoft.com/azure/active-directory/reports-monitoring/concept-sign-ins https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes ConditionalAccessStatus == 0 // Success ConditionalAccessStatus == 1 // Failure ConditionalAccessStatus == 2 // Not Applied ConditionalAccessStatus == 3 // unknown |
Severity | Low |
Tactics | InitialAccess Persistence |
Techniques | T1078 T1098 |
Required data connectors | AzureActiveDirectory |
Kind | Scheduled |
Query frequency | 1d |
Query period | 1d |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/BypassCondAccessRule.yaml |
Version | 1.0.7 |
Arm template | 3af9285d-bb98-4a35-ad29-5ea39ba0c628.json |
let threshold = 1; // Modify this threshold value to reduce false positives based on your environment
let aadFunc = (tableName:string){
table(tableName)
| where ConditionalAccessStatus == 1 or ConditionalAccessStatus =~ "failure"
| mv-apply CAP = parse_json(ConditionalAccessPolicies) on (
project ConditionalAccessPoliciesName = CAP.displayName, result = CAP.result
| where result =~ "failure"
)
| extend DeviceDetail = todynamic(DeviceDetail), Status = todynamic(Status), LocationDetails = todynamic(LocationDetails)
| extend OS = DeviceDetail.operatingSystem, Browser = DeviceDetail.browser
| extend State = tostring(LocationDetails.state), City = tostring(LocationDetails.city), Region = tostring(LocationDetails.countryOrRegion)
| extend StatusCode = tostring(Status.errorCode), StatusDetails = tostring(Status.additionalDetails)
| extend Status = strcat(StatusCode, ": ", ResultDescription)
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Status = make_list(Status,10), StatusDetails = make_list(StatusDetails,50), IPAddresses = make_list(IPAddress,100), IPAddressCount = dcount(IPAddress), CorrelationIds = make_list(CorrelationId,100), ConditionalAccessPoliciesName = make_list(ConditionalAccessPoliciesName,100)
by UserPrincipalName, UserId, AppDisplayName, tostring(Browser), tostring(OS), City, State, Region, Type
| where IPAddressCount > threshold and StatusDetails !has "MFA successfully completed"
| mv-expand IPAddresses, Status, StatusDetails, CorrelationIds
| extend Status = strcat(Status, " ", StatusDetails)
| summarize IPAddresses = make_set(IPAddresses,100), Status = make_set(Status,10), CorrelationIds = make_set(CorrelationIds,100), ConditionalAccessPoliciesName = make_set(ConditionalAccessPoliciesName,100)
by StartTime, EndTime, UserPrincipalName, UserId, AppDisplayName, tostring(Browser), tostring(OS), City, State, Region, IPAddressCount, Type
| extend IPAddressFirst = tostring(IPAddresses[0]), Name = tostring(split(UserPrincipalName, "@")[0]), UPNSuffix = tostring(split(UserPrincipalName, "@")[1])
};
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
union isfuzzy=true aadSignin, aadNonInt
id: 3af9285d-bb98-4a35-ad29-5ea39ba0c628
tactics:
- InitialAccess
- Persistence
queryPeriod: 1d
triggerThreshold: 0
name: Attempt to bypass conditional access rule in Microsoft Entra ID
query: |
let threshold = 1; // Modify this threshold value to reduce false positives based on your environment
let aadFunc = (tableName:string){
table(tableName)
| where ConditionalAccessStatus == 1 or ConditionalAccessStatus =~ "failure"
| mv-apply CAP = parse_json(ConditionalAccessPolicies) on (
project ConditionalAccessPoliciesName = CAP.displayName, result = CAP.result
| where result =~ "failure"
)
| extend DeviceDetail = todynamic(DeviceDetail), Status = todynamic(Status), LocationDetails = todynamic(LocationDetails)
| extend OS = DeviceDetail.operatingSystem, Browser = DeviceDetail.browser
| extend State = tostring(LocationDetails.state), City = tostring(LocationDetails.city), Region = tostring(LocationDetails.countryOrRegion)
| extend StatusCode = tostring(Status.errorCode), StatusDetails = tostring(Status.additionalDetails)
| extend Status = strcat(StatusCode, ": ", ResultDescription)
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Status = make_list(Status,10), StatusDetails = make_list(StatusDetails,50), IPAddresses = make_list(IPAddress,100), IPAddressCount = dcount(IPAddress), CorrelationIds = make_list(CorrelationId,100), ConditionalAccessPoliciesName = make_list(ConditionalAccessPoliciesName,100)
by UserPrincipalName, UserId, AppDisplayName, tostring(Browser), tostring(OS), City, State, Region, Type
| where IPAddressCount > threshold and StatusDetails !has "MFA successfully completed"
| mv-expand IPAddresses, Status, StatusDetails, CorrelationIds
| extend Status = strcat(Status, " ", StatusDetails)
| summarize IPAddresses = make_set(IPAddresses,100), Status = make_set(Status,10), CorrelationIds = make_set(CorrelationIds,100), ConditionalAccessPoliciesName = make_set(ConditionalAccessPoliciesName,100)
by StartTime, EndTime, UserPrincipalName, UserId, AppDisplayName, tostring(Browser), tostring(OS), City, State, Region, IPAddressCount, Type
| extend IPAddressFirst = tostring(IPAddresses[0]), Name = tostring(split(UserPrincipalName, "@")[0]), UPNSuffix = tostring(split(UserPrincipalName, "@")[1])
};
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
union isfuzzy=true aadSignin, aadNonInt
severity: Low
triggerOperator: gt
kind: Scheduled
relevantTechniques:
- T1078
- T1098
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/BypassCondAccessRule.yaml
queryFrequency: 1d
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- connectorId: AzureActiveDirectory
dataTypes:
- AADNonInteractiveUserSignInLogs
description: |
'Identifies an attempt to Bypass conditional access rule(s) in Microsoft Entra ID.
The ConditionalAccessStatus column value details if there was an attempt to bypass Conditional Access or if the Conditional access rule was not satisfied (ConditionalAccessStatus == 1).
References:
https://docs.microsoft.com/azure/active-directory/conditional-access/overview
https://docs.microsoft.com/azure/active-directory/reports-monitoring/concept-sign-ins
https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes
ConditionalAccessStatus == 0 // Success
ConditionalAccessStatus == 1 // Failure
ConditionalAccessStatus == 2 // Not Applied
ConditionalAccessStatus == 3 // unknown'
status: Available
version: 1.0.7
entityMappings:
- fieldMappings:
- columnName: UserPrincipalName
identifier: FullName
- columnName: Name
identifier: Name
- columnName: UPNSuffix
identifier: UPNSuffix
entityType: Account
- fieldMappings:
- columnName: UserId
identifier: AadUserId
entityType: Account
- fieldMappings:
- columnName: IPAddressFirst
identifier: Address
entityType: IP
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"apiVersion": "2024-01-01-preview",
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/3af9285d-bb98-4a35-ad29-5ea39ba0c628')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/3af9285d-bb98-4a35-ad29-5ea39ba0c628')]",
"properties": {
"alertRuleTemplateName": "3af9285d-bb98-4a35-ad29-5ea39ba0c628",
"customDetails": null,
"description": "'Identifies an attempt to Bypass conditional access rule(s) in Microsoft Entra ID.\nThe ConditionalAccessStatus column value details if there was an attempt to bypass Conditional Access or if the Conditional access rule was not satisfied (ConditionalAccessStatus == 1).\nReferences:\nhttps://docs.microsoft.com/azure/active-directory/conditional-access/overview\nhttps://docs.microsoft.com/azure/active-directory/reports-monitoring/concept-sign-ins\nhttps://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes\nConditionalAccessStatus == 0 // Success\nConditionalAccessStatus == 1 // Failure\nConditionalAccessStatus == 2 // Not Applied\nConditionalAccessStatus == 3 // unknown'\n",
"displayName": "Attempt to bypass conditional access rule in Microsoft Entra ID",
"enabled": true,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "UserPrincipalName",
"identifier": "FullName"
},
{
"columnName": "Name",
"identifier": "Name"
},
{
"columnName": "UPNSuffix",
"identifier": "UPNSuffix"
}
]
},
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "UserId",
"identifier": "AadUserId"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "IPAddressFirst",
"identifier": "Address"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Microsoft Entra ID/Analytic Rules/BypassCondAccessRule.yaml",
"query": "let threshold = 1; // Modify this threshold value to reduce false positives based on your environment\nlet aadFunc = (tableName:string){\ntable(tableName)\n| where ConditionalAccessStatus == 1 or ConditionalAccessStatus =~ \"failure\"\n| mv-apply CAP = parse_json(ConditionalAccessPolicies) on (\n project ConditionalAccessPoliciesName = CAP.displayName, result = CAP.result\n | where result =~ \"failure\"\n)\n| extend DeviceDetail = todynamic(DeviceDetail), Status = todynamic(Status), LocationDetails = todynamic(LocationDetails)\n| extend OS = DeviceDetail.operatingSystem, Browser = DeviceDetail.browser\n| extend State = tostring(LocationDetails.state), City = tostring(LocationDetails.city), Region = tostring(LocationDetails.countryOrRegion)\n| extend StatusCode = tostring(Status.errorCode), StatusDetails = tostring(Status.additionalDetails)\n| extend Status = strcat(StatusCode, \": \", ResultDescription)\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), Status = make_list(Status,10), StatusDetails = make_list(StatusDetails,50), IPAddresses = make_list(IPAddress,100), IPAddressCount = dcount(IPAddress), CorrelationIds = make_list(CorrelationId,100), ConditionalAccessPoliciesName = make_list(ConditionalAccessPoliciesName,100)\nby UserPrincipalName, UserId, AppDisplayName, tostring(Browser), tostring(OS), City, State, Region, Type\n| where IPAddressCount > threshold and StatusDetails !has \"MFA successfully completed\"\n| mv-expand IPAddresses, Status, StatusDetails, CorrelationIds\n| extend Status = strcat(Status, \" \", StatusDetails)\n| summarize IPAddresses = make_set(IPAddresses,100), Status = make_set(Status,10), CorrelationIds = make_set(CorrelationIds,100), ConditionalAccessPoliciesName = make_set(ConditionalAccessPoliciesName,100)\nby StartTime, EndTime, UserPrincipalName, UserId, AppDisplayName, tostring(Browser), tostring(OS), City, State, Region, IPAddressCount, Type\n| extend IPAddressFirst = tostring(IPAddresses[0]), Name = tostring(split(UserPrincipalName, \"@\")[0]), UPNSuffix = tostring(split(UserPrincipalName, \"@\")[1])\n};\nlet aadSignin = aadFunc(\"SigninLogs\");\nlet aadNonInt = aadFunc(\"AADNonInteractiveUserSignInLogs\");\nunion isfuzzy=true aadSignin, aadNonInt\n",
"queryFrequency": "P1D",
"queryPeriod": "P1D",
"severity": "Low",
"status": "Available",
"subTechniques": [],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"InitialAccess",
"Persistence"
],
"techniques": [
"T1078",
"T1098"
],
"templateVersion": "1.0.7",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}