IP addresses with multiple failed Microsoft Entra ID logins may indicate credential stuffing attempts, and a subsequent successful login to Palo Alto VPN suggests the adversary has compromised credentials, making proactive hunting critical to identify and mitigate potential lateral movement in Azure Sentinel. SOC teams should hunt for this behavior to detect and respond to credential-based attacks before they lead to broader network compromise.
KQL Query
//Set a threshold of failed AAD signins from an IP address within 1 day above which we want to deem those logins suspicious.
let signin_threshold = 5;
//Make a list of IPs with AAD signin failures above our threshold.
let aadFunc = (tableName:string){
let suspicious_signins =
table(tableName)
//Looking for logon failure results
| where ResultType !in ("0", "50125", "50140")
//Exclude localhost addresses to reduce the chance of FPs
| where IPAddress !in ("127.0.0.1", "::1")
| summarize count() by IPAddress
| where count_ > signin_threshold
| summarize make_set(IPAddress);
suspicious_signins
};
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
let suspicious_signins =
union isfuzzy=true aadSignin, aadNonInt
| summarize make_set(set_IPAddress);
//See if any of those IPs have sucessfully logged into PA VPNs during the same timeperiod
CommonSecurityLog
//Select only PA VPN sucessful logons
| where DeviceVendor == "Palo Alto Networks" and DeviceEventClassID == "globalprotect"
| where Message has "GlobalProtect gateway user authentication succeeded"
//Parse out the logon source IP from the Message field to match on
| extend SourceIP = extract("Login from: ([^,]+)", 1, Message)
| where SourceIP in (suspicious_signins)
| extend Reason = "Multiple failed AAD logins from SourceIP"
//Parse out other useful information from Message field
| extend User = extract('User name: ([^,]+)', 1, Message)
| extend ClientOS = extract('Client OS version: ([^,\"]+)', 1, Message)
| extend Location = extract('Source region: ([^,]{2})',1, Message)
| project TimeGenerated, Reason, SourceIP, User, ClientOS, Location, Message, DeviceName, ReceiptTime, DeviceVendor, DeviceEventClassID, Computer, FileName
| extend timestamp = TimeGenerated
id: ba144bf8-75b8-406f-9420-ed74397f9479
name: IP with multiple failed Microsoft Entra ID logins successfully logs in to Palo Alto VPN
description: |
This query creates a list of IP addresses with the number of failed login attempts to Entra ID
above a set threshold ( default of 5 ). It then looks for any successful Palo Alto VPN logins from any of these IPs within the same timeframe.
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- connectorId: AzureActiveDirectory
dataTypes:
- AADNonInteractiveUserSignInLogs
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- CredentialAccess
relevantTechniques:
- T1078
- T1110
query: |
//Set a threshold of failed AAD signins from an IP address within 1 day above which we want to deem those logins suspicious.
let signin_threshold = 5;
//Make a list of IPs with AAD signin failures above our threshold.
let aadFunc = (tableName:string){
let suspicious_signins =
table(tableName)
//Looking for logon failure results
| where ResultType !in ("0", "50125", "50140")
//Exclude localhost addresses to reduce the chance of FPs
| where IPAddress !in ("127.0.0.1", "::1")
| summarize count() by IPAddress
| where count_ > signin_threshold
| summarize make_set(IPAddress);
suspicious_signins
};
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
let suspicious_signins =
union isfuzzy=true aadSignin, aadNonInt
| summarize make_set(set_IPAddress);
//See if any of those IPs have sucessfully logged into PA VPNs during the same timeperiod
CommonSecurityLog
//Select only PA VPN sucessful logons
| where DeviceVendor == "Palo Alto Networks" and DeviceEventClassID == "globalprotect"
| where Message has "GlobalProtect gateway user authentication succeeded"
//Parse out the logon source IP from the Message field to match on
| extend SourceIP = extract("Login from: ([^,]+)", 1, Message)
| where SourceIP in (suspicious_signins)
| extend Reason = "Multiple failed AAD logins from SourceIP"
//Parse out other useful information from Message field
| extend User = extract('User name: ([^,]+)', 1, Message)
| extend ClientOS = extract('Client OS version: ([^,\"]+)', 1, Message)
| extend Location = extract('Source region: ([^,]{2})',1, Message)
| project TimeGenerated, Reason, SourceIP, User, ClientOS, Location, Message, DeviceName, ReceiptTime, DeviceVendor, DeviceEventClassID, Computer, FileName
| extend timestamp = TimeGenerated
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: User
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: Devic
| Sentinel Table | Notes |
|---|---|
AADNonInteractiveUserSignInLogs | Ensure this data connector is enabled |
CommonSecurityLog | Ensure this data connector is enabled |
SigninLogs | Ensure this data connector is enabled |
Scenario: Scheduled System Maintenance Job
Description: A scheduled job runs on the Palo Alto firewall to update security policies or perform health checks, which requires a successful login from the admin IP address.
Filter/Exclusion: Exclude IP addresses associated with scheduled maintenance tasks or use a filter based on the user field to exclude known admin accounts used for automation.
Scenario: Microsoft Entra ID Sync Job
Description: A failed login attempt occurs during a Microsoft Entra ID synchronization job (e.g., Azure AD Connect), which is followed by a successful login to the Palo Alto firewall to complete the sync.
Filter/Exclusion: Exclude IP addresses that are known to be used by synchronization tools like Azure AD Connect or use a filter based on the source field to exclude internal sync services.
Scenario: Admin Access from a Trusted IP for Patching
Description: An admin logs in from a trusted IP address to apply patches or updates to the Palo Alto firewall, which results in a successful login after several failed attempts from the same IP due to incorrect credentials.
Filter/Exclusion: Exclude IP addresses that are whitelisted for administrative access or use a filter based on the ip field to exclude known trusted IPs used for maintenance.
Scenario: False Positive from a Misconfigured Log Source
Description: A misconfigured log source (e.g., a third-party tool or SIEM integration) generates false failed login attempts to Entra ID, which are then followed by a legitimate successful login to the Palo Alto firewall.
Filter/Exclusion: Exclude log sources that are known to be misconfigured or use a filter based on the source or device field to exclude non-Entra ID log sources.
Scenario: Multi-Factor Authentication (MFA) Retry Attempts
Description: An admin attempts to log