Detection rules › Panther

VPC Flow Port Scanning

Status
Experimental
Severity
medium
Group by
dstAddr, region, srcAddr, subNetId, vpcId
Log types
AWS.VPCFlow
Tags
Discovery:Network Service Discovery
Source
github.com/panther-labs/panther-analysis

Detects potential port scanning activity by alerting when a single source address communicates with 10 or more distinct destination ports on the same target within 60 minutes. Common ports (80, 443, 53, etc.) are excluded to reduce noise.

MITRE ATT&CK coverage

TacticTechniques
DiscoveryT1046 Network Service Discovery

Rule body yaml

AnalysisType: rule
Filename: aws_vpc_port_scanning.py
RuleID: "AWS.VPC.PortScanning"
DisplayName: "VPC Flow Port Scanning"
Status: Experimental
Enabled: false
Severity: Medium
DedupPeriodMinutes: 60
Threshold: 10
LogTypes:
  - AWS.VPCFlow
Description: >
  Detects potential port scanning activity by alerting when a single source address
  communicates with 10 or more distinct destination ports on the same target within 60 minutes.
  Common ports (80, 443, 53, etc.) are excluded to reduce noise.
Reports:
  MITRE ATT&CK:
    - TA0007:T1046
Tags:
  - Discovery:Network Service Discovery
Runbook: |
  1. Query VPC Flow logs for all egress traffic from srcAddr in the 1 hour around this alert to identify the full sequence of dstPort values targeted on dstAddr within vpcId
  2. Check if srcAddr is associated with known vulnerability scanners, corporate IT tooling, or threat intelligence feeds
  3. Find other alerts involving srcAddr or vpcId in the past 7 days to determine if this is part of ongoing reconnaissance
Tests:
  - Name: Egress to Non-Common Port
    ExpectedResult: true
    Log:
      srcAddr: "10.0.0.1"
      dstAddr: "192.168.1.1"
      srcPort: 54321
      dstPort: 8080
      flowDirection: egress
      vpcId: vpc-12345678
      region: us-east-1
      subNetId: subnet-12345678
  - Name: Ingress Flow
    ExpectedResult: false
    Log:
      srcAddr: "10.0.0.1"
      dstAddr: "192.168.1.1"
      srcPort: 54321
      dstPort: 8080
      flowDirection: ingress
      vpcId: vpc-12345678
      region: us-east-1
      subNetId: subnet-12345678
  - Name: Egress to Common HTTPS Port
    ExpectedResult: false
    Log:
      srcAddr: "10.0.0.1"
      dstAddr: "192.168.1.1"
      srcPort: 54321
      dstPort: 443
      flowDirection: egress
      vpcId: vpc-12345678
      region: us-east-1
      subNetId: subnet-12345678
  - Name: Null Source Address
    ExpectedResult: false
    Log:
      srcAddr: "null"
      dstAddr: "192.168.1.1"
      srcPort: 54321
      dstPort: 8080
      flowDirection: egress
      vpcId: vpc-12345678
      region: us-east-1
      subNetId: subnet-12345678
  - Name: Egress from External IP - Default Severity
    ExpectedResult: true
    Log:
      srcAddr: "8.8.8.8"
      dstAddr: "10.0.0.1"
      srcPort: 54321
      dstPort: 8080
      flowDirection: egress
      vpcId: vpc-12345678
      region: us-east-1
      subNetId: subnet-12345678
  - Name: Egress from Internal IP - High Severity
    ExpectedResult: true
    Log:
      srcAddr: "172.16.0.5"
      dstAddr: "10.0.0.1"
      srcPort: 54321
      dstPort: 8080
      flowDirection: egress
      vpcId: vpc-12345678
      region: us-east-1
      subNetId: subnet-12345678

Detection logic

Condition

flowDirection eq "egress"
not (srcAddr is_null or srcAddr eq "null")
dstPort not in ["80", "123", "443", "445", "53", "853", "2049"]

Exclusions

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

FieldKindExcluded values
srcAddreqnull
srcAddris_null(no value, null check)
dstPortin123, 2049, 443, 445, 53, 80, 853

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
flowDirectioneq
  • egress

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.

Field
srcAddr
dstAddr