GCP offensive assessment and enumeration framework.
Project description
GCPwn
Overview
GCPwn is a Google Cloud offensive security assessment framework focused on workspace-driven credential handling, service enumeration, artifact download, and graph-based attack-path analysis.
The project started as a pentesting-focused "one-stop-shop" to combine:
- broad service enumeration,
- practical exploit workflows,
- saved permission tracking over time,
- and OpenGraph output for BloodHound.
Unlike one-off scripts, GCPwn stores both resource data and discovered actions/permissions while you run modules, which makes follow-on analysis and exploit pathing much faster.
High-Level Features
- CLI UX: Interactive terminal workflow for workspaces, credentials, module execution, and exports.
- Module Model: Enumeration, exploit, unauthenticated, and processing modules across many GCP services.
- Mass Enumeration:
enum_allorchestration support (implemented undermodules/everything). - IAM Visibility:
testIamPermissionscoverage across many resources + policy binding processing. - Artifact Downloads:
--downloadsupport across multiple modules with scoped skip/include behavior. - OpenGraph / BloodHound: Graph generation for GCP principals/resources, privilege-escalation edges, inheritance expansion, and optional conditional evaluation placeholders.
- Reporting Exports: SQLite-backed data export to CSV/JSON/Excel/tree image from the CLI.
Documentation
Documentation is maintained in the GitHub Wiki:
Additional project docs:
- Contributing:
CONTRIBUTING.md - Roadmap:
ROADMAP.md
Installation TLDR
Option 1: Local 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 prettytable, default output is text):
pip install -r requirements.txt
Install optional table output support:
pip install prettytable==3.17.0
Run the tool:
python -m gcpwn
Option 2: Pip Install (PyPI)
pip3 install gcpwn
If you want pretty tables, optional table rendering dependency prettytable can be installed via:
pip3 install "gcpwn[table]"
Run the tool:
gcpwn
If your shell cannot find gcpwn, run:
python -m gcpwn
Option 3: Docker
docker build -t gcpwn .
docker run --rm -it 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.
- Load credentials (user/service) and set your target project(s).
- Start with broad enumeration:
# Common first pass
modules run enum_all --iam
# Deep IAM permission pass at org/folder/project scale
modules run enum_all --iam --all-permissions
# Deep pass + download coverage
modules run enum_all --iam --all-permissions --download
- Review what was collected:
# See your current creds (includes testIamPermission references)
creds info
# Process all IAM bindings & get IAM summary
modules run process_iam_bindings
# Create BloodHound graph of IAM permissions
modules run enum_gcp_cloud_hound_data --out output.json
# Export all enumerated data to an excel sheet
data export excel
Unauthenticated Passthrough Mode
You can run unauthenticated modules directly without entering the interactive workspace shell. This creates a workspace called PASSTHROUGH implicitly.
Examples:
# Run via installed console script
gcpwn --module unauth_apikey_enum_all_scopes --api-key AIza...
# Same flow via python module entrypoint
python -m gcpwn --module unauth_apikey_gemini_exploit --api-key AIza...
# Modules that derive targets from project context can use --project-id
python -m gcpwn --module unauth_functionbrute --project-id my-project --region us-central1
Notes:
- In passthrough mode, all commands use
--module <module_name>(regardless ofgcpwnvspython -m gcpwnentrypoint). - Passthrough mode is currently limited to
Unauthenticatedmodules. - This mode skips workspace selection/startup and uses a lightweight runtime context.
- Pass module-specific help with:
gcpwn --module <module> -h - Use normal interactive mode (
gcpwnthenmodules run ...) when you want workspace-backed caching, credential management, and full data persistence.
OpenGraph TLDR
By default the OpenGraph module ONLY graphs edges and related resource edges that are tied to privilege escalatoin paths. This default allowlist of OpenGraph escalation rules live in gcpwn/mappings/og_privilege_escalation_paths.json. You can enable differnet flags. See below for differnet flags. In general the following is probably good in most cases:
# TLDR best option
modules run enum_gcp_cloud_hound_data --expand-inheritance --reset --out Bloodhound_Output.json
Graphing Strategy
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. 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
User B --> storage_admin@project:A --> Storage Resources
Generate OpenGraph JSON:
modules run enum_gcp_cloud_hound_data --out opengraph_output.json --reset [--include-all] [--expand-inherited] [--cond-eval]
# Example
(<project-id>:example-cred)> modules run enum_gcp_cloud_hound_data --out TEST.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: +77 nodes, +179 edges
[*] Step 3: inferred_permissions (Inferred permissions graph)
[*] Completed inferred_permissions: +69 nodes, +117 edges
[*] Step 4: resource_expansion (Resource expansion graph)
[*] Completed resource_expansion: +66 nodes, +71 edges
[*] Pruned isolated service-account IAM-binding islands (pairs=15, key_islands=2, nodes=39, edges=22).
[*] Pruned orphan implied-IAM-binding nodes (implied_bindings=15, nodes=15, edges=15).
[*] Pruned isolated service-account nodes (service_accounts=43, nodes=43, edges=0).
[*] OpenGraph generation complete. Nodes: 207 | Edges: 330
[*] Saved graph JSON to TEST.json
# Pass the TEST.json into your local installation of Bloodhound
> 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]
Optional flags:
--include-all: include broader relationship output that might not be "direct priv escalations" (ex. a binding that lets you read bucket content).--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 priv escalation (or any edges really) to be called out "by default", Just edit og_privilege_escalation_paths.json and add your edge. You need to know the permissions you want to flag on. We cover adding a single permission edge below, but we support multi-permission edges as described in the wiki.
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 toscripts/build_predfined_perm_to_role_input.txt - With your own GCP creds (ex. a free GCP account), run
./build_predefined_perm_to_roles.sh build_predfined_perm_to_role_input.txt > perm_to_role_mappings.jsonas an authenticated user. This is a bash script that just gets all permissions for all predefined rules in a GCP env to see what roles map to your permission. You could alos just add it manually to hte existinggcpwn/data/core/mappings/og_permission_to_roles_map.jsonfile if you want using https://docs.cloud.google.com/iam/docs/roles-permissions - You should see the permission --> role(s) mapping in
perm_to_role_mappings.json. Replacegcpwn/data/core/mappings/og_permission_to_roles_map.jsonwithperm_to_role_mappings.json
- If your target permission (i.e.
- Add a rule definition to
og_privilege_escalation_paths.json(Note multi-permission rules are covered in the wiki). In our case, it might look like the netry below. Noteresource_scopes_possibleis where one might see a binding with those permisisions, andresource_typesare the actual resource nodes you will be drawing edges to. For example, you might seecloudkms.cryptoKeys.updateat the project level or attached directly to a key, but the end edge is going to be drawn to a key (not a project; if attached to a project gcpwn will fan out edges to keys rather than the 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 Cypher TLDR
These examples assume your OpenGraph JSON has already been imported into Neo4j/BloodHound-compatible tooling.
- See all nodes and edges
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/owneror any custom role (replaceABC_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
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:
pip install prettytable==3.17.0
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 DB by default)
data sql --db service "SELECT * FROM iam_allow_policies LIMIT 25"
# Wipe service DB rows for current workspace (destructive)
data wipe-service --yes
Dependency Inventory
Direct runtime dependencies are sourced from requirements.txt.
Core utilities
boto3>=1.34,<2(includesbotocoretransitively)pandas==3.0.2requests==2.33.1xlsxwriter==3.2.9
Google API and auth libraries
google-api-core==2.30.3google-api-python-client==2.194.0google-auth-httplib2==0.3.1google-auth-oauthlib==1.3.1
Google Cloud client libraries
google-cloud-access-approval==1.19.0google-cloud-aiplatform==1.148.0google-cloud-api-gateway==1.15.0google-cloud-api-keys==0.8.0google-cloud-appengine-admin==1.17.0google-cloud-artifact-registry==1.21.0google-cloud-batch==0.21.0google-cloud-bigquery==3.41.0google-cloud-bigtable==2.36.0google-cloud-billing==1.19.0google-cloud-build==3.36.0google-cloud-compute==1.47.0google-cloud-container==2.49.0google-cloud-dns==0.36.1google-cloud-firestore==2.27.0google-cloud-functions==1.23.0google-cloud-iam==2.22.0google-cloud-kms==3.12.0google-cloud-language==2.20.0google-cloud-logging==3.15.0google-cloud-monitoring==2.30.0google-cloud-netapp==0.9.0google-cloud-orchestration-airflow==1.20.0google-cloud-pubsub==2.37.0google-cloud-redis==2.21.0google-cloud-resource-manager==1.17.0google-cloud-run==0.16.0google-cloud-runtimeconfig==0.36.1google-cloud-scheduler==2.19.0google-cloud-secret-manager==2.27.0google-cloud-service-directory==1.17.0google-cloud-speech==2.38.0google-cloud-storage==3.10.1google-cloud-storage-transfer==1.20.0google-cloud-storageinsights==0.4.0google-cloud-tasks==2.22.0google-cloud-translate==3.26.0google-cloud-videointelligence==2.19.0google-cloud-vision==3.13.0
Vertex/GenAI support
google-genai==1.73.1
Optional extras
prettytable==3.17.0viapip install "gcpwn[table]"
Dev-only extra
pytest>=8.0viapip install "gcpwn[dev]"
Repository Layout
gcpwn/: main package root.gcpwn/__main__.py:python -m gcpwnentrypoint.gcpwn/cli/: command processor and workspace command handlers.gcpwn/core/: session/config/db/runtime/export primitives.gcpwn/modules/: service modules (everything,opengraph, service-specific modules).gcpwn/mappings/: static mapping/config data used across modules.gcpwn.wiki/: local wiki/docs copy.tests/: unit/integration/module tests.databases/: SQLite stores for workspaces, sessions, and service data.
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: NetSPI
- License: BSD-3-Clause (
LICENSE) - Contributors: PRs and issues welcome
Resources
- fwd:cloudsec 2024: https://www.youtube.com/watch?v=opvv9h3Qe0s
- DEF CON 32 Cloud Village: https://www.youtube.com/watch?v=rxXyYo1n9cw
- Introduction blog: https://www.netspi.com/blog/technical-blog/cloud-pentesting/introduction-to-gcpwn-part-1/
Credits
Built on the shoulders of giants; inspiration, code, and/or supporting research included from:
- GMap API Scanner: https://github.com/ozguralp/gmapsapiscanner
- Rhino Security: https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/
- GCPBucketBrute: https://github.com/RhinoSecurityLabs/GCPBucketBrute
- Google Cloud Python docs: https://cloud.google.com/python/docs/reference
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gcpwn-0.5.1.tar.gz.
File metadata
- Download URL: gcpwn-0.5.1.tar.gz
- Upload date:
- Size: 627.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
55ef463688664e4ce692aa54dbebfd6ee5741beeea73d6c551fd443c632903b3
|
|
| MD5 |
f2f98a68b3cdda276d16ba30ae70cb35
|
|
| BLAKE2b-256 |
8794cea11212e56e07e97952e6f358d354653cb61abcce5193ce219398f44a3a
|
Provenance
The following attestation bundles were made for gcpwn-0.5.1.tar.gz:
Publisher:
publish.yml on NetSPI/gcpwn
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gcpwn-0.5.1.tar.gz -
Subject digest:
55ef463688664e4ce692aa54dbebfd6ee5741beeea73d6c551fd443c632903b3 - Sigstore transparency entry: 1417229296
- Sigstore integration time:
-
Permalink:
NetSPI/gcpwn@71713b2ce1200137e873edaf3887e9a1bdf309b8 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/NetSPI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@71713b2ce1200137e873edaf3887e9a1bdf309b8 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file gcpwn-0.5.1-py3-none-any.whl.
File metadata
- Download URL: gcpwn-0.5.1-py3-none-any.whl
- Upload date:
- Size: 691.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9452ccc296048df852dbcdc6e36de0a333139a30dc35754ad1964d7a77c5805
|
|
| MD5 |
0d015650fbad7aaf33b12f428322c5d3
|
|
| BLAKE2b-256 |
3d5473cae58736b747a263f8343cad1fa9fedfa9f0ce6e6a117511d372f594cc
|
Provenance
The following attestation bundles were made for gcpwn-0.5.1-py3-none-any.whl:
Publisher:
publish.yml on NetSPI/gcpwn
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gcpwn-0.5.1-py3-none-any.whl -
Subject digest:
b9452ccc296048df852dbcdc6e36de0a333139a30dc35754ad1964d7a77c5805 - Sigstore transparency entry: 1417229301
- Sigstore integration time:
-
Permalink:
NetSPI/gcpwn@71713b2ce1200137e873edaf3887e9a1bdf309b8 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/NetSPI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@71713b2ce1200137e873edaf3887e9a1bdf309b8 -
Trigger Event:
workflow_dispatch
-
Statement type: