Detection rules › Kusto

Netskope - New Risky App Access vs 7-Day Baseline

Status
available
Severity
medium
Time window
8d
Group by
CsUsername, XCsApp
Source
github.com/Azure/Azure-Sentinel

Compares today's accessed applications against a 7-day baseline and triggers alerts when users access new risky applications not seen before.

MITRE ATT&CK coverage

TacticTechniques
Initial AccessT1199 Trusted Relationship
DiscoveryT1526 Cloud Service Discovery

Rule body kusto

id: ba66b81c-2cf7-4c53-9db0-e8b6f537704a
name: Netskope - New Risky App Access vs 7-Day Baseline
description: |
  Compares today's accessed applications against a 7-day baseline and triggers alerts when users access new risky applications not seen before.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: NetskopeWebTxConnector
    dataTypes:
      - NetskopeWebTransactions_CL
queryFrequency: 1d
queryPeriod: 8d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Discovery
relevantTechniques:
  - T1199
  - T1526
query: |
  let lookbackPeriod = 7d;
  let currentPeriod = 1d;
  let baseline = NetskopeWebTransactions_CL
      | where TimeGenerated between (ago(lookbackPeriod) .. ago(currentPeriod))
      | where isnotempty(CsUsername) and isnotempty(XCsApp)
      | summarize BaselineApps = make_set(XCsApp) by CsUsername;
  let current = NetskopeWebTransactions_CL
      | where TimeGenerated > ago(currentPeriod)
      | where isnotempty(CsUsername) and isnotempty(XCsApp)
      | where XCsAppCcl =~ 'poor' or XCsAppCcl =~ 'low' or XCsAppCcl =~ 'medium' or XCsAppCci < 70
      | summarize 
          CurrentApps = make_set(XCsApp),
          arg_max(TimeGenerated, XCsAppCcl, XCsAppCci, XCsAppCategory)
          by CsUsername, XCsApp;
  current
  | join kind=leftouter baseline on CsUsername
  | extend BaselineApps = coalesce(BaselineApps, dynamic([]))
  | where not(set_has_element(BaselineApps, XCsApp))
  | where isnotempty(XCsApp)
  | where XCsAppCci < 70 or XCsAppCcl =~ 'poor' or XCsAppCcl =~ 'low'
  | project 
      TimeGenerated,
      User = CsUsername,
      NewRiskyApp = XCsApp,
      AppCCL = XCsAppCcl,
      AppCCI = XCsAppCci,
      AppCategory = XCsAppCategory,
      BaselineAppCount = array_length(BaselineApps)
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: User
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: NewRiskyApp
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let lookbackPeriod = 7d;
let currentPeriod = 1d;

Let binding: baseline

let baseline = NetskopeWebTransactions_CL
    | where TimeGenerated between (ago(lookbackPeriod) .. ago(currentPeriod))
    | where isnotempty(CsUsername) and isnotempty(XCsApp)
    | summarize BaselineApps = make_set(XCsApp) by CsUsername;

Derived from lookbackPeriod, currentPeriod.

The stages below define let current (the rule's main pipeline source).

Stage 1: source

NetskopeWebTransactions_CL

Stage 2: where

| where TimeGenerated > ago(currentPeriod)

Stage 3: where

| where isnotempty(CsUsername) and isnotempty(XCsApp)

Stage 4: where

| where XCsAppCcl =~ 'poor' or XCsAppCcl =~ 'low' or XCsAppCcl =~ 'medium' or XCsAppCci < 70

Stage 5: summarize

| summarize 
        CurrentApps = make_set(XCsApp),
        arg_max(TimeGenerated, XCsAppCcl, XCsAppCci, XCsAppCategory)
        by CsUsername, XCsApp

The stages below run on current (the outer pipeline).

Stage 6: join

current
| join kind=leftouter baseline on CsUsername

Stage 7: extend

| extend BaselineApps = coalesce(BaselineApps, dynamic([]))

Stage 8: where

| where not(set_has_element(BaselineApps, XCsApp))

Stage 9: where

| where isnotempty(XCsApp)

Stage 10: where

| where XCsAppCci < 70 or XCsAppCcl =~ 'poor' or XCsAppCcl =~ 'low'

Stage 11: project

| project 
    TimeGenerated,
    User = CsUsername,
    NewRiskyApp = XCsApp,
    AppCCL = XCsAppCcl,
    AppCCI = XCsAppCci,
    AppCategory = XCsAppCategory,
    BaselineAppCount = array_length(BaselineApps)

Exclusions

Top-level NOT(...) conjuncts: predicates this rule actively suppresses.

FieldKindExcluded values
XCsAppeqBaselineApps

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
CsUsernameis_not_null
  • (no value, null check)
XCsAppis_not_null
  • (no value, null check)
XCsAppCcilt
  • 70 transforms: cased
XCsAppCcleq
  • low
  • medium
  • poor

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
AppCCIproject
AppCCLproject
AppCategoryproject
BaselineAppCountproject
NewRiskyAppproject
TimeGeneratedproject
Userproject