Detection rules › Kusto

Potential beaconing activity (ASIM Network Session schema)

Status
available
Severity
low
Time window
1h
Group by
DstIpAddr, DstPortNumber, SrcIpAddr, TimeDeltainSeconds
Source
github.com/Azure/Azure-Sentinel

This rule identifies beaconing patterns from Network traffic logs based on recurrent frequency patterns. Such potential outbound beaconing patterns to untrusted public networks should be investigated for any malware callbacks or data exfiltration attempts as discussed in this Blog. This analytic rule uses ASIM and supports any built-in or custom source that supports the ASIM NetworkSession schema'

MITRE ATT&CK coverage

Event coverage

Rule body kusto

id: fcb9d75c-c3c1-4910-8697-f136bfef2363
name: Potential beaconing activity (ASIM Network Session schema)
description: |
  This rule identifies beaconing patterns from Network traffic logs based on recurrent frequency patterns. 
  Such potential outbound beaconing patterns to untrusted public networks should be investigated for any malware callbacks or data exfiltration attempts as discussed in this [Blog](https://medium.com/@HuntOperator/detect-beaconing-with-flare-elastic-stack-and-intrusion-detection-systems-110dc74e0c56).
  This analytic rule uses [ASIM](https://aka.ms/AboutASIM) and supports any built-in or custom source that supports the ASIM NetworkSession schema'
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: AWSS3
    dataTypes:
      - AWSVPCFlow
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceNetworkEvents
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsForwardedEvents
    dataTypes:
      - WindowsEvent
  - connectorId: Zscaler
    dataTypes:
      - CommonSecurityLog
  - connectorId: MicrosoftSysmonForLinux
    dataTypes:
      - Syslog
  - connectorId: PaloAltoNetworks
    dataTypes:
      - CommonSecurityLog
  - connectorId: AzureMonitor(VMInsights)
    dataTypes:
      - VMConnection
  - connectorId: AzureFirewall
    dataTypes:
      - AzureDiagnostics
  - connectorId: AzureNSG
    dataTypes:
      - AzureDiagnostics
  - connectorId: CiscoASA
    dataTypes:
      - CommonSecurityLog
  - connectorId: CiscoAsaAma
    dataTypes:
      - CommonSecurityLog
  - connectorId: Corelight
    dataTypes:
      - Corelight_CL
  - connectorId: AIVectraStream
    dataTypes:
      - VectraStream
  - connectorId: CheckPoint
    dataTypes:
      - CommonSecurityLog
  - connectorId: Fortinet
    dataTypes:
      - CommonSecurityLog
  - connectorId: CiscoMeraki
    dataTypes:
      - Syslog
      - CiscoMerakiNativePoller

queryFrequency: 1d
queryPeriod: 2d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
  - T1571
tags:
  - ParentAlert: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/CommonSecurityLog/PaloAlto-NetworkBeaconing.yaml
    ParentVersion: 1.0.0
  - Schema: ASIMNetworkSession
    SchemaVersion: 0.2.4

query: |
  let querystarttime = 2d;
  let queryendtime = 1d;
  let TimeDeltaThreshold = 10;
  let TotalEventsThreshold = 15;
  let PercentBeaconThreshold = 80;
  let LocalNetworks=dynamic(["169.254.0.0/16","127.0.0.0/8"]);
  _Im_NetworkSession(starttime=ago(querystarttime), endtime=ago(queryendtime))
  | where not(ipv4_is_private(DstIpAddr))
  | where not (ipv4_is_in_any_range(DstIpAddr, LocalNetworks))
  | project 
      TimeGenerated
      , SrcIpAddr
      , SrcPortNumber
      , DstIpAddr
      , DstPortNumber
      , DstBytes
      , SrcBytes
  | sort by 
      SrcIpAddr asc
      , TimeGenerated asc
      , DstIpAddr asc
      , DstPortNumber asc
  | serialize
  | extend 
      nextTimeGenerated = next(TimeGenerated, 1)
      , nextSrcIpAddr = next(SrcIpAddr, 1)
  | extend 
      TimeDeltainSeconds = datetime_diff('second', nextTimeGenerated, TimeGenerated)
  | where SrcIpAddr == nextSrcIpAddr
  //Whitelisting criteria/ threshold criteria
  | where TimeDeltainSeconds > TimeDeltaThreshold 
  | project
      TimeGenerated
      , TimeDeltainSeconds
      , SrcIpAddr
      , SrcPortNumber
      , DstIpAddr
      , DstPortNumber
      , DstBytes
      , SrcBytes
  | summarize
      count()
      , sum(DstBytes)
      , sum(SrcBytes)
      , make_list(TimeDeltainSeconds) 
      by TimeDeltainSeconds
          , bin(TimeGenerated, 1h)
          , SrcIpAddr
          , DstIpAddr
          , DstPortNumber
  | summarize
      (MostFrequentTimeDeltaCount, MostFrequentTimeDeltainSeconds) = arg_max(count_, TimeDeltainSeconds)
      , TotalEvents=sum(count_)
      , TotalSrcBytes = sum(sum_SrcBytes)
      , TotalDstBytes = sum(sum_DstBytes)
      by bin(TimeGenerated, 1h)
          , SrcIpAddr
          , DstIpAddr
          , DstPortNumber
  | where TotalEvents > TotalEventsThreshold 
  | extend BeaconPercent = MostFrequentTimeDeltaCount/toreal(TotalEvents) * 100
  | where BeaconPercent > PercentBeaconThreshold
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: DstIpAddr

alertDetailsOverride:
  alertDisplayNameFormat: Potential beaconing from {{SrcIpAddr}} to {{DstIpAddr}}
  alertDescriptionFormat: Potential beaconing pattern from a client at address {{SrcIpAddr}} to a server at address {{DstIpAddr}} over port {{DstPortNumber}} identified. Such potential outbound beaconing pattern to untrusted public networks should be investigated for any malware callbacks or data exfiltration attempts as discussed in this [Blog](http://www.austintaylor.io/detect/beaconing/intrusion/detection/system/command/control/flare/elastic/stack/2017/06/10/detect-beaconing-with-flare-elasticsearch-and-intrusion-detection-systems/). The recurring frequency, reported as FrequencyTime in the custom details, and the total transferred volume reported as TotalDstBytes in the custom details, can help to determine the significance of this incident.

customDetails:
  DstPortNumber: DstPortNumber
  FrequencyCount: TotalSrcBytes
  FrequencyTime: MostFrequentTimeDeltaCount
  TotalDstBytes: TotalDstBytes

version: 1.1.6
kind: Scheduled

Stages and Predicates

Parameters

let querystarttime = 2d;
let queryendtime = 1d;
let TimeDeltaThreshold = 10;
let TotalEventsThreshold = 15;
let PercentBeaconThreshold = 80;
let LocalNetworks = dynamic(["169.254.0.0/16","127.0.0.0/8"]);

Stage 1: source

_Im_NetworkSession(starttime=ago(querystarttime), endtime=ago(queryendtime))

Stage 2: where

| where not(ipv4_is_private(DstIpAddr))

Stage 3: where

| where not (ipv4_is_in_any_range(DstIpAddr, LocalNetworks))

Stage 4: project

| project 
    TimeGenerated
    , SrcIpAddr
    , SrcPortNumber
    , DstIpAddr
    , DstPortNumber
    , DstBytes
    , SrcBytes

Stage 5: sort

| sort by 
    SrcIpAddr asc
    , TimeGenerated asc
    , DstIpAddr asc
    , DstPortNumber asc

Stage 6: kusto:serialize

| serialize

Stage 7: extend

| extend 
    nextTimeGenerated = next(TimeGenerated, 1)
    , nextSrcIpAddr = next(SrcIpAddr, 1)

Stage 8: extend

| extend 
    TimeDeltainSeconds = datetime_diff('second', nextTimeGenerated, TimeGenerated)

Stage 9: where

| where SrcIpAddr == nextSrcIpAddr

Stage 10: where

| where TimeDeltainSeconds > TimeDeltaThreshold

Stage 11: project

| project
    TimeGenerated
    , TimeDeltainSeconds
    , SrcIpAddr
    , SrcPortNumber
    , DstIpAddr
    , DstPortNumber
    , DstBytes
    , SrcBytes

Stage 12: summarize

| summarize
    count()
    , sum(DstBytes)
    , sum(SrcBytes)
    , make_list(TimeDeltainSeconds) 
    by TimeDeltainSeconds
        , bin(TimeGenerated, 1h)
        , SrcIpAddr
        , DstIpAddr
        , DstPortNumber

Stage 13: summarize

| summarize
    (MostFrequentTimeDeltaCount, MostFrequentTimeDeltainSeconds) = arg_max(count_, TimeDeltainSeconds)
    , TotalEvents=sum(count_)
    , TotalSrcBytes = sum(sum_SrcBytes)
    , TotalDstBytes = sum(sum_DstBytes)
    by bin(TimeGenerated, 1h)
        , SrcIpAddr
        , DstIpAddr
        , DstPortNumber
Threshold
gt 15

Stage 14: where

| where TotalEvents > TotalEventsThreshold

Stage 15: extend

| extend BeaconPercent = MostFrequentTimeDeltaCount/toreal(TotalEvents) * 100

Stage 16: where

| where BeaconPercent > PercentBeaconThreshold

Exclusions

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

FieldKindExcluded values
DstIpAddrcidr_match10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8
DstIpAddrcidr_match169.254.0.0/16, 127.0.0.0/8

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
BeaconPercentgt
  • 80 transforms: cased corpus 3 (kusto 3)
SrcIpAddreq
  • nextSrcIpAddr transforms: cased
TimeDeltainSecondsgt
  • 10 transforms: cased
TotalEventsgt
  • 15 transforms: cased

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
DstIpAddrsummarize
DstPortNumbersummarize
MostFrequentTimeDeltaCountsummarize
SrcIpAddrsummarize
TotalDstBytessummarize
TotalEventssummarize
TotalSrcBytessummarize
BeaconPercentextend