Detection rules › Kusto

New Device/Location sign-in along with critical operation

Status
available
Severity
medium
Time window
1h
Group by
actor_alternateId_s, actor_displayName_s, authenticationContext_externalSessionId_s, client_device_s, client_geographicalContext_city_s, client_geographicalContext_country_s, client_geographicalContext_geolocation_lat_d, client_geographicalContext_geolocation_lon_d, client_ipAddress_s, client_userAgent_browser_s, client_userAgent_os_s, client_userAgent_rawUserAgent_s, displayMessage_s, outcome_reason_s, outcome_result_s, timekey
Source
github.com/Azure/Azure-Sentinel

This query identifies users seen login from a new geo location/country and a new device, then correlates that sign-in with successful risky operations such as policy changes, MFA changes, API token actions etc. This can be an indication of an attacker gaining access to a user's credentials and then performing critical operations, which typically requires admin privileges. By detecting such patterns, organizations can quickly respond to potential security incidents and mitigate risks associated with unauthorized access and privilege escalation.

MITRE ATT&CK coverage

Telemetry coverage

Rules detecting the same action

These rules filter on the same operation.

Rule body

id: 41e843a8-92e7-444d-8d72-638f1145d1e1
name: New Device/Location sign-in along with critical operation
description: |
  This query identifies users seen login from a new geo location/country and a new device, then correlates that sign-in with successful risky operations such as policy changes, MFA changes, API token actions etc. This can be an indication of an attacker gaining access to a user's credentials and then performing critical operations, which typically requires admin privileges. By detecting such patterns, organizations can quickly respond to potential security incidents and mitigate risks associated with unauthorized access and privilege escalation.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: OktaSSO
    dataTypes:
      - Okta_CL
  - connectorId: OktaSSOv2
    dataTypes:
      - OktaSSO
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1078
  - T1556
  - T1098
query: |
  let timeframe = 1h;
  let RiskyOperations = dynamic(["policy.rule.update","policy.rule.create","policy.rule.delete", "policy.rule.deactivate", "policy.lifecycle.update", "policy.rule.modify", "policy.lifecycle.create", "policy.lifecycle.delete", "policy.lifecycle.deactivate", "policy.lifecycle.modify", "network_zone.rule.disabled", "system.api_token.create", "system.api_token.revoke", "application.policy.sign_on.update", "application.policy.sign_on.rule.delete","user.mfa.factor.deactivate", "user.mfa.factor.reset_all", "system.mfa.factor.deactivate", "user.mfa.attempt_bypass"]);
  let UserLoginNewCountryDevice = OktaSSO
  | where eventType_s == "user.session.start"
  | where outcome_result_s == "SUCCESS"
  | extend debugContext_debugData_logOnlySecurityData_s = column_ifexists('debugContext_debugData_logOnlySecurityData_s', '{}')
  | extend behaviors = parse_json(tostring(parse_json(debugContext_debugData_logOnlySecurityData_s).behaviors))
  | where tostring(behaviors.["New Country"]) == "POSITIVE"
  | where tostring(behaviors.["New Geo-Location"]) == "POSITIVE"
  | where tostring(behaviors.["New Device"]) == "POSITIVE"
  | summarize by timekey = bin(TimeGenerated, timeframe), actor_alternateId_s, actor_displayName_s, client_userAgent_os_s, client_userAgent_browser_s, client_device_s, client_userAgent_rawUserAgent_s, client_ipAddress_s, authenticationContext_externalSessionId_s, client_geographicalContext_country_s, client_geographicalContext_city_s, displayMessage_s, outcome_result_s, outcome_reason_s, column_ifexists('debugContext_debugData_logOnlySecurityData_s', ""), column_ifexists('debugContext_debugData_threatSuspected_s', ""), client_geographicalContext_geolocation_lat_d, client_geographicalContext_geolocation_lon_d
  | extend Location = strcat(client_geographicalContext_city_s, "-", client_geographicalContext_country_s);
  let RiskyOperationsObserved = OktaSSO
  | where eventType_s in (RiskyOperations)
  | where outcome_result_s == "SUCCESS"
  | summarize by timekey = bin(TimeGenerated, timeframe), actor_alternateId_s, actor_displayName_s, client_userAgent_os_s, client_userAgent_browser_s, client_device_s, client_userAgent_rawUserAgent_s, client_ipAddress_s, authenticationContext_externalSessionId_s, client_geographicalContext_country_s, client_geographicalContext_city_s, displayMessage_s, outcome_result_s, outcome_reason_s, column_ifexists('debugContext_debugData_logOnlySecurityData_s', ""), column_ifexists('debugContext_debugData_threatSuspected_s', ""), client_geographicalContext_geolocation_lat_d, client_geographicalContext_geolocation_lon_d;
  UserLoginNewCountryDevice
  | join kind=inner (RiskyOperationsObserved) on timekey, actor_displayName_s, client_ipAddress_s
  | project TimeGenerated = timekey, actor_alternateId_s, actor_displayName_s, client_userAgent_os_s, client_userAgent_browser_s, client_device_s, client_userAgent_rawUserAgent_s, client_ipAddress_s, authenticationContext_externalSessionId_s, client_geographicalContext_country_s, client_geographicalContext_city_s, displayMessage_s, outcome_result_s, outcome_reason_s, column_ifexists('debugContext_debugData_logOnlySecurityData_s', ""), column_ifexists('debugContext_debugData_threatSuspected_s', ""), client_geographicalContext_geolocation_lat_d, client_geographicalContext_geolocation_lon_d, Location
  | extend AccountName = tostring(split(actor_alternateId_s, "@")[0]), AccountUPNSuffix = tostring(split(actor_alternateId_s, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: client_ipAddress_s
customDetails:
  ActorDisplayName: actor_displayName_s
  ActorDevice: client_device_s
  ActorOS: client_userAgent_os_s
  ActorBrowser: client_userAgent_browser_s
  UserAgent: client_userAgent_rawUserAgent_s
  SessionId: authenticationContext_externalSessionId_s
  Location:  Location
alertDetailsOverride:
  alertDisplayNameFormat: 'New Device/Location {{Location}} sign-in along with critical operation'
  alertDescriptionFormat: |
    This query identifies users seen login from new geo location/country {{Location}} as well as a new device and performing critical operations.
version: 1.1.3
kind: Scheduled

Stages and Predicates

Parameters

let timeframe = 1h;

let UserLoginNewCountryDevice is inlined into the numbered stages below.

Let binding: RiskyOperations

let RiskyOperations = dynamic(["policy.rule.update","policy.rule.create","policy.rule.delete", "policy.rule.deactivate", "policy.lifecycle.update", "policy.rule.modify", "policy.lifecycle.create", "policy.lifecycle.delete", "policy.lifecycle.deactivate", "policy.lifecycle.modify", "network_zone.rule.disabled", "system.api_token.create", "system.api_token.revoke", "application.policy.sign_on.update", "application.policy.sign_on.rule.delete","user.mfa.factor.deactivate", "user.mfa.factor.reset_all", "system.mfa.factor.deactivate", "user.mfa.attempt_bypass"]);

Let binding: RiskyOperationsObserved used in Stage 11

let RiskyOperationsObserved = OktaSSO
| where eventType_s in (RiskyOperations)
| where outcome_result_s == "SUCCESS"
| summarize by timekey = bin(TimeGenerated, timeframe), actor_alternateId_s, actor_displayName_s, client_userAgent_os_s, client_userAgent_browser_s, client_device_s, client_userAgent_rawUserAgent_s, client_ipAddress_s, authenticationContext_externalSessionId_s, client_geographicalContext_country_s, client_geographicalContext_city_s, displayMessage_s, outcome_result_s, outcome_reason_s, column_ifexists('debugContext_debugData_logOnlySecurityData_s', ""), column_ifexists('debugContext_debugData_threatSuspected_s', ""), client_geographicalContext_geolocation_lat_d, client_geographicalContext_geolocation_lon_d;

Stages 1 to 10 define let UserLoginNewCountryDevice (the rule's main pipeline source); stages 11 to 13 run on it.

Stage 1: source

OktaSSO

Stage 2: where

| where eventType_s == "user.session.start"

Stage 3: where

| where outcome_result_s == "SUCCESS"

Stage 4: extend

| extend debugContext_debugData_logOnlySecurityData_s = column_ifexists('debugContext_debugData_logOnlySecurityData_s', '{}')

Stage 5: extend

| extend behaviors = parse_json(tostring(parse_json(debugContext_debugData_logOnlySecurityData_s).behaviors))

Stage 6: where

| where tostring(behaviors.["New Country"]) == "POSITIVE"

Stage 7: where

| where tostring(behaviors.["New Geo-Location"]) == "POSITIVE"

Stage 8: where

| where tostring(behaviors.["New Device"]) == "POSITIVE"

Stage 9: summarize

| summarize by timekey = bin(TimeGenerated, timeframe), actor_alternateId_s, actor_displayName_s, client_userAgent_os_s, client_userAgent_browser_s, client_device_s, client_userAgent_rawUserAgent_s, client_ipAddress_s, authenticationContext_externalSessionId_s, client_geographicalContext_country_s, client_geographicalContext_city_s, displayMessage_s, outcome_result_s, outcome_reason_s, column_ifexists('debugContext_debugData_logOnlySecurityData_s', ""), column_ifexists('debugContext_debugData_threatSuspected_s', ""), client_geographicalContext_geolocation_lat_d, client_geographicalContext_geolocation_lon_d

Stage 10: extend

| extend Location = strcat(client_geographicalContext_city_s, "-", client_geographicalContext_country_s)

Stage 11: join

UserLoginNewCountryDevice
| join kind=inner (RiskyOperationsObserved) on timekey, actor_displayName_s, client_ipAddress_s

Stage 12: project

| project TimeGenerated = timekey, actor_alternateId_s, actor_displayName_s, client_userAgent_os_s, client_userAgent_browser_s, client_device_s, client_userAgent_rawUserAgent_s, client_ipAddress_s, authenticationContext_externalSessionId_s, client_geographicalContext_country_s, client_geographicalContext_city_s, displayMessage_s, outcome_result_s, outcome_reason_s, column_ifexists('debugContext_debugData_logOnlySecurityData_s', ""), column_ifexists('debugContext_debugData_threatSuspected_s', ""), client_geographicalContext_geolocation_lat_d, client_geographicalContext_geolocation_lon_d, Location

Stage 13: extend

| extend AccountName = tostring(split(actor_alternateId_s, "@")[0]), AccountUPNSuffix = tostring(split(actor_alternateId_s, "@")[1])

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
New Countryeq
  • POSITIVE transforms: tostring
field:"New Country" kind:eq value:"POSITIVE"
New Deviceeq
  • POSITIVE transforms: tostring
field:"New Device" kind:eq value:"POSITIVE"
New Geo-Locationeq
  • POSITIVE transforms: tostring
field:"New Geo-Location" kind:eq value:"POSITIVE"
eventType_seq
  • user.session.start
field:"okta::eventType" kind:eq value:"user.session.start"
eventType_sin
  • application.policy.sign_on.rule.delete
  • application.policy.sign_on.update
  • network_zone.rule.disabled
  • policy.lifecycle.create
  • policy.lifecycle.deactivate
  • policy.lifecycle.delete
  • policy.lifecycle.modify
  • policy.lifecycle.update
  • policy.rule.create
  • policy.rule.deactivate
  • policy.rule.delete
  • policy.rule.modify
  • policy.rule.update
  • system.api_token.create
  • system.api_token.revoke
  • system.mfa.factor.deactivate
  • user.mfa.attempt_bypass
  • user.mfa.factor.deactivate
  • user.mfa.factor.reset_all
field:"okta::eventType" kind:in
outcome_result_seq
  • SUCCESS
field:"okta::outcome.result" kind:eq value:"SUCCESS"

Output fields

These fields are emitted when the rule matches.

FieldSource
Locationproject
TimeGeneratedproject
actor_alternateId_sproject
actor_displayName_sproject
authenticationContext_externalSessionId_sproject
client_device_sproject
client_geographicalContext_city_sproject
client_geographicalContext_country_sproject
client_geographicalContext_geolocation_lat_dproject
client_geographicalContext_geolocation_lon_dproject
client_ipAddress_sproject
client_userAgent_browser_sproject
client_userAgent_os_sproject
client_userAgent_rawUserAgent_sproject
displayMessage_sproject
outcome_reason_sproject
outcome_result_sproject
AccountNameextend
AccountUPNSuffixextend