Skip to content

IAM Enumeration and Analysis Workflow

WebbinRoot edited this page Jul 14, 2026 · 6 revisions

IAM Enumeration and Analysis Workflow

This is the recommended path if your goal is permission mapping and escalation-path analysis.

Step 1: Enumerate resources and policy bindings

modules run enum_gcp --iam
modules run enum_gcp_policy_bindings --ensure-tree

If you need to constrain deep Resource Manager checks to specific IDs while retaining broad discovery:

modules run enum_gcp --modules resource-manager --iam --get --project-allowlist-file projects.txt --folder-allowlist 123456789012 --org-allowlist-file org_ids.txt

With allowlists present:

  • enum_gcp still performs broad Resource Manager discovery.
  • If no allowlist flags are supplied, all discovered resources/projects are treated as in scope.
  • non-Resource-Manager service modules only run for projects that resolve into the effective allowlist scope.
  • If only one direct allowlist type is provided, other direct resource types are out of scope unless explicitly allowlisted.
  • Parent allowlist flags constrain by descendant hierarchy depth and intersect with direct allowlists when both are present.
  • Exception: enum_gcp_policy_bindings still runs once on the final enum_gcp loop.
  • enum_gcp_policy_bindings reads cached workspace resources; if you previously enumerated resources outside the allowlist, those can still be included unless you reset/clean cache first.

enum_gcp_policy_bindings supports service scoping:

modules run enum_gcp_policy_bindings --resource-manager --storage --compute --secrets

IAM Deny Policies

Alongside the allow-policies (getIamPolicy), enum_gcp_policy_bindings also enumerates IAM deny policies (the iam.googleapis.com v2 Policies API — a separate API that can revoke permissions an allow grant would otherwise give). For every cached organization / folder / project node it lists the attached deny policies and fetches each one's rules (denied principals, denied permissions, exception principals, denial condition), caching them into the iam_deny_policies table. The iam.denypolicies.list / iam.denypolicies.get permissions are recorded as evidence per scope. It's best-effort: if the IAM API is disabled or you lack the permission, deny enumeration is skipped without affecting the allow-policy pass (a run prints [*] IAM deny policies cached: N).

# deny policies land automatically with the bindings run — inspect them via `data`:
data sql --db service "SELECT scope_type, scope_name, policy_id, denied_principals, denied_permissions FROM iam_deny_policies;"

Deny policies inherit downward: a policy attached to an org or folder applies to every descendant folder / project / resource (like allow-policy inheritance). GCPwn currently enumerates and stores them; folding their effect into the OpenGraph effective-access model (subtracting denied permissions down the hierarchy) is planned but not yet wired into the graph.

Fallback: Cloud Asset Inventory

If direct per-service IAM enumeration is denied but you hold cloudasset.*, a single Cloud Asset Inventory scan can populate the same IAM tables (iam_allow_policies, hierarchy, service accounts/keys/roles, compute instances, WIF) in one call:

modules run enum_asset_inventory --scope organizations/123456789012

--scope accepts projects/<id>, folders/<id>, or organizations/<id> (defaults to the current project). Inside enum_gcp this is opt-in — add --asset-inventory to run it in addition to everything else.

Step 2: Process IAM bindings into principal-centric summaries

modules run process_gcp_iam_bindings

Optional outputs:

modules run process_gcp_iam_bindings --csv --txt
modules run process_gcp_iam_bindings --silent --csv --txt
modules run process_gcp_iam_bindings --force-refresh-bindings

What this gives you:

  • flattened member-to-role/resource summaries
  • inheritance-aware organization/folder/project context
  • per-member rows stored for downstream reporting

Step 3: Review credential-effective permissions

creds info
creds info --csv

--csv is useful when terminal truncation hides long permission sets.

Step 4: Build OpenGraph

modules run process_og_gcpwn_data --expand-inherited --reset --out ./bh_out

--out/-o is an output directory; files are written as opengraph_<folder>.json.

Key behaviors:

  • --expand-inherited fans ancestor (org/folder) bindings down to every descendant scope. Each descendant binding node gets a @<scope>#src:<source> provenance suffix and its own dangerous edges. Without it, an ancestor binding stays attached to the ancestor only and descendants are not materialized.
  • Conditioned bindings always carry a #cond:<hash> suffix (see the conditionals FAQ in Troubleshooting). Inherited + conditioned bindings carry both: ...#src:<source>#cond:<hash>.
  • Default mode collapses dangerous CAN_* edges into ROLE_OWNER/ROLE_EDITOR. Add --include-all to keep the generic binding edges as well.

You can also build the graph directly from a Cloud Asset Inventory export file with --cai-file <export> (no prior enumeration / no SQLite); that output is exported but not persisted to the workspace.

Then import JSON into BloodHound CE.

Notes on analyze_vulns

analyze_vulns is currently a scaffold and returns:

[*] analyze_vulns is coming soon.

Use process_gcp_iam_bindings + OpenGraph for current operational analysis.

Clone this wiki locally