Query Details

Zscaler ZPA - Access to Internal Application from Anomalous Geolocation

31 CSL ZPA Anomalous Geolocation Access

Query

let recentWindow   = 1h;
let baselineWindow = 14d;
let baseline = CommonSecurityLog
    | where TimeGenerated between (ago(baselineWindow) .. ago(recentWindow))
    | where DeviceVendor == "Zscaler" and DeviceProduct has "ZPA"
    | where DeviceAction !in ("block", "BLOCK", "Failed")
    | where isnotempty(SourceUserName) and isnotempty(SourceIP)
    | extend SrcCountry = tostring(geo_info_from_ip_address(SourceIP).country)
    | summarize BaselineCountries = make_set(SrcCountry)
        by SourceUserName;
let recent = CommonSecurityLog
    | where TimeGenerated > ago(recentWindow)
    | where DeviceVendor == "Zscaler" and DeviceProduct has "ZPA"
    | where DeviceAction !in ("block", "BLOCK", "Failed")
    | where isnotempty(SourceUserName) and isnotempty(SourceIP)
    | extend SrcCountry = tostring(geo_info_from_ip_address(SourceIP).country)
    | summarize
        RecentCountries = make_set(SrcCountry),
        AccessedApps    = make_set(DestinationHostName, 10),
        ConnectionCount = count(),
        SrcIPs          = make_set(SourceIP, 5)
        by SourceUserName;
recent
| join kind=leftouter baseline on SourceUserName
| extend BaselineCountries = coalesce(BaselineCountries, dynamic([]))
| extend NewCountries = set_difference(RecentCountries, BaselineCountries)
| where array_length(NewCountries) > 0
| project SourceUserName, NewCountries, AccessedApps, ConnectionCount, SrcIPs
| order by ConnectionCount desc

Explanation

This query is designed to detect unusual access patterns to internal applications using Zscaler Private Access (ZPA). Here's a simplified breakdown of what it does:

  1. Purpose: It identifies when a user accesses internal applications from a country they haven't connected from in the past 14 days. This could indicate a security issue, such as compromised credentials or a legitimate user traveling to an unusual location.

  2. Data Source: It uses logs from Zscaler's CommonSecurityLog, focusing on successful connections (not blocked or failed attempts).

  3. Time Frames:

    • Baseline Window: Looks at the past 14 days to establish a baseline of countries from which each user has accessed internal applications.
    • Recent Window: Examines the last hour to identify new access locations.
  4. Process:

    • Baseline Creation: For each user, it collects a list of countries they have accessed from in the past 14 days.
    • Recent Activity: It gathers data on the countries accessed in the last hour, the applications accessed, the number of connections, and the source IPs.
    • Comparison: It compares the recent countries to the baseline. If there are new countries not seen in the baseline, it flags this as anomalous.
  5. Output: The query outputs details such as the username, new countries accessed, applications accessed, the number of connections, and source IPs. It orders the results by the number of connections.

  6. Alerting: If any anomalies are detected, it creates an alert with details about the user and the new countries accessed. It also supports incident creation and grouping based on user accounts.

  7. Severity and Techniques: The alert is marked with high severity and is associated with MITRE ATT&CK techniques related to valid accounts and external remote services.

In summary, this query helps identify potentially unauthorized access to internal applications by detecting connections from new and unusual geolocations.

Details

David Alonso profile picture

David Alonso

Released: March 2, 2026

Tables

CommonSecurityLog

Keywords

ZscalerZPAInternalApplicationGeolocationCredentialUserAccountSourceIPDestinationHostNameConnectionCountCountry

Operators

letbetweenagohas!inisnotemptytostringgeo_info_from_ip_addresssummarizemake_setby>joinkind=leftouteroncoalescedynamicset_differencearray_lengthprojectorder bydesc

Severity

High

Tactics

InitialAccessLateralMovement

MITRE Techniques

Frequency: PT1H

Period: P14D

Actions

GitHub