Risky user sign-ins from non-Microsoft network devices may indicate compromised credentials or lateral movement by adversaries. SOC teams should proactively hunt for this behavior to detect potential credential theft or unauthorized access in their Azure Sentinel environment.
KQL Query
SigninLogs
//Find risky Signin
| where RiskState == "atRisk" and ResultType == 0
| extend Signin_Time = TimeGenerated
| summarize
AppDisplayName=make_set(AppDisplayName),
ClientAppUsed=make_set(ClientAppUsed),
UserAgent=make_set(UserAgent),
CorrelationId=make_set(CorrelationId),
Signin_Time= min(Signin_Time),
RiskEventTypes=make_set(RiskEventTypes)
by
ConditionalAccessStatus,
IPAddress,
IsRisky,
ResourceDisplayName,
RiskDetail,
ResultType,
RiskLevelAggregated,
RiskLevelDuringSignIn,
RiskState,
UserPrincipalName=tostring(tolower(UserPrincipalName)),
SourceSystem
| join kind=inner (
CommonSecurityLog
| where DeviceVendor has_any ("Palo Alto Networks", "Fortinet", "Check Point", "Zscaler")
| where DeviceProduct startswith "FortiGate" or DeviceProduct startswith "PAN" or DeviceProduct startswith "VPN" or DeviceProduct startswith "FireWall" or DeviceProduct startswith "NSSWeblog" or DeviceProduct startswith "URL"
| where DeviceAction != "Block"
| where isnotempty(RequestURL)
| where isnotempty(SourceUserName)
| extend SourceUserName = tolower(SourceUserName)
| summarize
min(TimeGenerated),
max(TimeGenerated),
Activity=make_set(Activity)
by DestinationHostName, DestinationIP, RequestURL, SourceUserName=tostring(tolower(SourceUserName)),DeviceVendor,DeviceProduct
| extend 3p_observed_Time= min_TimeGenerated,Name = tostring(split(SourceUserName,"@")[0]),UPNSuffix =tostring(split(SourceUserName,"@")[1]))
on $left.IPAddress == $right.DestinationIP and $left.UserPrincipalName == $right.SourceUserName
| extend Timediff = datetime_diff('day', 3p_observed_Time, Signin_Time)
| where Timediff <= 1 and Timediff >= 0
id: 042f2801-a375-4cfd-bd29-041fc7ed88a0
name: Risky user signin observed in non-Microsoft network device
description: |
'This content is utilized to identify instances of successful login by risky users, who have been observed engaging in potentially suspicious network activity on non-Microsoft network devices.'
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog (PaloAlto)
- connectorId: Fortinet
dataTypes:
- CommonSecurityLog (Fortinet)
- connectorId: CheckPoint
dataTypes:
- CommonSecurityLog (CheckPoint)
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog (Zscaler)
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
SigninLogs
//Find risky Signin
| where RiskState == "atRisk" and ResultType == 0
| extend Signin_Time = TimeGenerated
| summarize
AppDisplayName=make_set(AppDisplayName),
ClientAppUsed=make_set(ClientAppUsed),
UserAgent=make_set(UserAgent),
CorrelationId=make_set(CorrelationId),
Signin_Time= min(Signin_Time),
RiskEventTypes=make_set(RiskEventTypes)
by
ConditionalAccessStatus,
IPAddress,
IsRisky,
ResourceDisplayName,
RiskDetail,
ResultType,
RiskLevelAggregated,
RiskLevelDuringSignIn,
RiskState,
UserPrincipalName=tostring(tolower(UserPrincipalName)),
SourceSystem
| join kind=inner (
CommonSecurityLog
| where DeviceVendor has_any ("Palo Alto Networks", "Fortinet", "Check Point", "Zscaler")
| where DeviceProduct startswith "FortiGate" or DeviceProduct startswith "PAN" or DeviceProduct startswith "VPN" or DeviceProduct startswith "FireWall" or DeviceProduct startswith "NSSWeblog" or DeviceProduct startswith "URL"
| where DeviceAction != "Block"
| where isnotempty(RequestURL)
| where isnotempty(SourceUserName)
| extend SourceUserName = tolower(SourceUserName)
| summarize
min(TimeGenerated),
max(TimeGenerated),
Activity=make_set(Activity)
by DestinationHostName, DestinationIP, RequestURL, SourceUserName=tostring(tolower(SourceUserName)),DeviceVendor,DeviceProduct
| extend 3p_observed_Time= min_TimeGenerated,Name = tostring(split(SourceUserName,"@")[0]),UPNSuffix =tostring(split(SourceUserName,"@")[1]))
on $left.IPAddress == $right.DestinationIP and $left.UserPrincipalName == $right.SourceUserName
| extend Timediff = datetime_diff('day', 3p_observed_Time, Signin_Time)
| where Timediff <= 1 and Timediff >= 0
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserPrincipalName
- identifier: Name
colum
| Sentinel Table | Notes |
|---|---|
CommonSecurityLog | Ensure this data connector is enabled |
SigninLogs | Ensure this data connector is enabled |
Scenario: System administrator performing routine maintenance on a non-Microsoft network device (e.g., Cisco ASA firewall)
Filter/Exclusion: Exclude logins with user agent strings containing “admin”, “maintenance”, or “cisco” and with timestamps matching scheduled maintenance windows.
Scenario: Scheduled job execution on a non-Microsoft network device (e.g., Juniper SRX firewall) for network configuration backup
Filter/Exclusion: Exclude logins associated with service accounts (e.g., backup_svc) and with command lines containing “backup” or “snapshot”.
Scenario: Network device configuration change via SSH by a privileged user (e.g., Palo Alto PA Series firewall)
Filter/Exclusion: Exclude logins with SSH session IDs and command lines containing “configure”, “edit”, or “commit”.
Scenario: User accessing a non-Microsoft network device (e.g., Fortinet FortiGate) for troubleshooting purposes
Filter/Exclusion: Exclude logins with user roles marked as “read-only” or “support” and with IP addresses in the internal network range.
Scenario: Automated monitoring tool (e.g., SolarWinds Network Configuration Manager) logging in to a non-Microsoft device for status checks
Filter/Exclusion: Exclude logins with usernames containing “monitoring” or “solarwinds” and with command lines containing “status” or “check”.