gcp-bigquery-escalate-via-grant-permission
A principal with bigquery.datasets.setIamPolicy can self-grant roles/bigquery.dataOwner and escalate its data-plane privilege.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?dataset) ==
DataWarehouse # BigQuery Dataset node
?principal.privilege_level < elevated # self-grant yields elevated/admin data access
emit
| source type | Identity |
|---|---|
| target type | Identity |
| source | ?principal |
| target | ?principal |
| permissions | bigquery.datasets.setIamPolicy |
| conditions | iam_permission |
| state logic | ACTIVE when CanGrantPermission on the dataset is ACTIVE and the self-grant would yield a privilege level above the current one; inherits state from the CanGrantPermission contributor. |
Narrative
{principal.name} holds bigquery.datasets.setIamPolicy on {dataset.name} and can grant itself roles/bigquery.dataOwner, escalating to full data-plane access over all tables in {dataset.name}.
Raw rule rules/derived/gcp/bigquery.yaml
id: gcp-bigquery-escalate-via-grant-permission
emits: CanEscalateTo
description: A principal with bigquery.datasets.setIamPolicy can self-grant roles/bigquery.dataOwner and
escalate its data-plane privilege.
match:
- - principal: null
- CanGrantPermission
- dataset: null
where:
- 'node_type(?dataset) == DataWarehouse # BigQuery Dataset node'
- '?principal.privilege_level < elevated # self-grant yields elevated/admin data access'
emit:
source_type: Identity
target_type: Identity
source: ?principal
target: ?principal
permissions:
- bigquery.datasets.setIamPolicy
conditions:
- iam_permission
state_logic: ACTIVE when CanGrantPermission on the dataset is ACTIVE and the self-grant would yield
a privilege level above the current one; inherits state from the CanGrantPermission contributor.
false_positive_note: Self-escalation via setIamPolicy is only meaningful when the resulting role (e.g.
roles/bigquery.dataOwner or roles/bigquery.admin) gives the principal more data access than it currently
holds. If the principal already holds roles/bigquery.admin or equivalent, no escalation. This is a
data-plane escalation (read/exfiltrate more data), not a management-plane IAM escalation (the setIamPolicy
is scoped to BigQuery dataset resources, not the project or organization IAM). For full project/org
privilege escalation the attacker would also need resourcemanager.projects.setIamPolicy (a different
edge). can-control.yaml escalate-via-execute-as handles the CanExecuteAs path; cite, do not re-derive.
narrative: '{principal.name} holds bigquery.datasets.setIamPolicy on {dataset.name} and can grant itself
roles/bigquery.dataOwner, escalating to full data-plane access over all tables in {dataset.name}.'
derived_from:
- ?principal CanGrantPermission ?dataset
- bigquery.datasets.setIamPolicy