Query Details

33 CSL ZPA First Time App Access

Query

id: a033b1c2-d3e4-4f5a-6b7c-8d9e0f1a2b3c
name: "Zscaler ZPA - First-Time Access to Internal Application - Possible Lateral Movement"
version: 1.0.0
kind: Scheduled
description: |
  Detects ZPA users successfully accessing internal applications they have never connected to in the previous 14 days. In a well-managed ZPA deployment, users have defined app segment policies; accessing new applications outside their normal pattern can indicate logical lateral movement — an attacker using valid credentials to explore internal resources, privilege escalation that granted access to additional app segments, or a legitimate administrator testing new policies. MITRE ATT&CK: T1078 (Valid Accounts), T1021 (Remote Services).
severity: Medium
requiredDataConnectors:
  - connectorId: CommonSecurityEvents
    dataTypes:
      - CommonSecurityLog
queryFrequency: PT1H
queryPeriod: P14D
triggerOperator: gt
triggerThreshold: 0
tactics:
  - LateralMovement
  - InitialAccess
relevantTechniques:
  - T1078
  - T1021
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", "Blocked", "Failed", "Error")
        | where isnotempty(SourceUserName) and isnotempty(DestinationHostName)
        | summarize HistoricApps = make_set(DestinationHostName)
            by SourceUserName;
    let recent = CommonSecurityLog
        | where TimeGenerated > ago(recentWindow)
        | where DeviceVendor == "Zscaler" and DeviceProduct has "ZPA"
        | where DeviceAction !in ("block", "BLOCK", "Blocked", "Failed", "Error")
        | where isnotempty(SourceUserName) and isnotempty(DestinationHostName)
        | summarize
            RecentApps      = make_set(DestinationHostName),
            ConnectionCount = count(),
            SourceIPs       = make_set(SourceIP, 5)
            by SourceUserName;
    recent
    | join kind=leftouter baseline on SourceUserName
    | extend HistoricApps = coalesce(HistoricApps, dynamic([]))
    | extend FirstTimeApps = set_difference(RecentApps, HistoricApps)
    | where array_length(FirstTimeApps) > 0
    | project SourceUserName, FirstTimeApps, ConnectionCount, SourceIPs, RecentApps
    | order by ConnectionCount desc
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: SourceUserName
customDetails:
  ConnectionCount: ConnectionCount
  FirstTimeApps: FirstTimeApps
alertDetailsOverride:
  alertDisplayNameFormat: "ZPA First-Time App Access - {{SourceUserName}} (new internal apps)"
  alertDescriptionFormat: "ZPA user {{SourceUserName}} accessed {{ConnectionCount}} connections to internal apps never seen in 14-day baseline: {{FirstTimeApps}}."
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT6H
    matchingMethod: Selected
    groupByEntities:
      - Account
    groupByAlertDetails: []
    groupByCustomDetails: []

Explanation

This query is designed to detect unusual access patterns in a Zscaler Private Access (ZPA) environment, which could indicate potential security threats such as lateral movement by an attacker. Here's a simple breakdown of what the query does:

  1. Purpose: It identifies users who have accessed internal applications for the first time within the last hour, where they haven't accessed these applications in the previous 14 days. This could suggest unauthorized exploration of internal resources.

  2. Data Source: The query uses data from the CommonSecurityLog, specifically looking at logs from Zscaler ZPA.

  3. Time Frames:

    • Recent Window: The last hour (1h).
    • Baseline Window: The previous 14 days (14d).
  4. Process:

    • Baseline Data: It collects a list of applications each user has accessed in the past 14 days.
    • Recent Data: It gathers data on applications accessed by users in the last hour.
    • Comparison: It compares the two datasets to find applications that users have accessed for the first time in the recent window.
  5. Output: The query produces a list of users who have accessed new applications, along with details such as the number of connections made, the new applications accessed, and the source IPs used.

  6. Alerting: If any first-time access is detected, an alert is generated with details about the user and the new applications accessed. The alert is categorized under tactics like Lateral Movement and Initial Access, referencing MITRE ATT&CK techniques T1078 (Valid Accounts) and T1021 (Remote Services).

  7. Incident Management: The system is set up to create incidents for these alerts, with configurations for grouping related alerts by user account.

In summary, this query helps security teams monitor for potential unauthorized access or lateral movement within their network by identifying users accessing new internal applications, which could indicate a security breach or policy testing.

Details

David Alonso profile picture

David Alonso

Released: March 2, 2026

Tables

CommonSecurityLog

Keywords

ZscalerZPAUsersInternalApplicationsDeviceVendorDeviceProductDeviceActionSourceUserNameDestinationHostNameSourceIP

Operators

letbetweenagohas!inisnotemptysummarizemake_setby>joinkind=leftouteronextendcoalescedynamicset_differencewherearray_length>projectorder bydesc

Actions