Ejemplo n.º 1
0
def get_managed_policy(iam, policy_arn):
    """
    Given the IAM data for an account and the ARN for a policy, 
    return the policy document
    """
    for policy in iam["Policies"]:
        if policy_arn == policy["Arn"]:
            return get_current_policy_doc(policy)
    raise Exception("Policy not found: {}".format(policy_arn))
Ejemplo n.º 2
0
def find_admins_in_account(region,
                           findings,
                           privs_to_look_for=None,
                           include_restricted=False):
    if privs_to_look_for is None:
        privs_to_look_for = [
            "iam:PutRolePolicy",
            "iam:AddUserToGroup",
            "iam:AddRoleToInstanceProfile",
            "iam:AttachGroupPolicy",
            "iam:AttachRolePolicy",
            "iam:AttachUserPolicy",
            "iam:ChangePassword",
            "iam:CreateAccessKey",
            "iam:DeleteUserPolicy",
            "iam:DetachGroupPolicy",
            "iam:DetachRolePolicy",
            "iam:DetachUserPolicy",
        ]

    account = region.account
    location = {"account": account.name}

    admins = []

    try:
        file_name = "account-data/{}/{}/{}".format(
            account.name, "us-east-1",
            "iam-get-account-authorization-details.json")
        iam = json.load(open(file_name))
    except:
        raise Exception("No IAM data for account {}".format(account.name))

    admin_policies = []
    policy_action_counts = {}
    for policy in iam["Policies"]:
        location["policy"] = policy["Arn"]
        policy_doc = get_current_policy_doc(policy)

        check_for_bad_policy(findings, region, policy["Arn"], policy_doc)

        analyzed_policy = analyze_policy_string(json.dumps(policy_doc))
        for f in analyzed_policy.findings:
            findings.add(
                Finding(
                    region,
                    "IAM_LINTER",
                    policy["Arn"],
                    resource_details={
                        "issue": str(f.issue),
                        "severity": str(f.severity),
                        "location": str(f.location),
                        "policy": policy_doc
                    },
                ))

        policy_action_counts[policy["Arn"]] = policy_action_count(
            policy_doc, location)

        if is_admin_policy(
                policy_doc,
                location,
                findings,
                region,
                privs_to_look_for,
                include_restricted,
        ):
            admin_policies.append(policy["Arn"])
            if ("arn:aws:iam::aws:policy/AdministratorAccess" in policy["Arn"]
                    or "arn:aws:iam::aws:policy/IAMFullAccess"
                    in policy["Arn"]):
                # Ignore the admin policies that are obviously admin
                continue
            if "arn:aws:iam::aws:policy" in policy["Arn"]:
                # Detects the deprecated `AmazonElasticTranscoderFullAccess`
                findings.add(
                    Finding(
                        region,
                        "IAM_MANAGED_POLICY_UNINTENTIONALLY_ALLOWING_ADMIN",
                        policy["Arn"],
                        resource_details={"policy": policy_doc},
                    ))
                continue
            findings.add(
                Finding(
                    region,
                    "IAM_CUSTOM_POLICY_ALLOWS_ADMIN",
                    policy["Arn"],
                    resource_details={"policy": policy_doc},
                ))
    location.pop("policy", None)

    # Identify roles that allow admin access
    for role in iam["RoleDetailList"]:
        location["role"] = role["Arn"]
        reasons_for_being_admin = []

        # Check if this role is an admin
        for policy in role["AttachedManagedPolicies"]:
            if policy["PolicyArn"] in admin_policies:
                reasons_for_being_admin.append(
                    "Attached managed policy: {}".format(policy["PolicyArn"]))
            if policy["PolicyArn"] in KNOWN_BAD_POLICIES:
                findings.add(
                    Finding(
                        region,
                        "IAM_KNOWN_BAD_POLICY",
                        role["Arn"],
                        resource_details={
                            "comment": KNOWN_BAD_POLICIES[policy["PolicyArn"]],
                            "policy": policy["PolicyArn"],
                        },
                    ))

        for policy in role["RolePolicyList"]:
            policy_doc = policy["PolicyDocument"]

            analyzed_policy = analyze_policy_string(json.dumps(policy_doc))
            for f in analyzed_policy.findings:
                findings.add(
                    Finding(
                        region,
                        "IAM_LINTER",
                        policy["Arn"],
                        resource_details={
                            "issue": str(f.issue),
                            "severity": str(f.severity),
                            "location": str(f.location),
                            "policy": policy_doc
                        },
                    ))

            if is_admin_policy(
                    policy_doc,
                    location,
                    findings,
                    region,
                    privs_to_look_for,
                    include_restricted,
            ):
                if ':role/OrganizationAccountAccessRole' in role['Arn']:
                    # AWS creates this role and adds an inline policy to it granting full
                    # privileges, so this just causes false positives as the purpose
                    # of this detection rule is to find unexpected admins
                    continue

                reasons_for_being_admin.append("Custom policy: {}".format(
                    policy["PolicyName"]))
                findings.add(
                    Finding(
                        region,
                        "IAM_CUSTOM_POLICY_ALLOWS_ADMIN",
                        role["Arn"],
                        resource_details={
                            "comment": "Role has custom policy allowing admin",
                            "policy": policy_doc,
                        },
                    ))

        # Check if role is accessible from anywhere
        policy = Policy(role["AssumeRolePolicyDocument"])
        if policy.is_internet_accessible():
            findings.add(
                Finding(
                    region,
                    "IAM_ROLE_ALLOWS_ASSUMPTION_FROM_ANYWHERE",
                    role["Arn"],
                    resource_details={
                        "statement": role["AssumeRolePolicyDocument"]
                    },
                ))

        # Check if anything looks malformed
        for stmt in role["AssumeRolePolicyDocument"]["Statement"]:
            if stmt["Effect"] != "Allow":
                findings.add(
                    Finding(
                        region,
                        "IAM_UNEXPECTED_FORMAT",
                        role["Arn"],
                        resource_details={
                            "comment":
                            "Unexpected Effect in AssumeRolePolicyDocument",
                            "statement": stmt,
                        },
                    ))
                continue

            if stmt["Action"] == "sts:AssumeRole":
                if len(reasons_for_being_admin) != 0:
                    # Admin assumption should be done by users or roles, not by AWS services
                    if "AWS" not in stmt["Principal"] or len(
                            stmt["Principal"]) != 1:
                        findings.add(
                            Finding(
                                region,
                                "IAM_UNEXPECTED_ADMIN_PRINCIPAL",
                                role["Arn"],
                                resource_details={
                                    "comment":
                                    "Unexpected Principal in AssumeRolePolicyDocument for an admin",
                                    "Principal": stmt["Principal"],
                                },
                            ))
            elif stmt["Action"] in [
                    "sts:AssumeRoleWithSAML", "sts:AssumeRoleWithWebIdentity"
            ]:
                continue
            else:
                findings.add(
                    Finding(
                        region,
                        "IAM_UNEXPECTED_FORMAT",
                        role["Arn"],
                        resource_details={
                            "comment":
                            "Unexpected Action in AssumeRolePolicyDocument",
                            "statement": [stmt],
                        },
                    ))

            if len(reasons_for_being_admin) != 0:
                record_admin(admins, account.name, "role", role["RoleName"])
        # TODO Should check users or other roles allowed to assume this role to show they are admins
    location.pop("role", None)

    # Identify groups that allow admin access
    admin_groups = []
    for group in iam["GroupDetailList"]:
        location["group"] = group["Arn"]
        is_admin = False
        for policy in group["AttachedManagedPolicies"]:
            if policy["PolicyArn"] in admin_policies:
                is_admin = True
                if "admin" not in group["Arn"].lower():
                    findings.add(
                        Finding(
                            region,
                            "IAM_NAME_DOES_NOT_INDICATE_ADMIN",
                            group["Arn"],
                            None,
                        ))
            if policy["PolicyArn"] in KNOWN_BAD_POLICIES:
                findings.add(
                    Finding(
                        region,
                        "IAM_KNOWN_BAD_POLICY",
                        role["Arn"],
                        resource_details={
                            "comment": KNOWN_BAD_POLICIES[policy["PolicyArn"]],
                            "policy": policy["PolicyArn"],
                        },
                    ))
        for policy in group["GroupPolicyList"]:
            policy_doc = policy["PolicyDocument"]
            if is_admin_policy(
                    policy_doc,
                    location,
                    findings,
                    region,
                    privs_to_look_for,
                    include_restricted,
            ):
                is_admin = True
                findings.add(
                    Finding(
                        region,
                        "IAM_CUSTOM_POLICY_ALLOWS_ADMIN",
                        group["Arn"],
                        resource_details={
                            "comment":
                            "Group has custom policy allowing admin",
                            "policy": policy_doc,
                        },
                    ))
        if is_admin:
            admin_groups.append(group["GroupName"])
    location.pop("group", None)

    # Check users
    for user in iam["UserDetailList"]:
        location["user"] = user["UserName"]
        reasons_for_being_admin = []

        # Check the different ways in which the user could be an admin
        for policy in user["AttachedManagedPolicies"]:
            if policy["PolicyArn"] in admin_policies:
                reasons_for_being_admin.append(
                    "Attached managed policy: {}".format(policy["PolicyArn"]))
            if policy["PolicyArn"] in KNOWN_BAD_POLICIES:
                findings.add(
                    Finding(
                        region,
                        "IAM_KNOWN_BAD_POLICY",
                        role["Arn"],
                        resource_details={
                            "comment": KNOWN_BAD_POLICIES[policy["PolicyArn"]],
                            "policy": policy["PolicyArn"],
                        },
                    ))
        for policy in user.get("UserPolicyList", []):
            policy_doc = policy["PolicyDocument"]

            analyzed_policy = analyze_policy_string(json.dumps(policy_doc))
            for f in analyzed_policy.findings:
                findings.add(
                    Finding(
                        region,
                        "IAM_LINTER",
                        policy["Arn"],
                        resource_details={
                            "issue": str(f.issue),
                            "severity": str(f.severity),
                            "location": str(f.location),
                            "policy": policy_doc
                        },
                    ))

            if is_admin_policy(
                    policy_doc,
                    location,
                    findings,
                    region,
                    privs_to_look_for,
                    include_restricted,
            ):
                reasons_for_being_admin.append("Custom user policy: {}".format(
                    policy["PolicyName"]))
                findings.add(
                    Finding(
                        region,
                        "IAM_CUSTOM_POLICY_ALLOWS_ADMIN",
                        user["UserName"],
                        resource_details={
                            "comment": "User has custom policy allowing admin",
                            "policy": policy_doc,
                        },
                    ))
        for group in user["GroupList"]:
            if group in admin_groups:
                reasons_for_being_admin.append(
                    "In admin group: {}".format(group))

        # Log them if they are an admin
        if len(reasons_for_being_admin) != 0:
            record_admin(admins, account.name, "user", user["UserName"])

    location.pop("user", None)
    return admins