From 6e595eaf92c04a150ff00f6ac49625f7cc114db6 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:22:56 +0100 Subject: [PATCH] feat(ec2): add new fixer `ec2_instance_port_cifs_exposed_to_internet_fixer` (#6159) --- ...nce_port_cifs_exposed_to_internet_fixer.py | 51 +++ ...ort_cifs_exposed_to_internet_fixer_test.py | 430 ++++++++++++++++++ 2 files changed, 481 insertions(+) create mode 100644 prowler/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet_fixer.py create mode 100644 tests/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet_fixer_test.py diff --git a/prowler/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet_fixer.py b/prowler/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet_fixer.py new file mode 100644 index 0000000000..f898b8e5f3 --- /dev/null +++ b/prowler/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet_fixer.py @@ -0,0 +1,51 @@ +from prowler.lib.logger import logger +from prowler.providers.aws.services.ec2.ec2_client import ec2_client +from prowler.providers.aws.services.ec2.lib.security_groups import check_security_group + + +def fixer(resource_id: str, region: str) -> bool: + """ + Revokes any ingress rule allowing CIFS ports (139, 445) from any address (0.0.0.0/0) + for the EC2 instance's security groups. + This fixer will only be triggered if the check identifies CIFS ports open to the Internet. + Requires the ec2:RevokeSecurityGroupIngress permission. + Permissions: + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "ec2:RevokeSecurityGroupIngress", + "Resource": "*" + } + ] + } + Args: + resource_id (str): The EC2 instance ID. + region (str): The AWS region where the EC2 instance exists. + Returns: + bool: True if the operation is successful (ingress rule revoked), False otherwise. + """ + try: + regional_client = ec2_client.regional_clients[region] + check_ports = [139, 445] + for instance in ec2_client.instances: + if instance.id == resource_id: + for sg in ec2_client.security_groups.values(): + if sg.id in instance.security_groups: + for ingress_rule in sg.ingress_rules: + if check_security_group( + ingress_rule, "tcp", check_ports, any_address=True + ): + regional_client.revoke_security_group_ingress( + GroupId=sg.id, + IpPermissions=[ingress_rule], + ) + + except Exception as error: + logger.error( + f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return False + else: + return True diff --git a/tests/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet_fixer_test.py b/tests/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet_fixer_test.py new file mode 100644 index 0000000000..ef7771637e --- /dev/null +++ b/tests/providers/aws/services/ec2/ec2_instance_port_cifs_exposed_to_internet/ec2_instance_port_cifs_exposed_to_internet_fixer_test.py @@ -0,0 +1,430 @@ +from unittest import mock + +import botocore +import botocore.client +from boto3 import client, resource +from moto import mock_aws + +from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider + +mock_make_api_call = botocore.client.BaseClient._make_api_call + + +def mock_make_api_call_error(self, operation_name, kwarg): + if operation_name == "RevokeSecurityGroupIngress": + raise botocore.exceptions.ClientError( + { + "Error": { + "Code": "InvalidPermission.NotFound", + "Message": "The specified rule does not exist in this security group.", + } + }, + operation_name, + ) + return mock_make_api_call(self, operation_name, kwarg) + + +class Test_ec2_instance_port_cifs_exposed_to_internet_fixer: + @mock_aws + def test_ec2_instance_exposed_port_in_private_subnet_with_ip4_and_ip6(self): + # Create EC2 Mocked Resources + ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) + ec2_resource = resource("ec2", region_name=AWS_REGION_EU_WEST_1) + vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"] + default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[ + "SecurityGroups" + ][0] + default_sg_id = default_sg["GroupId"] + ec2_client.authorize_security_group_ingress( + GroupId=default_sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": 1, + "ToPort": 1000, + "IpRanges": [{"CidrIp": "0.0.0.0/0"}, {"CidrIp": "10.0.0.0/24"}], + "Ipv6Ranges": [{"CidrIpv6": "::/0"}, {"CidrIpv6": "2001:db8::/32"}], + }, + { + "IpProtocol": "tcp", + "FromPort": 445, + "ToPort": 445, + "IpRanges": [{"CidrIp": "0.0.0.0/0"}, {"CidrIp": "10.0.0.0/24"}], + "Ipv6Ranges": [{"CidrIpv6": "::/0"}, {"CidrIpv6": "2001:db8::/32"}], + }, + ], + ) + subnet_id = ec2_client.create_subnet(VpcId=vpc_id, CidrBlock="10.0.0.0/16")[ + "Subnet" + ]["SubnetId"] + instance_id = ec2_resource.create_instances( + ImageId="ami-12345678", + MinCount=1, + MaxCount=1, + InstanceType="t2.micro", + SecurityGroupIds=[default_sg_id], + SubnetId=subnet_id, + TagSpecifications=[ + {"ResourceType": "instance", "Tags": [{"Key": "Name", "Value": "test"}]} + ], + )[0].id + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer.ec2_client", + new=EC2(aws_provider), + ): + # Test Fixer + from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import ( + fixer, + ) + + assert fixer(instance_id, AWS_REGION_EU_WEST_1) + + @mock_aws + def test_ec2_instance_exposed_port_error(self): + with mock.patch( + "botocore.client.BaseClient._make_api_call", new=mock_make_api_call_error + ): + # Create EC2 Mocked Resources + ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) + ec2_resource = resource("ec2", region_name=AWS_REGION_EU_WEST_1) + vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"] + default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[ + "SecurityGroups" + ][0] + default_sg_id = default_sg["GroupId"] + ec2_client.authorize_security_group_ingress( + GroupId=default_sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": 1, + "ToPort": 1000, + "IpRanges": [ + {"CidrIp": "0.0.0.0/0"}, + {"CidrIp": "10.0.0.0/24"}, + ], + "Ipv6Ranges": [ + {"CidrIpv6": "::/0"}, + {"CidrIpv6": "2001:db8::/32"}, + ], + }, + { + "IpProtocol": "tcp", + "FromPort": 445, + "ToPort": 445, + "IpRanges": [ + {"CidrIp": "0.0.0.0/0"}, + {"CidrIp": "10.0.0.0/24"}, + ], + "Ipv6Ranges": [ + {"CidrIpv6": "::/0"}, + {"CidrIpv6": "2001:db8::/32"}, + ], + }, + ], + ) + subnet_id = ec2_client.create_subnet(VpcId=vpc_id, CidrBlock="10.0.0.0/16")[ + "Subnet" + ]["SubnetId"] + instance_id = ec2_resource.create_instances( + ImageId="ami-12345678", + MinCount=1, + MaxCount=1, + InstanceType="t2.micro", + SecurityGroupIds=[default_sg_id], + SubnetId=subnet_id, + TagSpecifications=[ + { + "ResourceType": "instance", + "Tags": [{"Key": "Name", "Value": "test"}], + } + ], + )[0].id + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer.ec2_client", + new=EC2(aws_provider), + ): + # Test Fixer + from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import ( + fixer, + ) + + assert not fixer(instance_id, AWS_REGION_EU_WEST_1) + + @mock_aws + def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip4(self): + # Create EC2 Mocked Resources + ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) + ec2_resource = resource("ec2", region_name=AWS_REGION_EU_WEST_1) + vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"] + default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[ + "SecurityGroups" + ][0] + default_sg_id = default_sg["GroupId"] + ec2_client.authorize_security_group_ingress( + GroupId=default_sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": 139, + "ToPort": 139, + "IpRanges": [{"CidrIp": "0.0.0.0/0"}, {"CidrIp": "10.0.0.0/24"}], + }, + { + "IpProtocol": "tcp", + "FromPort": 445, + "ToPort": 445, + "IpRanges": [{"CidrIp": "0.0.0.0/0"}, {"CidrIp": "10.0.0.0/24"}], + }, + ], + ) + subnet_id = ec2_client.create_subnet(VpcId=vpc_id, CidrBlock="10.0.0.0/16")[ + "Subnet" + ]["SubnetId"] + instance_id = ec2_resource.create_instances( + ImageId="ami-12345678", + MinCount=1, + MaxCount=1, + InstanceType="t2.micro", + SecurityGroupIds=[default_sg_id], + SubnetId=subnet_id, + TagSpecifications=[ + {"ResourceType": "instance", "Tags": [{"Key": "Name", "Value": "test"}]} + ], + )[0].id + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer.ec2_client", + new=EC2(aws_provider), + ): + # Test Fixer + from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import ( + fixer, + ) + + assert fixer(instance_id, AWS_REGION_EU_WEST_1) + + @mock_aws + def test_ec2_instance_exposed_port_in_private_subnet_only_with_ip6(self): + # Create EC2 Mocked Resources + ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) + ec2_resource = resource("ec2", region_name=AWS_REGION_EU_WEST_1) + vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"] + default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[ + "SecurityGroups" + ][0] + default_sg_id = default_sg["GroupId"] + ec2_client.authorize_security_group_ingress( + GroupId=default_sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": 139, + "ToPort": 139, + "Ipv6Ranges": [{"CidrIpv6": "::/0"}, {"CidrIpv6": "2001:db8::/32"}], + }, + { + "IpProtocol": "tcp", + "FromPort": 445, + "ToPort": 445, + "Ipv6Ranges": [{"CidrIpv6": "::/0"}, {"CidrIpv6": "2001:db8::/32"}], + }, + ], + ) + subnet_id = ec2_client.create_subnet(VpcId=vpc_id, CidrBlock="10.0.0.0/16")[ + "Subnet" + ]["SubnetId"] + instance_id = ec2_resource.create_instances( + ImageId="ami-12345678", + MinCount=1, + MaxCount=1, + InstanceType="t2.micro", + SecurityGroupIds=[default_sg_id], + SubnetId=subnet_id, + TagSpecifications=[ + {"ResourceType": "instance", "Tags": [{"Key": "Name", "Value": "test"}]} + ], + )[0].id + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer.ec2_client", + new=EC2(aws_provider), + ): + # Test Fixer + from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import ( + fixer, + ) + + assert fixer(instance_id, AWS_REGION_EU_WEST_1) + + @mock_aws + def test_ec2_instance_exposed_port_in_public_subnet_only_139_port(self): + # Create EC2 Mocked Resources + ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) + ec2_resource = resource("ec2", region_name=AWS_REGION_EU_WEST_1) + vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"] + default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[ + "SecurityGroups" + ][0] + default_sg_id = default_sg["GroupId"] + ec2_client.authorize_security_group_ingress( + GroupId=default_sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": 139, + "ToPort": 139, + "IpRanges": [{"CidrIp": "0.0.0.0/0"}], + } + ], + ) + subnet_id = ec2_client.create_subnet(VpcId=vpc_id, CidrBlock="10.0.0.0/16")[ + "Subnet" + ]["SubnetId"] + instance = ec2_resource.create_instances( + ImageId="ami-12345678", + MinCount=1, + MaxCount=1, + InstanceType="t2.micro", + SecurityGroupIds=[default_sg_id], + NetworkInterfaces=[ + { + "DeviceIndex": 0, + "SubnetId": subnet_id, + "AssociatePublicIpAddress": True, + } + ], + TagSpecifications=[ + {"ResourceType": "instance", "Tags": [{"Key": "Name", "Value": "test"}]} + ], + )[0] + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer.ec2_client", + new=EC2(aws_provider), + ): + # Test Fixer + from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import ( + fixer, + ) + + assert fixer(instance.id, AWS_REGION_EU_WEST_1) + + @mock_aws + def test_ec2_instance_exposed_port_with_public_ip_in_public_subnet_only_445_port( + self, + ): + # Create EC2 Mocked Resources + ec2_client = client("ec2", region_name=AWS_REGION_EU_WEST_1) + ec2_resource = resource("ec2", region_name=AWS_REGION_EU_WEST_1) + vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"] + default_sg = ec2_client.describe_security_groups(GroupNames=["default"])[ + "SecurityGroups" + ][0] + default_sg_id = default_sg["GroupId"] + ec2_client.authorize_security_group_ingress( + GroupId=default_sg_id, + IpPermissions=[ + { + "IpProtocol": "tcp", + "FromPort": 445, + "ToPort": 445, + "IpRanges": [{"CidrIp": "0.0.0.0/0"}], + } + ], + ) + subnet_id = ec2_client.create_subnet(VpcId=vpc_id, CidrBlock="10.0.0.0/16")[ + "Subnet" + ]["SubnetId"] + # add default route of subnet to an internet gateway to make it public + igw_id = ec2_client.create_internet_gateway()["InternetGateway"][ + "InternetGatewayId" + ] + # attach internet gateway to subnet + ec2_client.attach_internet_gateway(InternetGatewayId=igw_id, VpcId=vpc_id) + # create route table + route_table_id = ec2_client.create_route_table(VpcId=vpc_id)["RouteTable"][ + "RouteTableId" + ] + # associate route table with subnet + ec2_client.associate_route_table( + RouteTableId=route_table_id, SubnetId=subnet_id + ) + # add route to route table + ec2_client.create_route( + RouteTableId=route_table_id, + DestinationCidrBlock="0.0.0.0/0", + GatewayId=igw_id, + ) + instance = ec2_resource.create_instances( + ImageId="ami-12345678", + MinCount=1, + MaxCount=1, + InstanceType="t2.micro", + SecurityGroupIds=[default_sg_id], + NetworkInterfaces=[ + { + "DeviceIndex": 0, + "SubnetId": subnet_id, + "AssociatePublicIpAddress": True, + } + ], + TagSpecifications=[ + {"ResourceType": "instance", "Tags": [{"Key": "Name", "Value": "test"}]} + ], + )[0] + + from prowler.providers.aws.services.ec2.ec2_service import EC2 + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer.ec2_client", + new=EC2(aws_provider), + ): + # Test Fixer + from prowler.providers.aws.services.ec2.ec2_instance_port_cifs_exposed_to_internet.ec2_instance_port_cifs_exposed_to_internet_fixer import ( + fixer, + ) + + assert fixer(instance.id, AWS_REGION_EU_WEST_1)