コード例 #1
0
    def test_new_findings(self):
        """output.new_findings.Findings"""
        self.maxDiff = None
        test_policy = {
            "Version":
            "2012-10-17",
            "Statement": [{
                "Effect": "Allow",
                "Action": ["s3:GetObject"],
                "Resource": "*"
            }]
        }
        policy_document = PolicyDocument(test_policy)
        # (1) If the user is a member of an excluded group, return True

        exclusions_cfg = dict(users=["obama"],
                              groups=["exclude-group"],
                              roles=["MyRole"],
                              policies=["exclude-policy"])
        exclusions = Exclusions(exclusions_cfg)
        attached_managed_policies = [{
            "PolicyArn": "arn:aws:iam::aws:policy/AWSLambdaFullAccess",
            "PolicyName": "AWSLambdaFullAccess"
        }]
        # Let's just re-use the same policy for users groups and roles
        user_finding = UserFinding(
            policy_name="MyPolicy",
            arn="arn:aws:iam::123456789012:user/SomeUser",
            actions=["s3:GetObject"],
            policy_document=policy_document,
            group_membership=["admin"],
            attached_managed_policies=attached_managed_policies,
            exclusions=exclusions)
        group_finding = GroupFinding(
            policy_name="MyPolicy",
            arn="arn:aws:iam::123456789012:group/SomeGroup",
            actions=["s3:GetObject"],
            policy_document=policy_document,
            members=["obama"],
            exclusions=exclusions)
        trust_policy_from_compute_service_ecs_tasks = {
            "Version":
            "2012-10-17",
            "Statement": [{
                "Effect": "Allow",
                "Action": ["sts:AssumeRole"],
                "Principal": {
                    "Service": "ecs-tasks.amazonaws.com",
                    "AWS": "arn:aws:iam::012345678910:root",
                }
            }]
        }
        assume_role_policy_document = AssumeRolePolicyDocument(
            trust_policy_from_compute_service_ecs_tasks)
        role_finding = RoleFinding(
            policy_name="MyPolicy",
            arn="arn:aws:iam::123456789012:role/SomeRole",
            actions=["s3:GetObject"],
            policy_document=policy_document,
            assume_role_policy_document=assume_role_policy_document,
            exclusions=exclusions)
        policy_finding = PolicyFinding(
            policy_name="AWSLambdaFullAccess",
            arn="arn:aws:iam::aws:policy/AWSLambdaFullAccess",
            actions=["s3:GetObject"],
            policy_document=policy_document,
            exclusions=exclusions)
        all_findings = Findings(exclusions)
        all_findings.add_user_finding(user_finding)
        result = all_findings.users[0]
        expected_user_result = {
            "AccountID": "123456789012",
            "ManagedBy": "Customer",
            "Name": "SomeUser",
            "PolicyName": "MyPolicy",
            "Type": "User",
            "Arn": "arn:aws:iam::123456789012:user/SomeUser",
            "ActionsCount": 1,
            "ServicesCount": 1,
            "Services": ["s3"],
            "Actions": ["s3:GetObject"],
            "PolicyDocument": {
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Effect": "Allow",
                    "Action": ["s3:GetObject"],
                    "Resource": "*"
                }]
            },
            "AssumeRolePolicyDocument": None,
            "AssumableByComputeService": [],
            "PrivilegeEscalation": [],
            "DataExfiltrationActions": ["s3:GetObject"],
            "PermissionsManagementActions": []
        }
        self.assertDictEqual(result.json, expected_user_result)
        principal_policy_mapping = [
            {
                "Principal": "SomeUser",
                "Type": "User",
                "PolicyType": "Managed",
                "ManagedBy": "AWS",
                "PolicyName": "MyPolicy",
                "GroupMembership": None,
            },
            {
                "Principal": "SomeUser",
                "Type": "User",
                "PolicyType": "Managed",
                "ManagedBy": "AWS",
                "PolicyName": "AWSLambdaFullAccess",
                "GroupMembership": None,
            },
            {
                "Principal": "SomeGroup",
                "Type": "Group",
                "PolicyType": "Managed",
                "ManagedBy": "AWS",
                "PolicyName": "MyPolicy",
                "GroupMembership": None,
            },
            {
                "Principal": "SomeRole",
                "Type": "Role",
                "PolicyType": "Managed",
                "ManagedBy": "AWS",
                "PolicyName": "MyPolicy",
                "GroupMembership": None,
            },
        ]
        all_findings.principal_policy_mapping = principal_policy_mapping
        all_findings.add_group_finding(group_finding)
        all_findings.add_role_finding(role_finding)
        all_findings.add_policy_finding(policy_finding)
        print(len(all_findings))
        self.assertEqual(len(all_findings), 4)
        results = all_findings.json
        expected_results = [{
            "AccountID": "N/A",
            "ManagedBy": "AWS",
            "Name": "AWSLambdaFullAccess",
            "PolicyName": "AWSLambdaFullAccess",
            "Type": "Policy",
            "Arn": "arn:aws:iam::aws:policy/AWSLambdaFullAccess",
            "ActionsCount": 1,
            "ServicesCount": 1,
            "Services": ["s3"],
            "Actions": ["s3:GetObject"],
            "PolicyDocument": {
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Effect": "Allow",
                    "Action": ["s3:GetObject"],
                    "Resource": "*"
                }]
            },
            "AssumeRolePolicyDocument": None,
            "AssumableByComputeService": [],
            "PrivilegeEscalation": [],
            "DataExfiltrationActions": ["s3:GetObject"],
            "PermissionsManagementActions": []
        }, {
            "AccountID": "123456789012",
            "ManagedBy": "Customer",
            "Name": "SomeUser",
            "PolicyName": "MyPolicy",
            "Type": "User",
            "Arn": "arn:aws:iam::123456789012:user/SomeUser",
            "ActionsCount": 1,
            "ServicesCount": 1,
            "Services": ["s3"],
            "Actions": ["s3:GetObject"],
            "PolicyDocument": {
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Effect": "Allow",
                    "Action": ["s3:GetObject"],
                    "Resource": "*"
                }]
            },
            "AssumeRolePolicyDocument": None,
            "AssumableByComputeService": [],
            "PrivilegeEscalation": [],
            "DataExfiltrationActions": ["s3:GetObject"],
            "PermissionsManagementActions": []
        }, {
            "AccountID": "123456789012",
            "ManagedBy": "Customer",
            "Name": "SomeGroup",
            "PolicyName": "MyPolicy",
            "Type": "Group",
            "Arn": "arn:aws:iam::123456789012:group/SomeGroup",
            "ActionsCount": 1,
            "ServicesCount": 1,
            "Services": ["s3"],
            "Actions": ["s3:GetObject"],
            "PolicyDocument": {
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Effect": "Allow",
                    "Action": ["s3:GetObject"],
                    "Resource": "*"
                }]
            },
            "AssumeRolePolicyDocument": None,
            "AssumableByComputeService": [],
            "PrivilegeEscalation": [],
            "DataExfiltrationActions": ["s3:GetObject"],
            "PermissionsManagementActions": []
        }, {
            "AccountID": "123456789012",
            "ManagedBy": "Customer",
            "Name": "SomeRole",
            "PolicyName": "MyPolicy",
            "Type": "Role",
            "Arn": "arn:aws:iam::123456789012:role/SomeRole",
            "ActionsCount": 1,
            "ServicesCount": 1,
            "Services": ["s3"],
            "Actions": ["s3:GetObject"],
            "PolicyDocument": {
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Effect": "Allow",
                    "Action": ["s3:GetObject"],
                    "Resource": "*"
                }]
            },
            "AssumeRolePolicyDocument": {
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Effect": "Allow",
                    "Action": ["sts:AssumeRole"],
                    "Principal": {
                        "Service": "ecs-tasks.amazonaws.com",
                        "AWS": "arn:aws:iam::012345678910:root"
                    }
                }]
            },
            "AssumableByComputeService": ["ecs-tasks"],
            "PrivilegeEscalation": [],
            "DataExfiltrationActions": ["s3:GetObject"],
            "PermissionsManagementActions": []
        }]
        # print(json.dumps(all_findings.json, indent=4))
        self.assertListEqual(results, expected_results)
コード例 #2
0
class AuthorizationDetails:
    """
    Represents the entire JSON file generated by the aws iam get-account-authorization-details command.
    """
    def __init__(self, auth_json):
        self.auth_json = auth_json
        self.policies = ManagedPolicyDetails(auth_json.get("Policies", None))
        self.user_detail_list = PrincipalTypeDetails(
            auth_json.get("UserDetailList", None))
        self.group_detail_list = PrincipalTypeDetails(
            auth_json.get("GroupDetailList", None))
        self.role_detail_list = PrincipalTypeDetails(
            auth_json.get("RoleDetailList", None))
        self._update_group_membership()
        self.findings = Findings()
        self.customer_managed_policies_in_use = self._customer_managed_policies_in_use(
        )
        self.aws_managed_policies_in_use = self._aws_managed_policies_in_use()
        # New Authorization file stuff

        self.new_group_detail_list = GroupDetailList(
            auth_json.get("GroupDetailList"), self.policies)
        self.new_user_detail_list = UserDetailList(
            auth_json.get("UserDetailList"), self.policies,
            self.new_group_detail_list)
        self.new_role_detail_list = RoleDetailList(
            auth_json.get("RoleDetailList"), self.policies)

    @property
    def new_principal_policy_mapping(self):
        """Get the new JSON format of the Principals data"""
        results = {
            "groups": self.new_group_detail_list.json,
            "users": self.new_user_detail_list.json,
            "roles": self.new_role_detail_list.json,
            "policies": self.policies.json
        }
        return results

    def _update_group_membership(self):
        """A hacky approach to ensuring that groups have their list of members, as that is not included
        in the initial AWS API call response and we have to calculate it ourselves."""
        # Let's make sure the groups know who their members are
        # First, skim through the user_detail_list, and compile the dict of groups and their users
        groups = {}
        for principal in self.user_detail_list.principals:
            if principal.principal_type == "User":
                if principal.group_member:
                    logger.debug(
                        f"User {principal.name} is a member of the group(s): {principal.group_member}"
                    )
                    for group in principal.group_member:
                        if group not in groups:
                            groups[group] = []
                            groups[group].append(principal.name)
                        else:
                            groups[group].append(principal.name)
        # Let's make sure we add the list of group members to their groups We do that by skimming through that
        # groups list and then adding them as class attributes to the principal class.
        logger.debug(f"_update_group_membership: groups: {groups}")
        for principal in self.group_detail_list.principals:
            if principal.principal_type == "Group":
                for group in groups:
                    if group.lower() == principal.name.lower():
                        principal.members.extend(groups[group])
                        logger.debug(
                            f"_update_group_membership: The group {group} has members: {groups[group]}"
                        )

    def _aws_managed_policies_in_use(self):
        aws_managed_policies = []
        for policy in self.policies.policy_details:
            if "arn:aws:iam::aws:" in policy.arn:
                aws_managed_policies.append(policy.policy_name)
        # Policies attached to groups
        for principal in self.group_detail_list.principals:
            for attached_managed_policy in principal.attached_managed_policies:
                if "arn:aws:iam::aws:" in attached_managed_policy.get(
                        "PolicyArn"):
                    aws_managed_policies.append(
                        attached_managed_policy.get("PolicyName"))
        # Policies attached to users
        for principal in self.user_detail_list.principals:
            for attached_managed_policy in principal.attached_managed_policies:
                if "arn:aws:iam::aws:" in attached_managed_policy.get(
                        "PolicyArn"):
                    aws_managed_policies.append(
                        attached_managed_policy.get("PolicyName"))
        # Policies attached to roles
        for principal in self.role_detail_list.principals:
            for attached_managed_policy in principal.attached_managed_policies:
                if "arn:aws:iam::aws:" in attached_managed_policy.get(
                        "PolicyArn"):
                    aws_managed_policies.append(
                        attached_managed_policy.get("PolicyName"))
        aws_managed_policies = list(dict.fromkeys(aws_managed_policies))
        return aws_managed_policies

    def _customer_managed_policies_in_use(self):
        customer_managed_policies = []
        for policy in self.policies.policy_details:
            if "arn:aws:iam::aws:" not in policy.arn:
                customer_managed_policies.append(policy.policy_name)
        # Policies attached to groups
        for principal in self.group_detail_list.principals:
            for attached_managed_policy in principal.attached_managed_policies:
                # Skipping coverage here because it would be redundant
                if "arn:aws:iam::aws:" not in attached_managed_policy.get(
                        "PolicyArn"):  # pragma: no cover
                    customer_managed_policies.append(
                        attached_managed_policy.get("PolicyName"))
        # Policies attached to users
        for principal in self.user_detail_list.principals:
            for attached_managed_policy in principal.attached_managed_policies:
                # Skipping coverage here because it would be redundant
                if "arn:aws:iam::aws:" not in attached_managed_policy.get(
                        "PolicyArn"):  # pragma: no cover
                    customer_managed_policies.append(
                        attached_managed_policy.get("PolicyName"))
        # Policies attached to roles
        for principal in self.role_detail_list.principals:
            for attached_managed_policy in principal.attached_managed_policies:
                # Skipping coverage here because it would be redundant
                if "arn:aws:iam::aws:" not in attached_managed_policy.get(
                        "PolicyArn"):  # pragma: no cover
                    customer_managed_policies.append(
                        attached_managed_policy.get("PolicyName"))
        customer_managed_policies = list(
            dict.fromkeys(customer_managed_policies))
        return customer_managed_policies

    @property
    def groups(self):
        """A list of the group names in the account, according to the account authorization details."""
        group_names = []
        for principal in self.group_detail_list.principals:
            group_names.append(principal.name)
        return group_names

    @property
    def roles(self):
        """A list of the role names in the account, according to the account authorization details."""
        role_names = []
        for principal in self.role_detail_list.principals:
            role_names.append(principal.name)
        return role_names

    @property
    def users(self):
        """A list of the user names in the account, according to the account authorization details."""
        user_names = []
        for principal in self.user_detail_list.principals:
            user_names.append(principal.name)
        return user_names

    @property
    def principals(self):
        """Get a list of PrincipalDetail objects for all principals - Users, Groups, or Roles - in the account."""
        all_principals = []
        # Users
        for principal in self.user_detail_list.principals:
            all_principals.append(principal)
        # Groups
        for principal in self.group_detail_list.principals:
            all_principals.append(principal)
        # Roles
        for principal in self.role_detail_list.principals:
            all_principals.append(principal)
        return all_principals

    @property
    def principal_policy_mapping(self):
        """
        Returns a mapping of principals vs the policies that they are attached to - either inline or managed.
        """
        principal_policy_mapping = []

        for principal in self.principals:
            # Inline Policies
            if principal.inline_principal_policies:
                for inline_policy in principal.inline_principal_policies:
                    if principal.principal_type == "User":
                        group_membership = principal.group_member
                    elif principal.principal_type == "Group":
                        group_membership = principal.members
                    else:
                        group_membership = None
                    entry = dict(
                        Principal=principal.name,
                        Type=principal.principal_type,
                        PolicyType="Inline",
                        ManagedBy="Customer",
                        PolicyName=inline_policy.get("PolicyName"),
                        GroupMembership=group_membership,
                    )
                    principal_policy_mapping.append(entry)
            # AttachedManagedPolicies
            if principal.attached_managed_policies:
                for attached_managed_policy in principal.attached_managed_policies:
                    if "arn:aws:iam::aws:" in attached_managed_policy.get(
                            "PolicyArn"):
                        managed_by = "AWS"
                    else:
                        managed_by = "Customer"
                    if principal.principal_type == "User":
                        group_membership = principal.group_member
                    elif principal.principal_type == "Group":
                        group_membership = principal.members
                    else:
                        group_membership = None
                    entry = dict(
                        Principal=principal.name,
                        Type=principal.principal_type,
                        PolicyType="Managed",
                        ManagedBy=managed_by,
                        PolicyName=attached_managed_policy.get("PolicyName"),
                        GroupMembership=group_membership,
                    )
                    principal_policy_mapping.append(entry)

            # While users might have inline policies or managed policies,
            # their findings will need to reflect their group membership findings as well.
            # So we need to run that loop again, finding the data for their groups and adding it.
            if principal.principal_type == "User":
                group_memberships = principal.group_member
                for group_membership in group_memberships:
                    for some_principal in self.principals:
                        if (some_principal.principal_type == "Group"
                                and some_principal.name == group_membership):
                            if some_principal.inline_principal_policies:
                                for (
                                        inline_policy
                                ) in some_principal.inline_principal_policies:
                                    entry = dict(
                                        Principal=principal.name,
                                        Type=principal.principal_type,
                                        PolicyType="Inline",
                                        ManagedBy="Customer",
                                        PolicyName=inline_policy.get(
                                            "PolicyName"),
                                        GroupMembership=principal.group_member,
                                    )
                                    principal_policy_mapping.append(entry)
                            # AttachedManagedPolicies
                            if some_principal.attached_managed_policies:
                                for (
                                        attached_managed_policy
                                ) in some_principal.attached_managed_policies:
                                    if ("arn:aws:iam::aws:"
                                            in attached_managed_policy.get(
                                                "PolicyArn")):
                                        managed_by = "AWS"
                                    else:
                                        managed_by = "Customer"
                                    entry = dict(
                                        Principal=principal.name,
                                        Type=principal.principal_type,
                                        PolicyType="Managed",
                                        ManagedBy=managed_by,
                                        PolicyName=attached_managed_policy.get(
                                            "PolicyName"),
                                        GroupMembership=principal.group_member,
                                    )
                                    principal_policy_mapping.append(entry)
        # Sort it
        principal_policy_mapping = sorted(
            principal_policy_mapping,
            key=itemgetter("Type", "Principal", "PolicyType", "PolicyName"),
        )
        return principal_policy_mapping

    def missing_resource_constraints(self,
                                     exclusions=DEFAULT_EXCLUSIONS,
                                     modify_only=True):
        """Scan the account authorization details for missing resource constraints."""
        if not isinstance(exclusions, Exclusions):
            raise Exception(
                "The provided exclusions is not the Exclusions object type. "
                "Please use the Exclusions object.")
        self.findings = Findings(exclusions)
        print("-----USERS-----")
        self.scan_principal_type_details(self.user_detail_list, exclusions,
                                         modify_only)
        print("-----GROUPS-----")
        self.scan_principal_type_details(self.group_detail_list, exclusions,
                                         modify_only)
        print("-----ROLES-----")
        self.scan_principal_type_details(self.role_detail_list, exclusions,
                                         modify_only)
        print("-----POLICIES-----")
        self.findings.principal_policy_mapping = self.principal_policy_mapping
        self.scan_policy_details(exclusions, modify_only)
        return self.findings.json

    def scan_policy_details(self,
                            exclusions=DEFAULT_EXCLUSIONS,
                            modify_only=True):
        """Scan the ManagedPolicyDetails block of the account authorization details output."""
        if not isinstance(exclusions, Exclusions):
            raise Exception(
                "The provided exclusions is not the Exclusions object type. "
                "Please use the Exclusions object.")
        for policy in self.policies.policy_details:
            print(f"Scanning policy: {policy.policy_name}")
            actions_missing_resource_constraints = []
            if exclusions.is_policy_excluded(
                    policy.policy_name) or exclusions.is_policy_excluded(
                        policy.full_policy_path):
                print(f"\tExcluded policy name: {policy.policy_name}")
            else:
                for statement in policy.policy_document.statements:
                    if modify_only:
                        if statement.effect == "Allow":
                            actions_missing_resource_constraints.extend(
                                statement.
                                missing_resource_constraints_for_modify_actions(
                                    exclusions))
                    else:
                        if statement.effect == "Allow":
                            actions_missing_resource_constraints.extend(
                                statement.missing_resource_constraints(
                                    exclusions))
                if actions_missing_resource_constraints:
                    actions_missing_resource_constraints = list(
                        dict.fromkeys(actions_missing_resource_constraints)
                    )  # remove duplicates
                    actions_missing_resource_constraints.sort()
                    policy_finding = PolicyFinding(
                        policy_name=policy.policy_name,
                        arn=policy.arn,
                        actions=actions_missing_resource_constraints,
                        policy_document=policy.policy_document,
                        exclusions=exclusions,
                    )
                    self.findings.add_policy_finding(policy_finding)

    def scan_principal_type_details(
        self,
        principal_type_detail_list,
        exclusions=DEFAULT_EXCLUSIONS,
        modify_only=True,
    ):
        """Scan the UserDetailList, GroupDetailList, or RoleDetailList
        blocks of the account authorization details output."""
        if not isinstance(exclusions, Exclusions):
            raise Exception(
                "The provided exclusions is not the Exclusions object type. "
                "Please use the Exclusions object.")
        for principal in principal_type_detail_list.principals:
            print(f"Scanning {principal.principal_type}: {principal.name}")

            for policy in principal.policy_list:
                print(f"\tScanning Policy: {policy['PolicyName']}")

                if exclusions.is_policy_excluded(policy["PolicyName"]):
                    pass
                elif principal.is_principal_excluded(exclusions):
                    print(f"\tExcluded principal name: {principal.name}")
                else:
                    policy_document = policy["PolicyDocument"]
                    actions_missing_resource_constraints = []
                    for statement in policy_document.statements:
                        if modify_only:
                            if statement.effect == "Allow":
                                actions_missing_resource_constraints.extend(
                                    statement.
                                    missing_resource_constraints_for_modify_actions(
                                        exclusions))
                        else:
                            if statement.effect == "Allow":
                                actions_missing_resource_constraints.extend(
                                    statement.missing_resource_constraints(
                                        exclusions))
                    if actions_missing_resource_constraints:

                        if principal.principal_type == "User":

                            user_finding = UserFinding(
                                policy_name=policy["PolicyName"],
                                arn=principal.arn,
                                actions=actions_missing_resource_constraints,
                                policy_document=policy["PolicyDocument"],
                                exclusions=exclusions,
                                attached_managed_policies=principal.
                                attached_managed_policies,
                                group_membership=principal.group_member,
                            )
                            self.findings.add_user_finding(user_finding)
                        elif principal.principal_type == "Group":
                            group_finding = GroupFinding(
                                policy_name=policy["PolicyName"],
                                arn=principal.arn,
                                actions=actions_missing_resource_constraints,
                                policy_document=policy["PolicyDocument"],
                                exclusions=exclusions,
                                members=principal.members,
                                attached_managed_policies=principal.
                                attached_managed_policies,
                            )
                            logger.debug(
                                f"scan_principal_type_details: The Group {principal.name} has the members {principal.members}"
                            )
                            self.findings.add_group_finding(group_finding)
                        elif principal.principal_type == "Role":
                            role_finding = RoleFinding(
                                policy_name=policy["PolicyName"],
                                arn=principal.arn,
                                actions=actions_missing_resource_constraints,
                                policy_document=policy["PolicyDocument"],
                                exclusions=exclusions,
                                assume_role_policy_document=principal.
                                assume_role_policy_document,
                                attached_managed_policies=principal.
                                attached_managed_policies,
                            )
                            self.findings.add_role_finding(role_finding)