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.