gcp-gcs-exfiltrate
storage.objects.get on the source bucket + storage.objects.create on a destination bucket (cross-project or attacker-controlled) constitute effective data exfiltration; achievable via storage.objects.copy (rewriteObject) which combines read+write permissions.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
?principal has EFFECTIVE storage.objects.create on a destination bucket in a different project or attacker-controlled bucket
node_type(?bucket) ==
ObjectStorage
emit
| source type | Identity |
|---|---|
| target type | Storage |
| source | ?principal |
| target | ?bucket |
| permissions | storage.objects.get storage.objects.create |
| conditions | iam_permission resource_policy |
| state logic | ACTIVE when CanReadData(?principal, ?bucket) is ACTIVE AND storage.objects.create on a writable destination bucket (ideally attacker-controlled or cross-project) is confirmed; CONDITIONAL(iam_permission) when destination bucket write rights are uncertain; POTENTIAL when no destination write path is modeled. CMEK state inherits from the CanReadData input (CONDITIONAL(key_permission) if CMEK gated). |
Narrative
{principal.name} can read objects from {bucket.name} (storage.objects.get) and write them to an attacker-controlled or cross-project bucket (storage.objects.create), enabling exfiltration via storage.objects.copy or gcloud storage cp to move data outside the project boundary.
Raw rule rules/derived/gcp/gcs.yaml
id: gcp-gcs-exfiltrate
emits: CanExfiltrate
description: storage.objects.get on the source bucket + storage.objects.create on a destination bucket
(cross-project or attacker-controlled) constitute effective data exfiltration; achievable via storage.objects.copy
(rewriteObject) which combines read+write permissions.
match:
- - principal: null
- CanReadData
- bucket: null
where:
- ?principal has EFFECTIVE storage.objects.create on a destination bucket in a different project or attacker-controlled
bucket
- node_type(?bucket) == ObjectStorage
emit:
source_type: Identity
target_type: Storage
source: ?principal
target: ?bucket
permissions:
- storage.objects.get
- storage.objects.create
conditions:
- iam_permission
- resource_policy
state_logic: ACTIVE when CanReadData(?principal, ?bucket) is ACTIVE AND storage.objects.create on a
writable destination bucket (ideally attacker-controlled or cross-project) is confirmed; CONDITIONAL(iam_permission)
when destination bucket write rights are uncertain; POTENTIAL when no destination write path is modeled.
CMEK state inherits from the CanReadData input (CONDITIONAL(key_permission) if CMEK gated).
confidence: min(contributing_confidences) * 0.92
derived_from:
- ?principal CanReadData ?bucket
- storage.objects.create on destination bucket
false_positive_note: 'Exfiltration requires write access to a destination bucket outside the source
project boundary or controlled by the attacker. The GCS JSON API storage.objects.copy (rewriteObject)
operation uses the SAME permissions required by this rule: storage.objects.get on the source AND storage.objects.create
on the destination. Both the copy operation and the gcloud storage cp command use storage.objects.get
+ storage.objects.create and are covered by this rule. A write-only path to a bucket in the SAME project
without read may indicate supply-chain risk but not exfiltration. VPC-SC perimeters that co-perimeter
source and destination projects may block cross-perimeter object copies -> BLOCKED(resource_policy).
Object lifecycle / retention does not block copy operations to a different bucket. Storage Transfer
Service (storagetransfer.*) is a separate API and requires separate permissions not modeled here.'
narrative: '{principal.name} can read objects from {bucket.name} (storage.objects.get) and write them
to an attacker-controlled or cross-project bucket (storage.objects.create), enabling exfiltration
via storage.objects.copy or gcloud storage cp to move data outside the project boundary.'