Detection rules › Kusto

Palo Alto - possible internal to external port scanning

Status
available
Severity
low
Time window
1h
Group by
ApplicationProtocol, DestinationIP, DestinationPort, DeviceAction, DeviceName, DeviceProduct, DeviceVendor, Protocol, Reason, SourceIP, SourceUserID
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 "ApplicationProtocol = incomplete" designation. The server resets coupled with an "Incomplete" ApplicationProtocol designation can be an indication of internal to external port scanning or probing attack. References: https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClUvCAK and https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClTaCAK'

MITRE ATT&CK coverage

TacticTechniques
DiscoveryT1046 Network Service Discovery

Rule body kusto

id: 5b72f527-e3f6-4a00-9908-8e4fee14da9f
name: Palo Alto - 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 "ApplicationProtocol = incomplete" designation. The server resets coupled with an "Incomplete" ApplicationProtocol designation can be an indication of internal to external port scanning or probing attack.
  References: https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClUvCAK and
  https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClTaCAK'
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
relevantTechniques:
  - T1046
query: |
  CommonSecurityLog
  | where isnotempty(DestinationPort) and DeviceAction !in ("reset-both", "deny")
  // filter out common usage ports. Add ports that are legitimate for your environment
  | where DestinationPort !in ("443", "53", "389", "80", "0", "880", "8888", "8080")
  | where ApplicationProtocol == "incomplete"
  // filter out IANA ephemeral or negotiated ports as per https://en.wikipedia.org/wiki/Ephemeral_port
  | where DestinationPort !between (toint(49512) .. toint(65535))
  | where Computer != ""
  | where ipv4_is_private(DestinationIP) == false
  | extend Reason = coalesce(
                                column_ifexists("Reason", ""),
                                extract("reason=(.+?)(;|$)", 1, AdditionalExtensions),
                                ""
                            )
  // Filter out any graceful reset reasons of AGED OUT which occurs when a TCP session closes with a FIN due to aging out.
  | where Reason !has "aged-out"
  // Filter out any TCP FIN which occurs when a TCP FIN is used to gracefully close half or both sides of a connection.
  | where Reason !has "tcp-fin"
  // Uncomment one of the following where clauses to trigger on specific TCP reset reasons
  // See Palo Alto article for details - https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA10g000000ClUvCAK
  // TCP RST-server - Occurs when the server sends a TCP reset to the client
  // | where AdditionalExtensions has "reason=tcp-rst-from-server"
  // TCP RST-client - Occurs when the client sends a TCP reset to the server
  // | where AdditionalExtensions has "reason=tcp-rst-from-client"
  // Already performed
  //| extend reason = tostring(split(AdditionalExtensions, ";")[3])
  | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), count() by DeviceName, SourceUserID, SourceIP, ApplicationProtocol, Reason, DestinationPort, Protocol, DeviceVendor, DeviceProduct, DeviceAction, DestinationIP
  | where count_ >= 10
  | summarize StartTimeUtc = min(StartTimeUtc), EndTimeUtc = max(EndTimeUtc), makeset(DestinationIP), totalcount = sum(count_) by DeviceName, SourceUserID, SourceIP, ApplicationProtocol, Reason, DestinationPort, Protocol, DeviceVendor, DeviceProduct, DeviceAction
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: SourceUserID
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: DeviceName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIP
version: 1.0.8
kind: Scheduled

Stages and Predicates

Stage 1: source

CommonSecurityLog

Stage 2: where

| where isnotempty(DestinationPort) and DeviceAction !in ("reset-both", "deny")

Stage 3: where

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

Stage 4: where

| where ApplicationProtocol == "incomplete"

Stage 5: where

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

Stage 6: where

| where Computer != ""

Stage 7: where

| where ipv4_is_private(DestinationIP) == false

Stage 8: extend

| extend Reason = coalesce(
                              column_ifexists("Reason", ""),
                              extract("reason=(.+?)(;|$)", 1, AdditionalExtensions),
                              ""
                          )

Stage 9: where

| where Reason !has "aged-out"

Stage 10: where

| where Reason !has "tcp-fin"

Stage 11: summarize

| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), count() by DeviceName, SourceUserID, SourceIP, ApplicationProtocol, Reason, DestinationPort, Protocol, DeviceVendor, DeviceProduct, DeviceAction, DestinationIP

Stage 12: where

| where count_ >= 10

Stage 13: summarize

| summarize StartTimeUtc = min(StartTimeUtc), EndTimeUtc = max(EndTimeUtc), makeset(DestinationIP), totalcount = sum(count_) by DeviceName, SourceUserID, SourceIP, ApplicationProtocol, Reason, DestinationPort, Protocol, DeviceVendor, DeviceProduct, DeviceAction

Exclusions

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

FieldKindExcluded values
DeviceActionindeny, reset-both
DestinationPortin0, 389, 443, 53, 80, 8080, 880, 8888
DestinationPortge49512
DestinationPortle65535
DestinationIPcidr_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
Reasonmatchaged-out
Reasonmatchtcp-fin

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
ApplicationProtocoleq
  • incomplete transforms: cased
DestinationPortis_not_null
  • (no value, null check)
count_ge
  • 10 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
ApplicationProtocolsummarize
DestinationPortsummarize
DeviceActionsummarize
DeviceNamesummarize
DeviceProductsummarize
DeviceVendorsummarize
EndTimeUtcsummarize
Protocolsummarize
Reasonsummarize
SourceIPsummarize
SourceUserIDsummarize
StartTimeUtcsummarize
totalcountsummarize