Ejemplo n.º 1
0
def get_cidrs_for_account(account, cidrs):
    account = Account(None, account)

    # TODO Need to use CloudMapper's prepare to identify trusted IPs that are actually in use.
    for region_json in get_regions(account):
        region = Region(account, region_json)
        sg_json = query_aws(account, "ec2-describe-security-groups", region)
        sgs = pyjq.all('.SecurityGroups[]', sg_json)
        for sg in sgs:
            cidrs_seen = set()
            cidr_and_name_list = pyjq.all('.IpPermissions[].IpRanges[]|[.CidrIp,.Description]', sg)
            for cidr, name in cidr_and_name_list:
                if not is_external_cidr(cidr):
                    continue

                if is_unneeded_cidr(cidr):
                    print('WARNING: Unneeded cidr used {} in {}'.format(cidr, sg['GroupId']))
                    continue

                for cidr_seen in cidrs_seen:
                    if (IPNetwork(cidr_seen) in IPNetwork(cidr) or
                                IPNetwork(cidr) in IPNetwork(cidr_seen)):
                        print('WARNING: Overlapping CIDRs in {}, {} and {}'.format(sg['GroupId'], cidr, cidr_seen))
                cidrs_seen.add(cidr)

                if cidr.startswith('0.0.0.0') and not cidr.endswith('/0'):
                    print('WARNING: Unexpected CIDR for attempted public access {} in {}'.format(cidr, sg['GroupId']))
                    continue

                if cidr == '0.0.0.0/0':
                    continue

                cidrs[cidr] = cidrs.get(cidr, set())
                if name is not None:
                    cidrs[cidr].add(name)
Ejemplo n.º 2
0
def audit_sg(findings, region):
    # TODO Check if security groups allow large CIDR range (ex. 1.2.3.4/3)
    # TODO Check if an SG restricts IPv4 and then opens IPv6 or vice versa.

    cidrs = {}
    sg_json = query_aws(region.account, 'ec2-describe-security-groups', region)
    sgs = pyjq.all('.SecurityGroups[]', sg_json)
    for sg in sgs:
        cidr_and_name_list = pyjq.all('.IpPermissions[].IpRanges[]|[.CidrIp,.Description]', sg)
        for cidr, name in cidr_and_name_list:
            if not is_external_cidr(cidr):
                continue

            if is_unblockable_cidr(cidr):
                findings.add(Finding(
                    region,
                    'SG_CIDR_UNNEEDED',
                    sg['GroupId'],
                    resource_details={'cidr': cidr}))
                continue

            if cidr.startswith('0.0.0.0') and not cidr.endswith('/0'):
                findings.add(Finding(
                    region,
                    'SG_CIDR_UNEXPECTED',
                    sg['GroupId'],
                    resource_details={'cidr': cidr}))
                continue

            if cidr == '0.0.0.0/0':
                continue

            cidrs[cidr] = cidrs.get(cidr, set())
            cidrs[cidr].add(sg['GroupId'])

        for ip_permissions in sg['IpPermissions']:
            cidrs_seen = set()
            for ip_ranges in ip_permissions['IpRanges']:
                if 'CidrIp' not in ip_ranges:
                    continue
                cidr = ip_ranges['CidrIp']
                for cidr_seen in cidrs_seen:
                    if (IPNetwork(cidr_seen) in IPNetwork(cidr) or
                            IPNetwork(cidr) in IPNetwork(cidr_seen)):
                        findings.add(Finding(
                            region,
                            'SG_CIDR_OVERLAPS',
                            sg['GroupId'],
                            resource_details={'cidr1': cidr, 'cidr2': cidr_seen}))
                cidrs_seen.add(cidr)

    for cidr in cidrs:
        ip = IPNetwork(cidr)
        if ip.size > 2048:
            findings.add(Finding(
                region,
                'SG_LARGE_CIDR',
                cidr,
                resource_details={'size': ip.size, 'security_groups': cidrs[cidr]}))
Ejemplo n.º 3
0
def get_cidrs_for_account(account, cidrs):
    account = Account(None, account)

    for region_json in get_regions(account):
        region = Region(account, region_json)
        sg_json = query_aws(account, "ec2-describe-security-groups", region)
        sgs = pyjq.all(".SecurityGroups[]", sg_json)
        for sg in sgs:
            cidr_and_name_list = pyjq.all(
                ".IpPermissions[].IpRanges[]|[.CidrIp,.Description]", sg
            )
            for cidr, name in cidr_and_name_list:
                if not is_external_cidr(cidr):
                    continue

                if is_unblockable_cidr(cidr):
                    print(
                        "WARNING: Unneeded cidr used {} in {}".format(
                            cidr, sg["GroupId"]
                        )
                    )
                    continue

                if cidr.startswith("0.0.0.0") and not cidr.endswith("/0"):
                    print(
                        "WARNING: Unexpected CIDR for attempted public access {} in {}".format(
                            cidr, sg["GroupId"]
                        )
                    )
                    continue

                if cidr == "0.0.0.0/0":
                    continue

                cidrs[cidr] = cidrs.get(cidr, set())
                if name is not None:
                    cidrs[cidr].add(name)

            for ip_permissions in sg["IpPermissions"]:
                cidrs_seen = set()
                for ip_ranges in ip_permissions["IpRanges"]:
                    if "CidrIp" not in ip_ranges:
                        continue
                    cidr = ip_ranges["CidrIp"]
                    for cidr_seen in cidrs_seen:
                        if IPNetwork(cidr_seen) in IPNetwork(cidr) or IPNetwork(
                            cidr
                        ) in IPNetwork(cidr_seen):
                            print(
                                "WARNING: Overlapping CIDRs in {}, {} and {}".format(
                                    sg["GroupId"], cidr, cidr_seen
                                )
                            )
                    cidrs_seen.add(cidr)
Ejemplo n.º 4
0
def get_external_cidrs(account, config):
    external_cidrs = []
    unique_cidrs = {}
    for region in account.children:
        for vpc in region.children:
            sgs = get_sgs(vpc)

            # Get external IPs
            for sg in sgs:
                cidrs = pyjq.all('.IpPermissions[].IpRanges[].CidrIp', sg)
                for cidr in cidrs:
                    unique_cidrs[cidr] = 1

    # Remove private CIDR ranges
    for cidr in unique_cidrs.keys():
        if is_external_cidr(cidr):
            # It's something else, so add it
            external_cidrs.append(Cidr(cidr, get_cidr_name(cidr, config)))
    return external_cidrs
Ejemplo n.º 5
0
def get_cidrs_for_account(account, cidrs):
    account = Account(None, account)

    # TODO Need to use CloudMapper's prepare to identify trusted IPs that are actually in use.
    for region_json in get_regions(account):
        region = Region(account, region_json)
        sg_json = query_aws(account, "ec2-describe-security-groups", region)
        sgs = pyjq.all('.SecurityGroups[]', sg_json)
        for sg in sgs:
            cidr_and_name_list = pyjq.all(
                '.IpPermissions[].IpRanges[]|[.CidrIp,.Description]', sg)
            for cidr, name in cidr_and_name_list:
                if not is_external_cidr(cidr):
                    continue
                if is_unneeded_cidr(cidr):
                    print('WARNING: Unneeded cidr used {}'.format(cidr))
                    continue
                if cidr == '0.0.0.0/0':
                    continue
                cidrs[cidr] = cidrs.get(cidr, set())
                if name is not None:
                    cidrs[cidr].add(name)
Ejemplo n.º 6
0
def audit_sg(findings, region):
    # TODO Check if security groups allow large CIDR range (ex. 1.2.3.4/3)
    # TODO Check if an SG restricts IPv4 and then opens IPv6 or vice versa.

    cidrs = {}
    sg_json = query_aws(region.account, "ec2-describe-security-groups", region)
    sgs = pyjq.all(".SecurityGroups[]", sg_json)
    for sg in sgs:
        cidr_and_name_list = pyjq.all(
            ".IpPermissions[]?.IpRanges[]|[.CidrIp,.Description]", sg)
        for cidr, name in cidr_and_name_list:
            if not is_external_cidr(cidr):
                continue

            if is_unblockable_cidr(cidr):
                findings.add(
                    Finding(
                        region,
                        "SG_CIDR_UNNEEDED",
                        sg["GroupId"],
                        resource_details={"cidr": cidr},
                    ))
                continue

            if cidr.startswith("0.0.0.0") and not cidr.endswith("/0"):
                findings.add(
                    Finding(
                        region,
                        "SG_CIDR_UNEXPECTED",
                        sg["GroupId"],
                        resource_details={"cidr": cidr},
                    ))
                continue

            if cidr == "0.0.0.0/0":
                continue

            cidrs[cidr] = cidrs.get(cidr, list())
            cidrs[cidr].append(sg["GroupId"])

        for ip_permissions in sg.get("IpPermissions", []):
            cidrs_seen = set()
            for ip_ranges in ip_permissions.get("IpRanges", []):
                if "CidrIp" not in ip_ranges:
                    continue
                cidr = ip_ranges["CidrIp"]
                for cidr_seen in cidrs_seen:
                    if IPNetwork(cidr_seen) in IPNetwork(cidr) or IPNetwork(
                            cidr) in IPNetwork(cidr_seen):
                        findings.add(
                            Finding(
                                region,
                                "SG_CIDR_OVERLAPS",
                                sg["GroupId"],
                                resource_details={
                                    "cidr1": cidr,
                                    "cidr2": cidr_seen
                                },
                            ))
                cidrs_seen.add(cidr)

    for cidr in cidrs:
        ip = IPNetwork(cidr)
        if ip.size > 2048:
            findings.add(
                Finding(
                    region,
                    "SG_LARGE_CIDR",
                    cidr,
                    resource_details={
                        "size": ip.size,
                        "security_groups": list(cidrs[cidr]),
                    },
                ))
Ejemplo n.º 7
0
def get_connections(cidrs, vpc, outputfilter):
    """
    For a VPC, for each instance, find all of the other instances that can connect to it,
    including those in peered VPCs.
    Note I do not consider subnet ACLs, routing tables, or some other network concepts.
    """
    connections = {}

    # Get mapping of security group names to nodes that have that security group
    sg_to_instance_mapping = {}
    for instance in vpc.leaves:
        for sg in instance.security_groups:
            sg_to_instance_mapping.setdefault(sg, {})[instance] = True

    # For each security group, find all the instances that are allowed to connect to instances
    # within that group.
    for sg in get_sgs(vpc):
        # Get the CIDRs that are allowed to connect
        for cidr in pyjq.all('.IpPermissions[].IpRanges[].CidrIp', sg):
            if not is_external_cidr(cidr):
                # This is a private IP, ex. 10.0.0.0/16

                # See if we should skip this
                if not outputfilter["internal_edges"]:
                    continue

                # Find all instances in this VPC and peered VPCs that are in this CIDR
                for sourceVpc in itertools.chain(vpc.peers, (vpc,)):

                    # Ensure it is possible for instances in this VPC to be in the CIDR
                    if not (IPNetwork(sourceVpc.cidr) in IPNetwork(cidr) or
                            IPNetwork(cidr) in IPNetwork(sourceVpc.cidr)):
                        # The CIDR from the security group does not overlap with the CIDR of the VPC,
                        # so skip it
                        continue

                    # For each instance, check if one of its IPs is within the CIDR
                    for sourceInstance in sourceVpc.leaves:
                        for ip in sourceInstance.ips:
                            if IPAddress(ip) in IPNetwork(cidr):
                                # Instance found that can connect to instances in the SG
                                # So connect this instance (sourceInstance) to every instance
                                # in the SG.
                                for targetInstance in sg_to_instance_mapping.get(sg["GroupId"], {}):
                                    add_connection(connections, sourceInstance, targetInstance, sg)

            else:
                # This is an external IP (ie. not in a private range).
                for instance in sg_to_instance_mapping.get(sg["GroupId"], {}):
                    # Ensure it has a public IP, as resources with only private IPs can't be reached
                    if instance.is_public:
                        cidrs[cidr].is_used = True
                        add_connection(connections, cidrs[cidr], instance, sg)
                    else:
                        if cidr == '0.0.0.0/0':
                            # Resource is not public, but allows anything to access it,
                            # so mark set all the resources in the VPC as allowing access to it.
                            for source_instance in vpc.leaves:
                                add_connection(connections, source_instance, instance, sg)

        if outputfilter["internal_edges"]:
            # Connect allowed in Security Groups
            for ingress_sg in pyjq.all('.IpPermissions[].UserIdGroupPairs[].GroupId', sg):
                # We have an SG and a list of SG's it allows in
                for target in sg_to_instance_mapping.get(sg["GroupId"], {}):
                    # We have an instance and a list of SG's it allows in
                    for source in sg_to_instance_mapping.get(ingress_sg, {}):
                        if (not outputfilter["inter_rds_edges"] and
                                (source.node_type == "rds" or source.node_type == "rds_rr") and
                                (target.node_type == "rds" or target.node_type == "rds_rr")):
                            continue
                        add_connection(connections, source, target, sg)

    # Connect everything to the Gateway endpoints
    for targetResource in vpc.leaves:
        if targetResource.has_unrestricted_ingress:
            for sourceVpc in itertools.chain(vpc.peers, (vpc,)):
                for sourceResource in sourceVpc.leaves:
                    add_connection(connections, sourceResource, targetResource, [])

    # Remove connections for source nodes that cannot initiate traffic (ex. VPC endpoints)
    for connection in list(connections):
        if not connection.source.can_egress:
            del connections[connection]

    return connections