Detection rules › Panther
AWS Network ACL Restricts Insecure Protocols
This policy validates that Network ACLs block the usage of ports typically associated with insecure or unencrypted protocols.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Command & Control | T1095 Non-Application Layer Protocol |
Rule body yaml
AnalysisType: policy
Filename: aws_network_acl_restricts_insecure_protocols.py
PolicyID: "AWS.NetworkACL.RestrictsInsecureProtocols"
DisplayName: "AWS Network ACL Restricts Insecure Protocols"
Enabled: false
ResourceTypes:
- AWS.EC2.NetworkACL
Tags:
- AWS
- PCI
- Command and Control:Non-Application Layer Protocol
Reports:
PCI:
- 8.2.1
MITRE ATT&CK:
- TA0011:T1095
Severity: Low
Description: >
This policy validates that Network ACLs block the usage of ports typically associated with insecure or unencrypted protocols.
Runbook: Add Network ACL entries to block ports typically associated with insecure protocols.
Reference: https://docs.aws.amazon.com/vpc/latest/userguide/vpc-recommended-nacl-rules.html
Tests:
- Name: Network ACL Restricts Insecure Protocols
ExpectedResult: true
Resource:
{
"AccountId": "123456789012",
"Region": "ap-southeast-2",
"ARN": "arn:aws:ec2:ap-southeast-2:123456789012:network-acl/acl-111222333",
"ID": "acl-111222333",
"Tags": { "environment": "pci" },
"ResourceID": "arn:aws:ec2:ap-southeast-2:123456789012:network-acl/acl-111222333",
"ResourceType": "AWS.EC2.NetworkACL",
"TimeCreated": null,
"Associations":
[
{
"NetworkAclAssociationId": "aclassoc-111222333",
"NetworkAclId": "acl-111222333",
"SubnetId": "subnet-111222333",
},
],
"Entries":
[
{
"CidrBlock": "8.8.8.8/32",
"Egress": false,
"IcmpTypeCode": null,
"Ipv6CidrBlock": null,
"PortRange": { "From": 22, "To": 22 },
"Protocol": "6",
"RuleAction": "allow",
"RuleNumber": 100,
},
{
"CidrBlock": "0.0.0.0/0",
"Egress": true,
"IcmpTypeCode": null,
"Ipv6CidrBlock": null,
"PortRange": { "From": 22, "To": 22 },
"Protocol": "6",
"RuleAction": "allow",
"RuleNumber": 100,
},
],
"IsDefault": true,
"NetworkAclId": "acl-111222333",
"OwnerId": "123456789012",
"VpcId": "vpc-6aa60b12",
}
- Name: Insecure Ports Not Restricted
ExpectedResult: false
Resource:
{
"AccountId": "123456789012",
"Region": "ap-southeast-2",
"ARN": "arn:aws:ec2:ap-southeast-2:123456789012:network-acl/acl-111222333",
"ID": "acl-111222333",
"Tags": { "environment": "pci" },
"ResourceID": "arn:aws:ec2:ap-southeast-2:123456789012:network-acl/acl-111222333",
"ResourceType": "AWS.EC2.NetworkACL",
"TimeCreated": null,
"Associations":
[
{
"NetworkAclAssociationId": "aclassoc-111222333",
"NetworkAclId": "acl-111222333",
"SubnetId": "subnet-111222333",
},
],
"Entries":
[
{
"CidrBlock": "0.0.0.0/0",
"Egress": false,
"IcmpTypeCode": null,
"Ipv6CidrBlock": null,
"PortRange": { "From": 21, "To": 21 },
"Protocol": "-1",
"RuleAction": "allow",
"RuleNumber": 100,
},
],
"IsDefault": true,
"NetworkAclId": "acl-111222333",
"OwnerId": "123456789012",
"VpcId": "vpc-6aa60b12",
}
- Name: All Ports Allowed
ExpectedResult: false
Resource:
{
"OwnerId": "123456789012",
"Region": "us-west-2",
"Associations":
[
{
"NetworkAclId": "acl-1",
"NetworkAclAssociationId": "aclassoc-1",
"SubnetId": "subnet-1",
},
],
"Id": "acl-1",
"Entries":
[
{
"CidrBlock": "0.0.0.0/0",
"Egress": false,
"RuleNumber": 100,
"Ipv6CidrBlock": null,
"Protocol": "-1",
"PortRange": null,
"IcmpTypeCode": null,
"RuleAction": "allow",
},
{
"RuleNumber": 32767,
"Ipv6CidrBlock": null,
"Protocol": "-1",
"PortRange": null,
"IcmpTypeCode": null,
"RuleAction": "deny",
"CidrBlock": "0.0.0.0/0",
"Egress": false,
},
],
"VpcId": "vpc-1",
"IsDefault": true,
"Tags": { "environment": "pci" },
"AccountId": "123456789012",
"ResourceType": "AWS.EC2.NetworkACL",
"ResourceId": "arn:aws:ec2:us-west-2:123456789012:network-acl/acl-1",
"Arn": "arn:aws:ec2:us-west-2:123456789012:network-acl/acl-1",
"TimeCreated": null,
}
Detection logic
Rule logic imperative Python
INSECURE_PORTS = {
21,
25,
23,
80,
110,
587,
}
def policy(resource):
for entry in resource["Entries"]:
if entry["Egress"]:
continue
if entry["Protocol"] == "-1" or not entry["PortRange"]:
return False
if any(
entry["PortRange"]["From"] <= port <= entry["PortRange"]["To"]
for port in INSECURE_PORTS
):
return False
return True
The parser cannot express this rule's logic as a field filter; the imperative Python above is the detection.