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