nccgroup / PMapper

A tool for quickly evaluating IAM permissions in AWS.
GNU Affero General Public License v3.0
1.37k stars 169 forks source link

cross account authorization checks do not evaluate resource policies #102

Closed jsimoni closed 2 years ago

jsimoni commented 2 years ago

Describe the bug The cross account authorization checks do not evaluate resource policies https://github.com/nccgroup/PMapper/wiki/Frequently-Asked-Questions#how-do-i-do-cross-account-authorization-checks I have an IAM User in Account A with the AWS managed AdministratorAccess policy attached. In Account B, I have an S3 bucket with no resource policy defined. The effective permissions is that my IAM User does not have any permissions on the S3 bucket since AWS requires an explicit Allow in the resource policy in order to grant cross-account permissions. However, PMapper is reporting that: user/InAccountA IS authorized to call action s3:GetObject for resource arn:aws:s3:::eu-west-2-in-account-b

To Reproduce edges = get_edges_between_graphs(root_graph, prod_graph) for node in root_graph.nodes: result = search_authorization_across_accounts([(root_graph, []), (prod_graph, [])], edges, node, ACTION, RESOURCE, {}) if result.allowed == True: result.print_result(ACTION, RESOURCE)

Expected behavior PMapper to evaluate the resource policy (including the AWS inferred rules) for cross-account access and correctly report wether the user has permission to perform the specified action on the specified resource.

ncc-erik-steringer commented 2 years ago

Hi there @jsimoni ,

Looking at that code, I think I've spotted the root cause. When invoking search_authorization_across_accounts, there are parameters named resource_policy and resource_owner. You'll need to specify these parameters to get accurate behavior. If your S3 bucket does not specify a bucket policy, you'll need to include a "stub" policy like so:

from principalmapper.common import Policy

acct_b_s3_bucket_stub_policy = Policy(
    'arn:aws:s3:::eu-west-2-in-account-b',
    'graph-2-bucket',
    {
        'Version': '2012-10-17',
        'Statement': []
    }
)
jsimoni commented 2 years ago

that seemed to be the issue...

root_graph = graph_actions.get_graph_from_disk(os.path.join(get_default_graph_path(SOURCE_ACCOUNT_ID)))
prod_graph = graph_actions.get_graph_from_disk(os.path.join(get_default_graph_path(DEST_ACCOUNT_ID)))

edges = get_edges_between_graphs(root_graph, prod_graph)

for node in root_graph.nodes:
    if INCLUDE_RESOURCE_POLICY:
        resource_policy = query_utils.pull_cached_resource_policy_by_arn(
                    prod_graph,
                    arn=RESOURCE,
                    query=None
                )
        if isinstance(resource_policy, Policy):
            resource_policy = resource_policy.policy_doc
        result = search_authorization_across_accounts([(root_graph, []), (prod_graph, [])], edges, node, ACTION, RESOURCE, {}, resource_policy, DEST_ACCOUNT_ID)
    else:
        result = search_authorization_across_accounts([(root_graph, []), (prod_graph, [])], edges, node, ACTION, RESOURCE, {})

    if result.allowed == True:
        result.print_result(ACTION, RESOURCE)