Query Details

Zscaler ZPA/ZIA - Perfect Impostor: Account Takeover Hiding Within Normal Traffic

36 CSL ZPA Perfect Impostor Account Takeover

Query

let baselineWindow = 14d;
let recentWindow   = 4h;
let ZPA_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)
    | extend BaselineCountry = tostring(geo_info_from_ip_address(SourceIP).country)
    | summarize
        BaselineCountries = make_set(BaselineCountry, 20),
        BaselineAppCount  = dcount(DestinationHostName)
      by UserName = tolower(SourceUserName);
let ZPA_Recent =
    CommonSecurityLog
    | where TimeGenerated > ago(recentWindow)
    | where DeviceVendor == "Zscaler" and DeviceProduct has "ZPA"
    | where isnotempty(SourceUserName)
    | extend RecentCountry = tostring(geo_info_from_ip_address(SourceIP).country)
    | summarize
        RecentCountries = make_set(RecentCountry, 5),
        RecentApps      = make_set(DestinationHostName, 10),
        RecentIPs       = make_set(SourceIP, 5),
        RecentConnCount = count(),
        ZPA_FirstSeen   = min(TimeGenerated)
      by UserName = tolower(SourceUserName);
let ZIA_Suspicious =
    CommonSecurityLog
    | where TimeGenerated > ago(recentWindow)
    | where DeviceVendor == "Zscaler"
    | where isnotempty(SourceUserName)
    | where DeviceCustomString2 has_any (
        "REMOTE_ACCESS_TOOLS", "REMOTE_ADMINISTRATION", "WEB_BASED_ADMIN",
        "ACCOUNT_CREATION", "TUNNEL_SOCKS_PROXY", "ANONYMOUS_PROXY",
        "ONLINE_STORAGE", "FILE_SHARE", "TOR", "VPN_SERVICES",
        "NEWLY_REGISTERED_DOMAINS", "PHISHING")
    | summarize
        ZIA_Categories   = make_set(DeviceCustomString2, 10),
        ZIA_Destinations = make_set(DestinationHostName, 10),
        ZIA_RequestCount = count(),
        ZIA_FirstSeen    = min(TimeGenerated)
      by UserName = tolower(SourceUserName);
let IdP_Changes =
    OfficeActivity
    | where TimeGenerated > ago(recentWindow)
    | where isnotempty(UserId)
    | where Operation in (
        "New-InboxRule",
        "Set-Mailbox",
        "Add-MailboxPermission",
        "Add OAuth2PermissionGrant",
        "Consent to application",
        "Add service principal credentials",
        "Update user",
        "Disable Strong Authentication",
        "Update StsRefreshTokenValidFrom",
        "Add member to role")
    | summarize
        IdP_EventCount = count(),
        IdP_Operations = make_set(Operation, 10),
        IdP_FirstSeen  = min(TimeGenerated)
      by UserName = tolower(UserId);
ZPA_Recent
| join kind=inner ZPA_Baseline on UserName
| extend NewCountries = set_difference(RecentCountries, BaselineCountries)
| join kind=leftouter ZIA_Suspicious on UserName
| join kind=leftouter IdP_Changes on UserName
| extend
    ZIA_RequestCount = coalesce(ZIA_RequestCount, 0),
    IdP_EventCount   = coalesce(IdP_EventCount, 0)
| extend ImpostorScore =
    iff(array_length(NewCountries) > 0, 40, 0)
  + (ZIA_RequestCount * 5)
  + (IdP_EventCount * 30)
| where ImpostorScore >= 40
    or (IdP_EventCount >= 1 and ZIA_RequestCount >= 1)
    or array_length(NewCountries) > 0
| project
    UserName,
    NewCountries, RecentCountries, BaselineCountries,
    RecentApps, RecentIPs, RecentConnCount,
    ZIA_Categories, ZIA_Destinations, ZIA_RequestCount,
    IdP_Operations, IdP_EventCount,
    ImpostorScore,
    ZPA_FirstSeen
| order by ImpostorScore desc

Explanation

This query is designed to detect potential account takeovers by identifying unusual behavior across different layers of user activity. Here's a simplified breakdown:

  1. Purpose: The query aims to identify "perfect impostor" scenarios where an attacker uses stolen credentials and mimics a legitimate user's behavior but introduces subtle anomalies.

  2. Layers Analyzed:

    • ZPA Layer: Checks for user sessions from countries not seen in the past 14 days, indicating possible use of a new device, VPN, or compromised session.
    • ZIA Layer: Looks for browsing activity to suspicious sites like remote admin tools, proxies, or newly registered domains, which are not typical for the user.
    • IdP/SaaS Layer: Monitors critical account changes such as MFA removal or privilege escalation.
  3. Scoring: Each layer is scored individually. A high score or a combination of suspicious activities across layers flags the account as potentially compromised.

  4. Outcome: The query generates a prioritized list of high-risk accounts for further review and action, such as session termination or MFA re-enforcement, to prevent attackers from gaining persistence or escalating privileges.

  5. Frequency and Duration: The query runs every 4 hours and analyzes data from the past 14 days.

  6. Alerting: If suspicious activity is detected, an alert is created with details about the anomalies and a calculated "ImpostorScore" to prioritize investigation.

Overall, this query helps security teams quickly identify and respond to potential account takeovers by correlating anomalies across multiple layers of user activity.

Details

David Alonso profile picture

David Alonso

Released: March 2, 2026

Tables

CommonSecurityLogOfficeActivity

Keywords

DevicesZscalerZPAZIAIdPSaaSOfficeActivityUserAccountCommonSecurityEventsCommonSecurityLogRemoteAccessToolsRemoteAdministrationWebBasedAdminAccountCreationTunnelSocksProxyAnonymousProxyOnlineStorageFileShareTORVPNServicesNewlyRegisteredDomainsPhishingNewInboxRuleSetMailboxAddMailboxPermissionAddOAuth2PermissionGrantConsentToApplicationAddServicePrincipalCredentialsUpdateUserDisableStrongAuthenticationUpdateStsRefreshTokenValidFromAddMemberToRole

Operators

letbetweenagohas!inisnotemptytostringgeo_info_from_ip_addresssummarizemake_setdcounttolowerhas_anycountmininjoinkindextendset_differenceleftoutercoalesceiffarray_lengthprojectorder bydesc

Severity

High

Tactics

InitialAccessPersistencePrivilegeEscalationDefenseEvasionCredentialAccess

MITRE Techniques

Frequency: PT4H

Period: P14D

Actions

GitHub