AWS Security Group - Only DMZ Publicly Accessible


Description

This policy validates that only Security Groups designated as DMZs allow inbound traffic from public IP space. This helps ensure no traffic is bypassing the DMZ.

Query · python

import json
from ipaddress import ip_network
from unittest.mock import MagicMock

# NOTE: Make sure to adjust DMZ_TAGS

DMZ_TAGS = [
    # ["environment", "dmz"]
]


# Defaults to False to assume something is not a DMZ if it is not tagged
def is_dmz_tags(resource, dmz_tags):
    """This function determines whether a given resource is tagged as existing in a DMZ."""
    if resource["Tags"] is None:
        return False
    for key, value in dmz_tags:
        if resource["Tags"].get(key) == value:
            return True
    return False


def policy(resource):
    # If this security group allows no inbound connections, it is secure
    if resource["IpPermissions"] is None:
        return True

    # DMZ security groups can have inbound permissions from the internet
    global DMZ_TAGS  # pylint: disable=global-statement
    if isinstance(DMZ_TAGS, MagicMock):
        DMZ_TAGS = {tuple(kv) for kv in json.loads(DMZ_TAGS())}  # pylint: disable=not-callable
    if is_dmz_tags(resource, DMZ_TAGS):
        return True

    for permission in resource["IpPermissions"]:
        # Check if any traffic is allowed from public IP space
        for ip_range in permission["IpRanges"] or []:
            if ip_range["CidrIp"] == "0.0.0.0/0" or not ip_network(ip_range["CidrIp"]).is_private:
                return False
        for ip_range in permission["Ipv6Ranges"] or []:
            if ip_range["CidrIpv6"] == "::/0" or not ip_network(ip_range["CidrIpv6"]).is_private:
                return False

    return True

Analyst notes

Remove the IP permissions in non-DMZ Security Groups that are allowing inbound traffic from public IP space.

Raw source AWS Security Group - Only DMZ Publicly Accessible · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: policy
Filename: aws_only_dmz_security_groups_publicly_accessible.py
PolicyID: "AWS.SecurityGroup.OnlyDMZPubliclyAccessible"
DisplayName: "AWS Security Group - Only DMZ Publicly Accessible"
Enabled: false
ResourceTypes:
  - AWS.EC2.SecurityGroup
Tags:
  - AWS
  - PCI
  - Initial Access:Exploit Public-Facing Application
Reports:
  PCI:
    - 1.3.1
    - 1.1.4
  MITRE ATT&CK:
    - TA0001:T1190
Severity: Medium
Description: >
  This policy validates that only Security Groups designated as DMZs allow inbound traffic from public IP space. This helps ensure no traffic is bypassing the DMZ.
Runbook: >
  Remove the IP permissions in non-DMZ Security Groups that are allowing inbound traffic from public IP space.
Reference: https://en.wikipedia.org/wiki/DMZ_(computing)
Tests:
  - Name: DMZ Security Group Does Allows Public Access
    ExpectedResult: true
    Mocks:
      - objectName: DMZ_TAGS
        returnValue: '[["environment", "dmz"]]'
    Resource:
      {
        "Description": "example VPC security group",
        "GroupId": "sg-abc123",
        "GroupName": "default",
        "IpPermissions":
          [
            {
              "FromPort": null,
              "IpProtocol": "-1",
              "IpRanges": null,
              "Ipv6Ranges": null,
              "PrefixListIds": null,
              "ToPort": null,
              "UserIdGroupPairs":
                [
                  {
                    "Description": null,
                    "GroupId": "sg-def123",
                    "GroupName": null,
                    "PeeringStatus": null,
                    "UserId": "123456789012",
                    "VpcId": null,
                    "VpcPeeringConnectionId": null,
                  },
                ],
            },
            {
              "FromPort": null,
              "IpProtocol": "-1",
              "IpRanges": [{ "CidrIp": "1.0.0.0/24", "Description": null }],
              "Ipv6Ranges": null,
              "PrefixListIds": null,
              "ToPort": null,
              "UserIdGroupPairs": null,
            },
          ],
        "IpPermissionsEgress":
          [
            {
              "FromPort": null,
              "IpProtocol": "-1",
              "IpRanges": [{ "CidrIp": "0.0.0.0/0", "Description": null }],
              "Ipv6Ranges": null,
              "PrefixListIds": null,
              "ToPort": null,
              "UserIdGroupPairs": null,
            },
          ],
        "OwnerId": "123456789012",
        "Tags": { "environment": "dmz" },
        "VpcId": "vpc-abc111222333",
      }
  - Name: Non DMZ Security Group Allows Public Access
    ExpectedResult: false
    Mocks:
      - objectName: DMZ_TAGS
        returnValue: '[["environment", "dmz"]]'
    Resource:
      {
        "Description": "example VPC security group",
        "GroupId": "sg-abc123",
        "GroupName": "default",
        "IpPermissions":
          [
            {
              "FromPort": null,
              "IpProtocol": "-1",
              "IpRanges": null,
              "Ipv6Ranges": null,
              "PrefixListIds": null,
              "ToPort": null,
              "UserIdGroupPairs":
                [
                  {
                    "Description": null,
                    "GroupId": "sg-def123",
                    "GroupName": null,
                    "PeeringStatus": null,
                    "UserId": "123456789012",
                    "VpcId": null,
                    "VpcPeeringConnectionId": null,
                  },
                ],
            },
            {
              "FromPort": null,
              "IpProtocol": "-1",
              "IpRanges": [{ "CidrIp": "1.1.1.1/32", "Description": null }],
              "Ipv6Ranges": null,
              "PrefixListIds": null,
              "ToPort": null,
              "UserIdGroupPairs": null,
            },
          ],
        "IpPermissionsEgress":
          [
            {
              "FromPort": null,
              "IpProtocol": "-1",
              "IpRanges": [{ "CidrIp": "192.0.2.0/24", "Description": null }],
              "Ipv6Ranges": null,
              "PrefixListIds": null,
              "ToPort": null,
              "UserIdGroupPairs": null,
            },
          ],
        "OwnerId": "123456789012",
        "Tags": { "environment": "pci" },
        "VpcId": "vpc-abc111222333",
      }
  - Name: Non DMZ Security Group Does Not Allow Public Access
    ExpectedResult: true
    Mocks:
      - objectName: DMZ_TAGS
        returnValue: '[["environment", "dmz"]]'
    Resource:
      {
        "Description": "example VPC security group",
        "GroupId": "sg-abc123",
        "GroupName": "default",
        "IpPermissions":
          [
            {
              "FromPort": null,
              "IpProtocol": "-1",
              "IpRanges": null,
              "Ipv6Ranges": null,
              "PrefixListIds": null,
              "ToPort": null,
              "UserIdGroupPairs":
                [
                  {
                    "Description": null,
                    "GroupId": "sg-def123",
                    "GroupName": null,
                    "PeeringStatus": null,
                    "UserId": "123456789012",
                    "VpcId": null,
                    "VpcPeeringConnectionId": null,
                  },
                ],
            },
            {
              "FromPort": null,
              "IpProtocol": "-1",
              "IpRanges": [{ "CidrIp": "192.168.0.0/24", "Description": null }],
              "Ipv6Ranges": null,
              "PrefixListIds": null,
              "ToPort": null,
              "UserIdGroupPairs": null,
            },
          ],
        "IpPermissionsEgress":
          [
            {
              "FromPort": null,
              "IpProtocol": "-1",
              "IpRanges": [{ "CidrIp": "192.0.2.0/24", "Description": null }],
              "Ipv6Ranges": null,
              "PrefixListIds": null,
              "ToPort": null,
              "UserIdGroupPairs": null,
            },
          ],
        "OwnerId": "123456789012",
        "Tags": { "environment": "pci" },
        "VpcId": "vpc-abc111222333",
      }


# ------ paired body: aws_only_dmz_security_groups_publicly_accessible.py ------

import json
from ipaddress import ip_network
from unittest.mock import MagicMock

# NOTE: Make sure to adjust DMZ_TAGS

DMZ_TAGS = [
    # ["environment", "dmz"]
]


# Defaults to False to assume something is not a DMZ if it is not tagged
def is_dmz_tags(resource, dmz_tags):
    """This function determines whether a given resource is tagged as existing in a DMZ."""
    if resource["Tags"] is None:
        return False
    for key, value in dmz_tags:
        if resource["Tags"].get(key) == value:
            return True
    return False


def policy(resource):
    # If this security group allows no inbound connections, it is secure
    if resource["IpPermissions"] is None:
        return True

    # DMZ security groups can have inbound permissions from the internet
    global DMZ_TAGS  # pylint: disable=global-statement
    if isinstance(DMZ_TAGS, MagicMock):
        DMZ_TAGS = {tuple(kv) for kv in json.loads(DMZ_TAGS())}  # pylint: disable=not-callable
    if is_dmz_tags(resource, DMZ_TAGS):
        return True

    for permission in resource["IpPermissions"]:
        # Check if any traffic is allowed from public IP space
        for ip_range in permission["IpRanges"] or []:
            if ip_range["CidrIp"] == "0.0.0.0/0" or not ip_network(ip_range["CidrIp"]).is_private:
                return False
        for ip_range in permission["Ipv6Ranges"] or []:
            if ip_range["CidrIpv6"] == "::/0" or not ip_network(ip_range["CidrIpv6"]).is_private:
                return False

    return True

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.