Detection rules › Kusto

CloudNGFW By Palo Alto Networks - possible internal to external port scanning

Status
available
Severity
low
Time window
1h
Group by
Action, Application, DestinationIP, DestinationPort, FirewallName_s, Protocol, Reason, SourceIP, ident_s
Source
github.com/Azure/Azure-Sentinel

'Identifies a list of internal Source IPs (10.x.x.x Hosts) that have triggered 10 or more non-graceful tcp server resets from one or more Destination IPs which results in an "app = incomplete" designation. The server resets coupled with an "Incomplete" app designation can be an indication of internal to external port scanning or probing attack. References: https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClUvCAK https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClTaCAK'

MITRE ATT&CK coverage

TacticTechniques
Discovery

Rule body

id: 5b72f527-e3f6-4a00-9908-8e4fee14da9f
name: CloudNGFW By Palo Alto Networks - possible internal to external port scanning
description: |
  'Identifies a list of internal Source IPs (10.x.x.x Hosts) that have triggered 10 or more non-graceful tcp server resets from one or more Destination IPs which results in an "app = incomplete" designation. The server resets coupled with an "Incomplete" app designation can be an indication of internal to external port scanning or probing attack.
  References: https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClUvCAK
  https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClTaCAK'
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: AzureCloudNGFWByPaloAltoNetworks
    dataTypes:
      - fluentbit_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
relevantTechniques:
  - T1046
query: |
  fluentbit_CL
  | where ident_s == "TRAFFIC"
  | where isnotempty(FirewallName_s)
  | extend message = parse_json(Message)
  | extend DestinationPort = tostring(message.dport)
  | extend SourceIP = tostring(message.src_ip)
  | extend DestinationIP = tostring(message.dst_ip)
  | extend Application = tostring(message.app)
  | extend Protocol = tostring(message.proto)
  | extend Action = tostring(message.action)
  | where isnotempty(DestinationPort) and message.action !in ("reset-both", "deny")
  | where DestinationPort !in ("443", "53", "389", "80", "0", "880", "8888", "8080")
  | where message.app == "incomplete"
  | where toint(DestinationPort) !between (49512 .. 65535)
  | where message.dst_ip !startswith "10."
  | extend Reason = coalesce(column_ifexists("Reason", ""), tostring(message.session_end_reason), "")
  | where Reason !has "aged-out"
  | where Reason !has "tcp-fin"
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by FirewallName_s, SourceIP, Application, Reason, DestinationPort, Protocol, ident_s, Action, DestinationIP
  | where count_ >= 10
  | summarize StartTime = min(StartTime), EndTime = max(EndTime), makeset(DestinationIP), totalcount = sum(count_) by FirewallName_s, SourceIP, Application, Reason, DestinationPort, Protocol, ident_s, Action
  | extend IPAddress = SourceIP
  | extend HostName = tostring(split(FirewallName_s, ".")[0]), DomainIndex = toint(indexof(FirewallName_s, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(FirewallName_s, DomainIndex + 1), FirewallName_s)
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: FirewallName_s
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: HostNameDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
version: 1.0.6
kind: Scheduled

Stages and Predicates

Stage 1: source

fluentbit_CL

Stage 2: where

| where ident_s == "TRAFFIC"

Stage 3: where

| where isnotempty(FirewallName_s)

Stage 4: extend (7 consecutive steps)

| extend message = parse_json(Message)
| extend DestinationPort = tostring(message.dport)
| extend SourceIP = tostring(message.src_ip)
| extend DestinationIP = tostring(message.dst_ip)
| extend Application = tostring(message.app)
| extend Protocol = tostring(message.proto)
| extend Action = tostring(message.action)

Stage 5: where

| where isnotempty(DestinationPort) and message.action !in ("reset-both", "deny")

Stage 6: where

| where DestinationPort !in ("443", "53", "389", "80", "0", "880", "8888", "8080")

Stage 7: where

| where message.app == "incomplete"

Stage 8: where

| where toint(DestinationPort) !between (49512 .. 65535)

Stage 9: where

| where message.dst_ip !startswith "10."

Stage 10: extend

| extend Reason = coalesce(column_ifexists("Reason", ""), tostring(message.session_end_reason), "")

Stage 11: where

| where Reason !has "aged-out"

Stage 12: where

| where Reason !has "tcp-fin"

Stage 13: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by FirewallName_s, SourceIP, Application, Reason, DestinationPort, Protocol, ident_s, Action, DestinationIP

Stage 14: where

| where count_ >= 10

Stage 15: summarize

| summarize StartTime = min(StartTime), EndTime = max(EndTime), makeset(DestinationIP), totalcount = sum(count_) by FirewallName_s, SourceIP, Application, Reason, DestinationPort, Protocol, ident_s, Action

Stage 16: extend (3 consecutive steps)

| extend IPAddress = SourceIP
| extend HostName = tostring(split(FirewallName_s, ".")[0]), DomainIndex = toint(indexof(FirewallName_s, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(FirewallName_s, DomainIndex + 1), FirewallName_s)

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
actionindeny, reset-bothexcludes:action field:"action" value:"deny" field:"action" value:"reset-both"
DestinationPortin0, 389, 443, 53, 80, 8080, 880, 8888excludes:DestinationPort
dst_ipstarts_with10.excludes:dst_ip field:"dst_ip" value:"10."
Reasonmatchaged-outexcludes:Reason field:"Reason" value:"aged-out"
Reasonmatchtcp-finexcludes:Reason field:"Reason" value:"tcp-fin"

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
Actionsummarize
Applicationsummarize
DestinationPortsummarize
EndTimesummarize
FirewallName_ssummarize
Protocolsummarize
Reasonsummarize
SourceIPsummarize
StartTimesummarize
ident_ssummarize
totalcountsummarize
IPAddressextend
DomainIndexextend
HostNameextend
HostNameDomainextend