コード例 #1
0
    def test_findings_add_list(self):
        """output.findings.findings.add: Make sure the add function works with adding it as a list"""
        test_policy = {
            "Version":
            "2012-10-17",
            "Statement": [{
                "Effect": "Allow",
                "Action": ["s3:GetObject"],
                "Resource": "*"
            }]
        }
        policy_document = PolicyDocument(test_policy)
        finding_1 = Finding(
            policy_name="MyPolicy",
            arn="arn:aws:iam::123456789012:group/SNSNotifications",
            actions=["s3:GetObject"],
            policy_document=policy_document)
        finding_2 = Finding(policy_name="MyPolicy",
                            arn="arn:aws:iam::aws:policy/BadAWSManagedPolicy",
                            actions=["s3:GetObject"],
                            policy_document=policy_document)

        findings = Findings()
        findings.add([finding_1, finding_2])
        result = findings.json
        self.assertTrue(len(result) == 2)
コード例 #2
0
def scan_policy(policy_json, policy_name, exclusions_cfg=DEFAULT_EXCLUSIONS_CONFIG):
    """
    Scan a policy document for missing resource constraints.

    :param policy_json: The AWS IAM policy document.
    :param exclusions_cfg: Defaults to the embedded exclusions file, which has no effect here.
    :param policy_name: The name of the IAM policy. Defaults to the filename when used from command line.
    :return:
    """
    policy_document = PolicyDocument(policy_json)
    actions_missing_resource_constraints = []

    # EXCLUDED ACTIONS - actions to exclude if they are false positives
    excluded_actions = exclusions_cfg.get("exclude-actions", None)
    if excluded_actions == [""]:
        excluded_actions = None

    # convert to lowercase for comparison purposes
    # some weird if/else logic to reduce loops and improve performance slightly
    if excluded_actions:
        excluded_actions = [x.lower() for x in excluded_actions]

    always_include_actions = exclusions_cfg.get("include-actions")
    findings = Findings()

    for statement in policy_document.statements:
        if statement.effect == "Allow":
            actions_missing_resource_constraints.extend(
                statement.missing_resource_constraints_for_modify_actions(
                    always_include_actions
                )
            )
    if actions_missing_resource_constraints:
        results_placeholder = []
        for action in actions_missing_resource_constraints:
            if excluded_actions:
                if not is_name_excluded(action.lower(), excluded_actions):
                    results_placeholder.append(action)  # pragma: no cover
            else:
                results_placeholder.append(action)
        actions_missing_resource_constraints = list(
            dict.fromkeys(results_placeholder)
        )  # remove duplicates
        actions_missing_resource_constraints.sort()
        finding = Finding(
            policy_name=policy_name,
            arn=policy_name,
            actions=actions_missing_resource_constraints,
            policy_document=policy_document,
        )
        findings.add(finding)
    return findings.json
コード例 #3
0
 def __init__(self, auth_json):
     self.auth_json = auth_json
     self.policies = PolicyDetails(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.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()
コード例 #4
0
 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
コード例 #5
0
def scan_policy(policy_json, policy_name, exclusions=DEFAULT_EXCLUSIONS):
    """
    Scan a policy document for missing resource constraints.

    :param exclusions: Exclusions object
    :param policy_json: The AWS IAM policy document.
    :param policy_name: The name of the IAM policy. Defaults to the filename when used from command line.
    :return:
    """
    actions_missing_resource_constraints = []

    policy_document = PolicyDocument(policy_json)

    findings = Findings(exclusions)

    for statement in policy_document.statements:
        logger.debug("Evaluating statement: %s", statement.json)
        if statement.effect == "Allow":
            actions_missing_resource_constraints.extend(
                statement.missing_resource_constraints_for_modify_actions(
                    exclusions))
    if actions_missing_resource_constraints:
        these_results = list(
            dict.fromkeys(
                actions_missing_resource_constraints))  # remove duplicates
        these_results.sort()
        finding = PolicyFinding(
            policy_name=policy_name,
            arn=policy_name,
            actions=these_results,
            policy_document=policy_document,
            exclusions=exclusions,
        )
        findings.add_policy_finding(finding)
        findings.single_use = True
        return finding.json
    else:
        return {}
コード例 #6
0
    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)
コード例 #7
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 = PolicyDetails(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.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()

    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"))

        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:
                if "arn:aws:iam::aws:" not in attached_managed_policy.get(
                        "PolicyArn"):
                    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:
                if "arn:aws:iam::aws:" not in attached_managed_policy.get(
                        "PolicyArn"):
                    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:
                if "arn:aws:iam::aws:" not in attached_managed_policy.get(
                        "PolicyArn"):
                    customer_managed_policies.append(
                        attached_managed_policy.get("PolicyName"))

        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:
                    entry = dict(Principal=principal.name,
                                 Type=principal.principal_type,
                                 PolicyType="Inline",
                                 ManagedBy="Customer",
                                 PolicyName=inline_policy.get("PolicyName"),
                                 Comment=None)
                    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"
                    entry = dict(
                        Principal=principal.name,
                        Type=principal.principal_type,
                        PolicyType="Managed",
                        ManagedBy=managed_by,
                        PolicyName=attached_managed_policy.get("PolicyName"),
                        Comment=None,
                    )
                    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"),
                                                 Comment="Group Membership")
                                    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"),
                                        Comment="Group Membership")
                                    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_cfg=DEFAULT_EXCLUSIONS_CONFIG,
                                     modify_only=True):
        """Scan the account authorization details for missing resource constraints."""
        print("-----USERS-----")
        self.scan_principal_type_details(self.user_detail_list, exclusions_cfg,
                                         modify_only)
        print("-----GROUPS-----")
        self.scan_principal_type_details(self.group_detail_list,
                                         exclusions_cfg, modify_only)
        print("-----ROLES-----")
        self.scan_principal_type_details(self.role_detail_list, exclusions_cfg,
                                         modify_only)
        print("-----POLICIES-----")
        self.scan_policy_details(exclusions_cfg, modify_only)
        return self.findings.json

    def scan_policy_details(self,
                            exclusions_cfg=DEFAULT_EXCLUSIONS_CONFIG,
                            modify_only=True):
        """Scan the PolicyDetails block of the account authorization details output."""
        excluded_actions = exclusions_cfg.get("exclude-actions", None)

        for policy in self.policies.policy_details:
            print(f"Scanning policy: {policy.policy_name}")
            always_include_actions = exclusions_cfg.get("include-actions")
            actions_missing_resource_constraints = []
            if is_name_excluded(policy.policy_name,
                                exclusions_cfg.get("policies")):
                print(f"\tExcluded policy name: {policy.policy_name}")
            elif is_name_excluded(policy.full_policy_path,
                                  exclusions_cfg.get("policies")):
                print(f"\tExcluded policy path: {policy.full_policy_path}")
            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(
                                    always_include_actions))
                    else:
                        if statement.effect == "Allow":
                            actions_missing_resource_constraints.extend(
                                statement.missing_resource_constraints)
                if actions_missing_resource_constraints:
                    actions_missing_resource_constraints = list(
                        dict.fromkeys(actions_missing_resource_constraints)
                    )  # remove duplicates
                    actions_missing_resource_constraints.sort()
                    finding = Finding(
                        policy_name=policy.policy_name,
                        arn=policy.arn,
                        actions=actions_missing_resource_constraints,
                        policy_document=policy.policy_document,
                        always_exclude_actions=excluded_actions)
                    self.findings.add(finding)

    def scan_principal_type_details(
        self,
        principal_type_detail_list,
        exclusions_cfg=DEFAULT_EXCLUSIONS_CONFIG,
        modify_only=True,
    ):
        """Scan the UserDetailList, GroupDetailList, or RoleDetailList blocks of the account authorization details output."""
        excluded_actions = exclusions_cfg.get("exclude-actions", None)

        for principal in principal_type_detail_list.principals:
            always_include_actions = exclusions_cfg.get("include-actions")
            print(f"Scanning {principal.principal_type}: {principal.name}")
            for policy in principal.policy_list:
                print(f"\tScanning Policy: {policy['PolicyName']}")

                if is_name_excluded(policy["PolicyName"],
                                    exclusions_cfg.get("policies")):
                    print(f"\tExcluded policy name: {policy['PolicyName']}")
                elif principal.is_principal_excluded(exclusions_cfg):
                    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(
                                        always_include_actions))
                        else:
                            if statement.effect == "Allow":
                                actions_missing_resource_constraints.extend(
                                    statement.missing_resource_constraints)
                    if actions_missing_resource_constraints:
                        finding = Finding(
                            policy_name=policy["PolicyName"],
                            arn=principal.arn,
                            actions=actions_missing_resource_constraints,
                            policy_document=policy["PolicyDocument"],
                            assume_role_policy_document=principal.
                            assume_role_policy_document,
                            always_exclude_actions=excluded_actions)
                        self.findings.add(finding)
コード例 #8
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)
コード例 #9
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)