↳ GitHub sourceAnalytics ruleMedium
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.
- Rule type
- Scheduled
- Version
- 1.0.4
- Query frequency
- 1d
- Query period
- 1d
- Trigger
- gt 0
Declared MITRE coverage
Declared sources
Metadata from the source file. No dependencies inferred from KQL.
Connectors
Data types
KQL query
Original query, unchanged.
//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
Declared entities
Related content
Links established from declared identifiers and solution manifests.
Source provenance
GitHubDisplayed values come from files in Azure/Azure-Sentinel. They describe the published template, not your workspace configuration.
- Commit
9800e51↗- Source identifier
ba144bf8-75b8-406f-9420-ed74397f9479
Additional source files 1
Detections/MultipleDataSources/AAD_PAVPN_Correlation.yamlsource ↗GSTEP / CATALOG TRACKING
Added to catalog : 16 Sept 2026 · 05:49 UTC
Last change observed : 16 Sept 2026 · 05:49 UTC