示例#1
0
    def test_gh_106_excluded_actions_should_not_show_in_results(self):
        """test_gh_106_excluded_actions_should_not_show_in_results: Make sure that autoscaling:SetDesiredCapacity does not show in results when it is excluded"""
        test_policy = {
            "Version":
            "2012-10-17",
            "Statement": [{
                "Sid":
                "Something",
                "Effect":
                "Allow",
                "Action": [
                    "autoscaling:SetDesiredCapacity",
                    "autoscaling:TerminateInstanceInAutoScalingGroup",
                    "autoscaling:UpdateAutoScalingGroup"
                ],
                "Resource":
                "*",
            }]
        }
        exclusions_cfg = {
            "policies": ["aws-service-role*"],
            "roles": ["aws-service-role*"],
            "users": [""],
            "include-actions": ["s3:GetObject"],
            "exclude-actions": [
                "autoscaling:SetDesiredCapacity",
                "autoscaling:TerminateInstanceInAutoScalingGroup",
                "autoscaling:UpdateAutoScalingGroup"
            ]
        }
        exclusions = Exclusions(exclusions_cfg)
        policy_document = PolicyDocument(test_policy, exclusions)
        print(policy_document.infrastructure_modification)
        self.assertEqual(policy_document.infrastructure_modification, [])

        exclusions_cfg_2 = {
            "policies": ["aws-service-role*"],
            "roles": ["aws-service-role*"],
            "users": [""],
            "include-actions": ["s3:GetObject"],
            "exclude-actions": [
                "autoscaling:SetDesiredCapacity",
                "autoscaling:TerminateInstanceInAutoScalingGroup",
            ]
        }
        exclusions_2 = Exclusions(exclusions_cfg_2)
        policy_document_2 = PolicyDocument(test_policy, exclusions_2)
        # Should still include one result
        print(policy_document_2.infrastructure_modification)
        self.assertEqual(policy_document_2.infrastructure_modification,
                         ["autoscaling:UpdateAutoScalingGroup"])
示例#2
0
    def __init__(
        self,
        policy_details: List[Dict[str, Any]],
        exclusions: Exclusions = DEFAULT_EXCLUSIONS,
        flag_conditional_statements: bool = False,
        flag_resource_arn_statements: bool = False,
    ) -> None:
        self.policy_details = []
        if not isinstance(exclusions, Exclusions):
            raise Exception(
                "The exclusions provided is not an Exclusions type object. "
                "Please supply an Exclusions object and try again.")
        self.exclusions = exclusions
        self.flag_conditional_statements = flag_conditional_statements
        self.flag_resource_arn_statements = flag_resource_arn_statements

        for policy_detail in policy_details:
            this_policy_name = policy_detail["PolicyName"]
            this_policy_id = policy_detail["PolicyId"]
            this_policy_path = policy_detail["Path"]
            # Always exclude the AWS service role policies
            if is_name_excluded(this_policy_path,
                                "aws-service-role*") or is_name_excluded(
                                    this_policy_path, "/aws-service-role*"):
                logger.debug(
                    "The %s Policy with the policy ID %s is excluded because it is "
                    "an immutable AWS Service role with a path of %s",
                    this_policy_name,
                    this_policy_id,
                    this_policy_path,
                )
                continue
            # Exclude the managed policies
            if (exclusions.is_policy_excluded(this_policy_name)
                    or exclusions.is_policy_excluded(this_policy_id)
                    or exclusions.is_policy_excluded(this_policy_path)):
                logger.debug(
                    "The %s Managed Policy with the policy ID %s and %s path is excluded.",
                    this_policy_name,
                    this_policy_id,
                    this_policy_path,
                )
                continue
            self.policy_details.append(
                ManagedPolicy(policy_detail,
                              exclusions=exclusions,
                              flag_resource_arn_statements=self.
                              flag_resource_arn_statements,
                              flag_conditional_statements=self.
                              flag_conditional_statements))
示例#3
0
 def test_finding_actions_excluded(self):
     test_policy = {
         "Version":
         "2012-10-17",
         "Statement": [{
             "Effect":
             "Allow",
             "Action":
             ["s3:GetObject", "logs:CreateLogStream", "logs:PutLogEvents"],
             "Resource":
             "*"
         }]
     }
     policy_document = PolicyDocument(test_policy)
     # (1) EXCLUDE actions
     exclusions_cfg = {
         "users": ["obama"],
         "groups": ["admin"],
         "roles": ["MyRole"],
         "policies": ["someOtherName"],
         "exclude-actions": ["logs:CreateLogStream", "logs:PutLogEvents"]
     }
     exclusions = Exclusions(exclusions_cfg)
     finding = Finding(
         policy_name="MyPolicy",
         arn="arn:aws:iam::123456789012:group/SNSNotifications",
         actions=["s3:GetObject"],
         policy_document=policy_document,
         exclusions=exclusions)
     # print(finding.actions)
     self.assertListEqual(finding.actions, ["s3:GetObject"])
 def test_excluded_actions_scan_policy_file_v2(self):
     """test_excluded_actions_scan_policy_file_v2: Test the scan_policy command when we have excluded actions"""
     test_policy = {
         "Version":
         "2012-10-17",
         "Statement": [
             {
                 "Effect": "Allow",
                 "Action": ["s3:GetObject", "iam:CreateAccessKey"],
                 "Resource": "*"
             },
         ]
     }
     expected_results_after_exclusion = {}
     exclusions_cfg_custom = {
         "users": ["MyRole"],
         "groups": ["obama"],
         "roles": ["admin"],
         "exclude-actions": ["s3:GetObject", "iam:CreateAccessKey"]
     }
     exclusions = Exclusions(exclusions_cfg_custom)
     results = scan_policy(test_policy, "test", exclusions)
     # print(json.dumps(results, indent=4))
     self.maxDiff = None
     self.assertDictEqual(results, expected_results_after_exclusion)
    def test_policy_finding_for_resource_exposure(self):
        test_policy = {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": [
                        "s3:PutObjectAcl"
                    ],
                    "Resource": "*"
                }
            ]
        }
        policy_document = PolicyDocument(test_policy)

        exclusions_cfg = dict()
        exclusions = Exclusions(exclusions_cfg)

        policy_finding = PolicyFinding(policy_document, exclusions)
        results = policy_finding.results
        expected_results = {
            "ServicesAffected": ["s3"],
            "PrivilegeEscalation": [],
            "ResourceExposure": ["s3:PutObjectAcl"],
            "DataExfiltration": [],
            "ServiceWildcard": [],
            "CredentialsExposure": [],
            "InfrastructureModification": ["s3:PutObjectAcl"],
        }
        # print(json.dumps(results, indent=4))
        self.assertDictEqual(results, expected_results)
示例#6
0
def scan(input, exclusions_file, output, all_access_levels,
         skip_open_report):  # pragma: no cover
    """
    Given the path to account authorization details files and the exclusions config file, scan all inline and
    managed policies in the account to identify actions that do not leverage resource constraints.
    """
    if exclusions_file:
        # Get the exclusions configuration
        with open(exclusions_file, "r") as yaml_file:
            try:
                exclusions_cfg = yaml.safe_load(yaml_file)
            except yaml.YAMLError as exc:
                logger.critical(exc)
        exclusions = Exclusions(exclusions_cfg)
    else:
        exclusions = DEFAULT_EXCLUSIONS

    if os.path.isfile(input):
        scan_account_authorization_file(input, exclusions, output,
                                        all_access_levels, skip_open_report)
    if os.path.isdir(input):
        logger.info(
            "The path given is a directory. Scanning for account authorization files and generating report."
        )
        input_files = get_authorization_files_in_directory(input)
        for file in input_files:
            logger.info(f"Scanning file: {file}")
            scan_account_authorization_file(file, exclusions, output,
                                            all_access_levels,
                                            skip_open_report)
示例#7
0
    def test_group_membership_finding(self):
        test_policy = {
            "Version":
            "2012-10-17",
            "Statement": [{
                "Effect": "Allow",
                "Action": ["s3:GetObject"],
                "Resource": "*"
            }]
        }
        policy_document = PolicyDocument(test_policy)

        exclusions_cfg = dict(users=["obama"],
                              groups=["admin"],
                              roles=["MyRole"],
                              policies=["AWSLambdaFullAccess"])
        exclusions = Exclusions(exclusions_cfg)
        group_finding = GroupFinding(
            policy_name="MyPolicy",
            arn="arn:aws:iam::123456789012:group/GroupShouldBeEmpty",
            actions=["s3:GetObject"],
            policy_document=policy_document,
            exclusions=exclusions,
            members=["obama"])
        result = group_finding.is_excluded(exclusions)
        self.assertListEqual(result, [])
        group_finding = GroupFinding(
            policy_name="MyPolicy",
            arn="arn:aws:iam::123456789012:group/GroupShouldBeEmpty",
            actions=["s3:GetObject"],
            policy_document=policy_document,
            exclusions=exclusions,
            members=["yolo"])
        self.assertFalse(group_finding.is_excluded(exclusions))
示例#8
0
 def test_new_exclusions_approach(self):
     exclusions_cfg = {
         "policies": ["aws-service-role*"],
         "roles": ["aws-service-role*"],
         "users": [""],
         "include-actions": ["s3:GetObject"],
         "exclude-actions": ["kms:Decrypt"]
     }
     exclusions = Exclusions(exclusions_cfg)
     test_actions_list = [
         "s3:GetObject", "kms:decrypt", "ssm:GetParameter",
         "ec2:DescribeInstances"
     ]
     result = exclusions.get_allowed_actions(test_actions_list)
     self.assertListEqual(
         result,
         ['s3:GetObject', 'ssm:GetParameter', 'ec2:DescribeInstances'])
示例#9
0
    def test_policy_name_finding(self):
        """output.new_findings.PolicyFinding"""
        test_policy = {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": [
                        "s3:GetObject"
                    ],
                    "Resource": "*"
                }
            ]
        }
        policy_document = PolicyDocument(test_policy)
        exclusions_cfg = dict(
            users=["obama"],
            groups=["admin"],
            roles=["MyRole"],
            policies=["AWSLambdaFullAccess"]
        )
        exclusions = Exclusions(exclusions_cfg)
        # (1) If the policy attached is excluded
        policy_finding = PolicyFinding(
            policy_name="AWSLambdaFullAccess",
            arn="arn:aws:iam::aws:policy/AWSLambdaFullAccess",  # Obama is excluded
            actions=["s3:GetObject"],
            policy_document=policy_document,
            exclusions=exclusions
        )
        result = policy_finding.is_excluded(exclusions)
        expected_result = ["AWSLambdaFullAccess"]
        self.assertListEqual(result, expected_result)

        # (2) Non-exclusion
        exclusions_cfg = dict(
            users=["obama"],
            groups=["admin"],
            roles=["MyRole"],
            policies=["someOtherName"]
        )
        exclusions = Exclusions(exclusions_cfg)
        result = policy_finding.is_excluded(exclusions)
        expected_result = False
        self.assertEqual(result, expected_result)
示例#10
0
    def test_is_principal_excluded(self):
        """scan.principals.Principal.is_principal_excluded"""
        example_authz_details_file = os.path.abspath(
            os.path.join(
                os.path.dirname(__file__),
                os.path.pardir,
                "files",
                "example-authz-details.json",
            ))
        with open(example_authz_details_file) as f:
            contents = f.read()
            auth_details_json = json.loads(contents)
        user_principal_detail = PrincipalDetail(
            auth_details_json["UserDetailList"][0])
        group_principal_detail = PrincipalDetail(
            auth_details_json["GroupDetailList"][0])
        role_principal_detail = PrincipalDetail(
            auth_details_json["RoleDetailList"][0])
        exclusions_cfg = {
            "users": ["obama"],
            "groups": ["admin"],
            "roles": [
                "MyRole",
            ],
        }
        exclusions = Exclusions(exclusions_cfg)
        self.assertTrue(
            user_principal_detail.is_principal_excluded(exclusions))
        self.assertTrue(
            group_principal_detail.is_principal_excluded(exclusions))
        self.assertTrue(
            role_principal_detail.is_principal_excluded(exclusions))

        # Testing these with mismatched categories
        exclusions_cfg = dict(users=["MyRole"],
                              groups=["obama"],
                              roles=["admin"])
        exclusions = Exclusions(exclusions_cfg)
        self.assertFalse(
            user_principal_detail.is_principal_excluded(exclusions))
        self.assertFalse(
            group_principal_detail.is_principal_excluded(exclusions))
        self.assertFalse(
            role_principal_detail.is_principal_excluded(exclusions))
示例#11
0
def scan_policy(policy_json, exclusions_config=DEFAULT_EXCLUSIONS_CONFIG):
    """
    Scan a policy document for missing resource constraints.

    :param policy_json: Dictionary containing the IAM policy.
    :param exclusions_config: Exclusions configuration. If none, just send an empty dictionary. Defaults to the contents of cloudsplaining.shared.default-exclusions.yml
    :return:
    """
    policy_document = PolicyDocument(policy_json)
    exclusions = Exclusions(exclusions_config)
    policy_finding = PolicyFinding(policy_document, exclusions)
    return policy_finding.results
示例#12
0
def get_exclusions(exclusions_file: Optional[str] = None) -> Exclusions:
    """Get the exclusions configuration from a file"""
    # Get the exclusions configuration
    if exclusions_file:
        with open(exclusions_file, "r") as yaml_file:
            try:
                exclusions_cfg = yaml.safe_load(yaml_file)
            except yaml.YAMLError as exc:
                logger.critical(exc)
        exclusions = Exclusions(exclusions_cfg)
    else:
        exclusions = DEFAULT_EXCLUSIONS
    return exclusions
示例#13
0
 def missing_resource_constraints(
         self, exclusions: Exclusions = DEFAULT_EXCLUSIONS) -> List[str]:
     """Return a list of any actions - regardless of access level - allowed by the statement that do not leverage
     resource constraints."""
     if not isinstance(exclusions, Exclusions):
         raise Exception(  # pragma: no cover
             "The provided exclusions is not the Exclusions object type. "
             "Please use the Exclusions object.")
     actions_missing_resource_constraints = []
     if len(self.resources) == 1 and self.resources[0] == "*":
         actions_missing_resource_constraints = self.restrictable_actions
     return exclusions.get_allowed_actions(
         actions_missing_resource_constraints)
示例#14
0
 def test_finding_actions_included(self):
     test_policy = {
         "Version": "2012-10-17",
         "Statement": [
             {
                 "Effect": "Allow",
                 "Action": [
                     "s3:GetObject",
                     "ec2:DescribeInstances",  # This is a bad thing to include, but just for the hell of it
                 ],
                 "Resource": "*"
             }
         ]
     }
     policy_document = PolicyDocument(test_policy)
     # (2) INCLUDE actions
     exclusions_cfg = {
         "users": ["obama"],
         "groups": ["admin"],
         "roles": ["MyRole"],
         "policies": ["someOtherName"],
         "include-actions": [
             "ec2:DescribeInstances"
         ],
         "exclude-actions": [
             "s3:GetObject",
         ],
     }
     exclusions = Exclusions(exclusions_cfg)
     finding = Finding(
         policy_name="MyPolicy",
         arn="arn:aws:iam::123456789012:group/SNSNotifications",
         actions=["s3:GetObject", "ec2:DescribeInstances"],
         policy_document=policy_document,
         exclusions=exclusions
     )
     # print(finding.actions)
     expected_results = [
         "ec2:DescribeInstances",
     ]
     self.assertListEqual(finding.actions, expected_results)
     group_finding = GroupFinding(
         policy_name="MyPolicy",
         arn="arn:aws:iam::123456789012:group/SNSNotifications",
         actions=["s3:GetObject", "ec2:DescribeInstances"],
         policy_document=policy_document,
         exclusions=exclusions,
         members=None
     )
     self.assertListEqual(group_finding.actions, expected_results)
示例#15
0
def scan_policy_file(input, exclusions_file, high_priority_only):  # pragma: no cover
    """Scan a single policy file to identify missing resource constraints."""
    file = input
    # Get the exclusions configuration
    with open(exclusions_file, "r") as yaml_file:
        try:
            exclusions_cfg = yaml.safe_load(yaml_file)
        except yaml.YAMLError as exc:
            logger.critical(exc)
    exclusions = Exclusions(exclusions_cfg)

    # Get the Policy
    with open(file) as json_file:
        logger.debug(f"Opening {file}")
        policy = json.load(json_file)

    policy_name = Path(file).stem

    # Run the scan and get the raw data.
    results = scan_policy(policy, policy_name, exclusions)

    # There will only be one finding in the results but it is in a list.
    results_exist = 0
    for finding in results:
        if finding["PrivilegeEscalation"]:
            print(f"{RED}Issue found: Privilege Escalation{END}")
            results_exist += 1
            for item in finding["PrivilegeEscalation"]:
                print(f"- Method: {item['type']}")
                print(f"  Actions: {', '.join(item['PrivilegeEscalation'])}\n")
        if finding["DataExfiltrationActions"]:
            results_exist += 1
            print(f"{RED}Issue found: Data Exfiltration{END}")
            print(
                f"{BOLD}Actions{END}: {', '.join(finding['DataExfiltrationActions'])}\n"
            )
        if finding["PermissionsManagementActions"]:
            results_exist += 1
            print(f"{RED}Issue found: Resource Exposure{END}")
            print(
                f"{BOLD}Actions{END}: {', '.join(finding['PermissionsManagementActions'])}\n"
            )
        if not high_priority_only:
            results_exist += 1
            print(f"{RED}Issue found: Unrestricted Infrastructure Modification{END}")
            print(f"{BOLD}Actions{END}: {', '.join(finding['Actions'])}")
    if results_exist == 0:
        print("There were no results found.")
示例#16
0
def scan_policy(
    policy_json: Dict[str, Any],
    exclusions_config: Dict[str, List[str]] = DEFAULT_EXCLUSIONS_CONFIG,
    flag_conditional_statements: bool = False,
    flag_resource_arn_statements: bool = False,
) -> Dict[str, Any]:
    """
    Scan a policy document for missing resource constraints.

    :param policy_json: Dictionary containing the IAM policy.
    :param exclusions_config: Exclusions configuration. If none, just send an empty dictionary. Defaults to the contents of cloudsplaining.shared.default-exclusions.yml
    :param flag_resource_arn_statements:
    :param flag_conditional_statements:
    :return:
    """
    exclusions = Exclusions(exclusions_config)
    policy_document = PolicyDocument(policy_json, exclusions=exclusions, flag_resource_arn_statements=flag_resource_arn_statements, flag_conditional_statements=flag_conditional_statements)
    policy_finding = PolicyFinding(policy_document, exclusions)
    return policy_finding.results
示例#17
0
 def missing_resource_constraints(
         self, exclusions: Exclusions = DEFAULT_EXCLUSIONS) -> List[str]:
     """Return a list of any actions - regardless of access level - allowed by the statement that do not leverage
     resource constraints."""
     if not isinstance(exclusions, Exclusions):
         raise Exception(  # pragma: no cover
             "The provided exclusions is not the Exclusions object type. "
             "Please use the Exclusions object.")
     actions_missing_resource_constraints = []
     if len(self.resources) == 1 and self.resources[0] == "*":
         actions_missing_resource_constraints = self.restrictable_actions
     # Fix #390 - if flag_resource_arn_statements is True, then let's treat this as missing resource constraints so we can flag the action anyway.
     elif self.flag_resource_arn_statements:
         actions_missing_resource_constraints = self.restrictable_actions
     else:
         pass
     result = exclusions.get_allowed_actions(
         actions_missing_resource_constraints)
     result.sort()
     return result
示例#18
0
    def test_policy_finding_for_data_exfiltration(self):
        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)
        policy_finding = PolicyFinding(policy_document, exclusions)
        results = policy_finding.results
        expected_results = {
            "ServicesAffected": ["s3"],
            "PrivilegeEscalation": [],
            "ResourceExposure": [],
            "DataExfiltration": [
                "s3:GetObject"
            ],
            "ServiceWildcard": [],
            "CredentialsExposure": [],
            "InfrastructureModification": [],
        }
        # print(json.dumps(results, indent=4))
        self.assertDictEqual(results, expected_results)
示例#19
0
def scan(input_file, exclusions_file, output,
         skip_open_report):  # pragma: no cover
    """
    Given the path to account authorization details files and the exclusions config file, scan all inline and
    managed policies in the account to identify actions that do not leverage resource constraints.
    """
    if exclusions_file:
        # Get the exclusions configuration
        with open(exclusions_file, "r") as yaml_file:
            try:
                exclusions_cfg = yaml.safe_load(yaml_file)
            except yaml.YAMLError as exc:
                logger.critical(exc)
        exclusions = Exclusions(exclusions_cfg)
    else:
        exclusions = DEFAULT_EXCLUSIONS

    if os.path.isfile(input_file):
        account_name = Path(input_file).stem
        with open(input_file) as f:
            contents = f.read()
            account_authorization_details_cfg = json.loads(contents)
        rendered_html_report = scan_account_authorization_details(
            account_authorization_details_cfg,
            exclusions,
            account_name,
            output,
            write_data_files=True)
        html_output_file = os.path.join(output,
                                        f"iam-report-{account_name}.html")
        logger.info("Saving the report to %s", html_output_file)
        if os.path.exists(html_output_file):
            os.remove(html_output_file)

        with open(html_output_file, "w") as f:
            f.write(rendered_html_report)

        print(f"Wrote HTML results to: {html_output_file}")

        # Open the report by default
        if not skip_open_report:
            print("Opening the HTML report")
            url = "file://%s" % os.path.abspath(html_output_file)
            webbrowser.open(url, new=2)

    if os.path.isdir(input_file):
        logger.info(
            "The path given is a directory. Scanning for account authorization files and generating report."
        )
        input_files = get_authorization_files_in_directory(input_file)
        for file in input_files:
            logger.info(f"Scanning file: {file}")
            with open(file) as f:
                contents = f.read()
                account_authorization_details_cfg = json.loads(contents)

            account_name = Path(file).stem
            # Scan the Account Authorization Details config
            rendered_html_report = scan_account_authorization_details(
                account_authorization_details_cfg,
                exclusions,
                account_name,
                output,
                write_data_files=True)
            html_output_file = os.path.join(output,
                                            f"iam-report-{account_name}.html")
            logger.info("Saving the report to %s", html_output_file)
            if os.path.exists(html_output_file):
                os.remove(html_output_file)

            with open(html_output_file, "w") as f:
                f.write(rendered_html_report)

            print(f"Wrote HTML results to: {html_output_file}")

            # Open the report by default
            if not skip_open_report:
                print("Opening the HTML report")
                url = "file://%s" % os.path.abspath(html_output_file)
                webbrowser.open(url, new=2)
示例#20
0
 def _is_excluded(self, exclusions: Exclusions) -> bool:
     """Determine whether the principal name or principal ID is excluded"""
     return (exclusions.is_principal_excluded(self.group_name, "Group")
             or exclusions.is_principal_excluded(self.group_id, "Group")
             or exclusions.is_principal_excluded(self.path, "Group"))
示例#21
0
    def __init__(
        self,
        group_detail: Dict[str, Any],
        policy_details: ManagedPolicyDetails,
        exclusions: Exclusions = DEFAULT_EXCLUSIONS,
        flag_conditional_statements: bool = False,
        flag_resource_arn_statements: bool = False,
    ):
        """
        Initialize the GroupDetail object.

        :param group_detail: Details about a particular group
        :param policy_details: The ManagedPolicyDetails object - i.e., details about all managed policies in the account so the group can inherit those attributes
        """
        self.create_date = group_detail.get("CreateDate")
        self.arn = group_detail.get("Arn")
        self.path = group_detail["Path"]
        self.group_id = group_detail["GroupId"]
        self.group_name = group_detail["GroupName"]

        # Fix Issue #254 - Allow flagging risky actions even when there are resource constraints
        self.flag_conditional_statements = flag_conditional_statements
        self.flag_resource_arn_statements = flag_resource_arn_statements

        if not isinstance(exclusions, Exclusions):
            raise Exception(
                "The exclusions provided is not an Exclusions type object. "
                "Please supply an Exclusions object and try again.")
        self.is_excluded = self._is_excluded(exclusions)

        # Inline Policies
        self.inline_policies = []
        # If the group itself is NOT excluded, add its inline policies
        if not self.is_excluded:
            for policy_detail in group_detail.get("GroupPolicyList", []):
                policy_name = policy_detail.get("PolicyName")
                policy_document = policy_detail.get("PolicyDocument")
                policy_id = get_non_provider_id(json.dumps(policy_document))
                if not (exclusions.is_policy_excluded(policy_name)
                        or exclusions.is_policy_excluded(policy_id)):
                    # NOTE: The Exclusions were not here before the #254 fix (which was an unfiled bug I just discovered) so the presence of this might break some older unit tests. Might need to fix that.
                    inline_policy = InlinePolicy(
                        policy_detail,
                        exclusions=exclusions,
                        flag_conditional_statements=flag_conditional_statements,
                        flag_resource_arn_statements=
                        flag_resource_arn_statements)
                    self.inline_policies.append(inline_policy)

        # Managed Policies (either AWS-managed or Customer managed)
        self.attached_managed_policies = []
        # If the group itself is NOT excluded, add its AWS-managed or Customer-managed policies
        if not self.is_excluded:
            for policy in group_detail.get("AttachedManagedPolicies", []):
                arn = policy.get("PolicyArn")
                if not (exclusions.is_policy_excluded(arn)
                        or exclusions.is_policy_excluded(
                            get_full_policy_path(arn)) or
                        exclusions.is_policy_excluded(get_policy_name(arn))):
                    attached_managed_policy_details = policy_details.get_policy_detail(
                        arn)
                    self.attached_managed_policies.append(
                        attached_managed_policy_details)
示例#22
0
    def __init__(
        self,
        user_detail: Dict[str, Any],
        policy_details: ManagedPolicyDetails,
        all_group_details: GroupDetailList,
        exclusions: Exclusions = DEFAULT_EXCLUSIONS,
        flag_conditional_statements: bool = False,
        flag_resource_arn_statements: bool = False,
    ) -> None:
        """
        Initialize the UserDetail object.

        :param user_detail: Details about a particular user
        :param policy_details: The ManagedPolicyDetails object - i.e., details about all managed policies in the account
        so the user can inherit those attributes
        :param all_group_details:
        """
        self.create_date = user_detail.get("CreateDate")
        self.arn = user_detail.get("Arn")
        self.path = user_detail["Path"]
        self.user_id = user_detail["UserId"]
        self.user_name = user_detail["UserName"]

        if not isinstance(exclusions, Exclusions):
            raise Exception(
                "The exclusions provided is not an Exclusions type object. "
                "Please supply an Exclusions object and try again.")
        self.is_excluded = self._is_excluded(exclusions)

        # Fix Issue #254 - Allow flagging risky actions even when there are resource constraints
        self.flag_conditional_statements = flag_conditional_statements
        self.flag_resource_arn_statements = flag_resource_arn_statements

        # Groups
        self.groups: List[GroupDetail] = []
        group_list = user_detail.get("GroupList")
        if group_list:
            self._add_group_details(group_list, all_group_details)
        # self.inline_policies = user_detail.get("UserPolicyList")
        # self.groups = user_detail.get("GroupList")

        # Inline Policies
        self.inline_policies = []
        # If the user itself is NOT excluded, add its inline policies
        if not self.is_excluded:
            for policy_detail in user_detail.get("UserPolicyList", []):
                policy_name = policy_detail.get("PolicyName")
                policy_document = policy_detail.get("PolicyDocument")
                policy_id = get_non_provider_id(json.dumps(policy_document))
                if not (exclusions.is_policy_excluded(policy_name)
                        or exclusions.is_policy_excluded(policy_id)):
                    inline_policy = InlinePolicy(
                        policy_detail,
                        exclusions=exclusions,
                        flag_conditional_statements=flag_conditional_statements,
                        flag_resource_arn_statements=
                        flag_resource_arn_statements)
                    self.inline_policies.append(inline_policy)

        # Managed Policies (either AWS-managed or Customer managed)
        self.attached_managed_policies = []
        # If the user itself is NOT excluded, add its AWS-managed or Customer-managed policies
        if not self.is_excluded:
            for policy in user_detail.get("AttachedManagedPolicies", []):
                arn = policy.get("PolicyArn")
                if not (exclusions.is_policy_excluded(arn)
                        or exclusions.is_policy_excluded(
                            get_full_policy_path(arn)) or
                        exclusions.is_policy_excluded(get_policy_name(arn))):
                    attached_managed_policy_details = policy_details.get_policy_detail(
                        arn)
                    self.attached_managed_policies.append(
                        attached_managed_policy_details)
示例#23
0
 def _is_excluded(self, exclusions: Exclusions) -> bool:
     """Determine whether the principal name or principal ID is excluded"""
     return bool(
         exclusions.is_principal_excluded(self.user_name, "User")
         or exclusions.is_principal_excluded(self.user_id, "User")
         or exclusions.is_principal_excluded(self.path, "User"))
示例#24
0
        "files",
        "new_data_file.json",
    ))
with open(example_data_file, 'r') as json_file:
    expected_data_file = json.load(json_file)

exclusions_cfg = {
    "policies": [
        "AWSServiceRoleFor*", "*ServiceRolePolicy", "*ServiceLinkedRolePolicy",
        "AdministratorAccess", "service-role*", "aws-service-role*", "MyRole"
    ],
    "roles": ["service-role*", "aws-service-role*", "MyRole"],
    "users": ["obama"],
    "groups": [""],
    "include-actions": [
        "s3:GetObject", "ssm:GetParameter", "ssm:GetParameters",
        "ssm:GetParametersByPath", "secretsmanager:GetSecretValue",
        "rds:CopyDBSnapshot", "rds:CreateDBSnapshot"
    ],
    "exclude-actions": [""]
}
exclusions = Exclusions(exclusions_cfg)


class TestNewDataFilePolicyDetail(unittest.TestCase):
    def test_new_principal_policy_mapping(self):
        authorization_details = AuthorizationDetails(cfg, exclusions)
        results = authorization_details.results
        # print(json.dumps(results))
        self.assertDictEqual(expected_data_file, results)
 def _is_excluded(self, exclusions: Exclusions) -> bool:
     """Determine whether the policy name or policy ID is excluded"""
     return (exclusions.is_policy_excluded(self.policy_name)
             or exclusions.is_policy_excluded(self.policy_id)
             or exclusions.is_policy_excluded(self.path)
             or is_name_excluded(self.path, "/aws-service-role*"))
示例#26
0
    def test_exclusions_for_service_roles(self):
        """test_exclusions_for_service_roles: Ensuring that exclusions config of service roles are specifically
        skipped, as designed"""
        authz_file = {
            "UserDetailList": [],
            "GroupDetailList": [],
            "RoleDetailList": [
                {
                    "Path":
                    "/aws-service-role/cloudwatch-crossaccount.amazonaws.com/",
                    "RoleName":
                    "AWSServiceRoleForCloudWatchCrossAccount",
                    "RoleId":
                    "LALALALALAALALA",
                    "Arn":
                    "arn:aws:iam::115657980943:role/aws-service-role/cloudwatch-crossaccount.amazonaws.com/AWSServiceRoleForCloudWatchCrossAccount",
                    "CreateDate":
                    "2019-11-07 20:21:23+00:00",
                    "AssumeRolePolicyDocument": {
                        "Version":
                        "2012-10-17",
                        "Statement": [{
                            "Effect": "Allow",
                            "Principal": {
                                "Service":
                                "cloudwatch-crossaccount.amazonaws.com"
                            },
                            "Action": "sts:AssumeRole"
                        }]
                    },
                    "InstanceProfileList": [],
                    "RolePolicyList": [],
                    "AttachedManagedPolicies": [{
                        "PolicyName":
                        "CloudWatch-CrossAccountAccess",
                        "PolicyArn":
                        "arn:aws:iam::aws:policy/aws-service-role/CloudWatch-CrossAccountAccess"
                    }]
                },
            ],
            "Policies": [{
                "PolicyName":
                "CloudWatch-CrossAccountAccess",
                "PolicyId":
                "LALALALALAALALA",
                "Arn":
                "arn:aws:iam::aws:policy/aws-service-role/CloudWatch-CrossAccountAccess",
                # "Arn": "arn:aws:iam::aws:policy/Sup",
                "Path":
                "/aws-service-role/",
                "DefaultVersionId":
                "v1",
                "AttachmentCount":
                1,
                "PermissionsBoundaryUsageCount":
                None,
                "IsAttachable":
                True,
                "CreateDate":
                "2019-07-23 09:59:27+00:00",
                "UpdateDate":
                "2019-07-23 09:59:27+00:00",
                "PolicyVersionList": [{
                    "Document": {
                        "Version":
                        "2012-10-17",
                        # This is fake, I'm just trying to trigger a response
                        "Statement": [{
                            "Action": ["iam:*"],
                            "Resource": ["*"],
                            "Effect": "Allow"
                        }]
                    },
                    "VersionId": "v1",
                    "IsDefaultVersion": True,
                    "CreateDate": "2019-07-23 09:59:27+00:00"
                }]
            }]
        }
        exclusions_cfg = {"policies": ["aws-service-role*"]}
        exclusions = Exclusions(exclusions_cfg)
        authorization_details = AuthorizationDetails(authz_file, exclusions)
        results = authorization_details.results

        expected_results = {
            "groups": {},
            "users": {},
            "roles": {},
            "aws_managed_policies": {},
            "customer_managed_policies": {},
            "inline_policies": {},
            "exclusions": {
                "policies": ["aws-service-role*"]
            },
            "links": {}
        }
        self.assertDictEqual(results, expected_results)
示例#27
0
 def _is_excluded(self, exclusions: Exclusions) -> bool:
     """Determine whether the principal name or principal ID is excluded"""
     return (exclusions.is_principal_excluded(self.role_name, "Role")
             or exclusions.is_principal_excluded(self.role_id, "Role")
             or exclusions.is_principal_excluded(self.path, "Role")
             or is_name_excluded(self.path, "/aws-service-role*"))
示例#28
0
 def _is_excluded(self, exclusions: Exclusions) -> bool:
     """Determine whether the policy name or policy ID is excluded"""
     return bool(
         exclusions.is_policy_excluded(self.policy_name)
         or exclusions.is_policy_excluded(self.policy_id))
示例#29
0
def scan_policy_file(input_file, exclusions_file,
                     high_priority_only):  # pragma: no cover
    """Scan a single policy file to identify missing resource constraints."""
    if input_file:
        # Get the Policy
        with open(input_file) as json_file:
            logger.debug(f"Opening {input_file}")
            policy = json.load(json_file)
        policy_name = Path(input_file).stem
    else:
        try:
            policy = json.load(sys.stdin)
        except json.decoder.JSONDecodeError as j_e:
            logger.critical(j_e)
            sys.exit()
        policy_name = "StdinPolicy"

    # Get the exclusions configuration
    with open(exclusions_file, "r") as yaml_file:
        try:
            exclusions_cfg = yaml.safe_load(yaml_file)
        except yaml.YAMLError as exc:
            logger.critical(exc)
    exclusions = Exclusions(exclusions_cfg)

    # Run the scan and get the raw data.
    results = scan_policy(policy, policy_name, exclusions)

    # There will only be one finding in the results but it is in a list.
    results_exist = 0
    if results:
        if results.get("PrivilegeEscalation"):
            print(
                f"{RED}Potential Issue found: Policy is capable of Privilege Escalation{END}"
            )
            results_exist += 1
            for item in results.get("PrivilegeEscalation"):
                print(f"- Method: {item.get('type')}")
                print(
                    f"  Actions: {', '.join(item.get('PrivilegeEscalation'))}\n"
                )
        if results.get("DataExfiltrationActions"):
            results_exist += 1
            print(
                f"{RED}Potential Issue found: Policy is capable of Data Exfiltration{END}"
            )
            print(
                f"{BOLD}Actions{END}: {', '.join(results.get('DataExfiltrationActions'))}\n"
            )
        if results.get("PermissionsManagementActions"):
            results_exist += 1
            print(
                f"{RED}Potential Issue found: Policy is capable of Resource Exposure{END}"
            )
            print(
                f"{BOLD}Actions{END}: {', '.join(results.get('PermissionsManagementActions'))}\n"
            )
        if not high_priority_only:
            results_exist += 1
            print(
                f"{RED}Potential Issue found: Policy is capable of Unrestricted Infrastructure Modification{END}"
            )
            print(f"{BOLD}Actions{END}: {', '.join(results.get('Actions'))}")
        if results_exist == 0:
            print("There were no results found.")
    else:
        print("There were no results found.")
示例#30
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)