AWS S3 Access IP Allowlist


Description

Checks that the remote IP accessing the S3 bucket is in the IP allowlist.

Query · python

from ipaddress import IPv4Network, IPv6Network, ip_network

from panther_aws_helpers import aws_rule_context

BUCKETS_TO_MONITOR = {
    # Example bucket names to watch go here
}
ALLOWLIST_NETWORKS = {
    # IP addresses (in CIDR notation) indicating approved IP ranges for accessing S3 buckets}
    ip_network("10.0.0.0/8"),
}


def rule(event):
    if BUCKETS_TO_MONITOR:
        if event.get("bucket") not in BUCKETS_TO_MONITOR:
            return False

    if "remoteip" not in event:
        return False

    cidr_ip = ip_network(event.get("remoteip"))
    return not any(
        is_subnet(approved_ip_range, cidr_ip) for approved_ip_range in ALLOWLIST_NETWORKS
    )


def title(event):
    return f"Non-Approved IP access to S3 Bucket [{event.get('bucket', '<UNKNOWN_BUCKET>')}]"


def alert_context(event):
    return aws_rule_context(event)


def is_subnet(supernet: IPv4Network | IPv6Network, subnet: IPv4Network | IPv6Network) -> bool:
    """Return true if 'subnet' is a subnet of 'supernet'"""
    # We can't do a classic subnet comparison between v4 and v6 networks, so we have to explictly
    #   check for version mismatch first
    if supernet.network_address.version != subnet.network_address.version:
        return False
    # Else, do the subnet calculation
    return subnet.subnet_of(supernet)

Analyst notes

Verify whether unapproved access of S3 objects occurred, and take appropriate steps to remediate damage (for example, informing related parties of unapproved access and potentially invalidating data that was accessed). Consider updating the access policies of the S3 bucket to prevent future unapproved access.

Raw source AWS S3 Access IP Allowlist · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: rule
Filename: aws_s3_access_ip_allowlist.py
RuleID: "AWS.S3.ServerAccess.IPWhitelist"
DisplayName: "AWS S3 Access IP Allowlist"
DedupPeriodMinutes: 60 # 1 hour
Enabled: false
LogTypes:
  - AWS.S3ServerAccess
Tags:
  - AWS
  - Configuration Required
  - Identity & Access Management
  - Collection:Data From Cloud Storage Object
Reports:
  MITRE ATT&CK:
    - TA0009:T1530
Severity: Medium
Description: >
  Checks that the remote IP accessing the S3 bucket is in the IP allowlist.
Runbook: >
  Verify whether unapproved access of S3 objects occurred, and take appropriate steps to remediate damage (for example, informing related parties of unapproved access and potentially invalidating data that was accessed). Consider updating the access policies of the S3 bucket to prevent future unapproved access.
Reference: https://aws.amazon.com/premiumsupport/knowledge-center/block-s3-traffic-vpc-ip/
SummaryAttributes:
  - bucket
  - key
  - remoteip
Tests:
  - Name: Access From Approved IP
    ExpectedResult: false
    Log: { "remoteip": "10.0.0.1", "bucket": "my-test-bucket" }
  - Name: Access From Unapproved IP
    ExpectedResult: true
    Log: { "remoteip": "11.0.0.1", "bucket": "my-test-bucket" }
  - Name: Access From IPv6
    ExpectedResult: true
    Log: { "remoteip": "2600:1ffe:8140::a47:a85a", "bucket": "my-test-bucket" }


# ------ paired body: aws_s3_access_ip_allowlist.py ------

from ipaddress import IPv4Network, IPv6Network, ip_network

from panther_aws_helpers import aws_rule_context

BUCKETS_TO_MONITOR = {
    # Example bucket names to watch go here
}
ALLOWLIST_NETWORKS = {
    # IP addresses (in CIDR notation) indicating approved IP ranges for accessing S3 buckets}
    ip_network("10.0.0.0/8"),
}


def rule(event):
    if BUCKETS_TO_MONITOR:
        if event.get("bucket") not in BUCKETS_TO_MONITOR:
            return False

    if "remoteip" not in event:
        return False

    cidr_ip = ip_network(event.get("remoteip"))
    return not any(
        is_subnet(approved_ip_range, cidr_ip) for approved_ip_range in ALLOWLIST_NETWORKS
    )


def title(event):
    return f"Non-Approved IP access to S3 Bucket [{event.get('bucket', '<UNKNOWN_BUCKET>')}]"


def alert_context(event):
    return aws_rule_context(event)


def is_subnet(supernet: IPv4Network | IPv6Network, subnet: IPv4Network | IPv6Network) -> bool:
    """Return true if 'subnet' is a subnet of 'supernet'"""
    # We can't do a classic subnet comparison between v4 and v6 networks, so we have to explictly
    #   check for version mismatch first
    if supernet.network_address.version != subnet.network_address.version:
        return False
    # Else, do the subnet calculation
    return subnet.subnet_of(supernet)

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.