Confusión de Dependencias

Reading time: 10 minutes

tip

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

Apoya a HackTricks

Información Básica

La Confusión de Dependencias (también conocida como ataques de sustitución) ocurre cuando un gestor de paquetes resuelve un nombre de dependencia de un registro/fuente no intencionado y menos confiable (generalmente un registro público) en lugar del privado/interno previsto. Esto generalmente conduce a la instalación de un paquete controlado por un atacante.

Causas raíz comunes:

  • Typosquatting/mal escritura: Importar reqests en lugar de requests (se resuelve desde el registro público).
  • Paquete interno inexistente/abandonado: Importar company-logging que ya no existe internamente, por lo que el resolvedor busca en registros públicos y encuentra un paquete del atacante.
  • Preferencia de versión a través de múltiples registros: Importar un company-requests interno mientras se permite que el resolvedor también consulte registros públicos y prefiera la versión “mejor”/más nueva publicada públicamente por un atacante.

Idea clave: Si el resolvedor puede ver múltiples registros para el mismo nombre de paquete y se le permite elegir el candidato “mejor” globalmente, eres vulnerable a menos que restrinjas la resolución.

Explotación

warning

En todos los casos, el atacante solo necesita publicar un paquete malicioso con el mismo nombre que la dependencia que tu construcción resuelve desde un registro público. Los hooks en el momento de la instalación (por ejemplo, scripts de npm) o los caminos de código en el momento de la importación a menudo permiten la ejecución de código.

Mal escrito e Inexistente

Si tu proyecto hace referencia a una biblioteca que no está disponible en el registro privado, y tus herramientas retroceden a un registro público, un atacante puede sembrar un paquete malicioso con ese nombre en el registro público. Tus runners/máquinas de CI/dev lo buscarán y lo ejecutarán.

Versión no especificada / selección de “mejor versión” a través de índices

Los desarrolladores frecuentemente dejan versiones sin fijar o permiten rangos amplios. Cuando un resolvedor está configurado con índices internos y públicos, puede seleccionar la versión más nueva sin importar la fuente. Para nombres internos como requests-company, si el índice interno tiene 1.0.1 pero un atacante publica 1.0.2 en el registro público y tu resolvedor considera ambos, el paquete público puede ganar.

Solución de AWS

Esta vulnerabilidad fue encontrada en AWS CodeArtifact (lee los detalles en esta publicación de blog). AWS agregó controles para marcar dependencias/feeds como internas o externas para que el cliente no obtenga nombres “internos” de registros públicos ascendentes.

Encontrando Bibliotecas Vulnerables

En la publicación original sobre la confusión de dependencias, el autor buscó miles de manifiestos expuestos (por ejemplo, package.json, requirements.txt, archivos de bloqueo) para inferir nombres de paquetes internos y luego publicó paquetes de versiones superiores en registros públicos.

Manual Práctico del Atacante (para equipos rojos en pruebas autorizadas)

  • Enumerar nombres:
  • Grep repos y configuraciones de CI para archivos de manifiesto/bloqueo y espacios de nombres internos.
  • Buscar prefijos específicos de la organización (por ejemplo, @company/*, company-*, groupIds internos, patrones de ID de NuGet, rutas de módulos privados para Go, etc.).
  • Verificar la disponibilidad en registros públicos:
  • Si el nombre no está registrado públicamente, regístralo; si existe, intenta el secuestro de subdependencias apuntando a nombres transitivos internos.
  • Publicar con precedencia:
  • Elegir un semver que “gane” (por ejemplo, una versión muy alta) o que coincida con las reglas del resolvedor.
  • Incluir ejecución mínima en el momento de la instalación donde sea aplicable (por ejemplo, scripts de npm preinstall/install/postinstall). Para Python, preferir caminos de ejecución en el momento de la importación, ya que los wheels típicamente no ejecutan código arbitrario en la instalación.
  • Exfiltrar control:
  • Asegurarse de que se permita la salida desde CI a tu punto final controlado; de lo contrario, usar consultas DNS o mensajes de error como un canal lateral para probar la ejecución de código.

caution

Siempre obtén autorización por escrito, usa nombres/versiones de paquetes únicos para el compromiso y despublica inmediatamente o coordina la limpieza cuando concluyan las pruebas.

Manual del Defensor (lo que realmente previene la confusión)

Estrategias de alto nivel que funcionan en todos los ecosistemas:

  • Usar espacios de nombres internos únicos y vincularlos a un solo registro.
  • Evitar mezclar niveles de confianza en el momento de la resolución. Preferir un solo registro interno que actúe como proxy para paquetes públicos aprobados en lugar de dar a los gestores de paquetes tanto puntos finales internos como públicos.
  • Para gestores que lo soporten, mapear paquetes a fuentes específicas (sin “mejor versión” global a través de registros).
  • Fijar y bloquear:
  • Usar archivos de bloqueo que registren las URL de registro resueltas (npm/yarn/pnpm) o usar fijación de hash/atestación (pip --require-hashes, verificación de dependencias de Gradle).
  • Bloquear la retroalimentación pública para nombres internos en la capa de registro/red.
  • Reservar tus nombres internos en registros públicos cuando sea posible para prevenir futuros squats.

Notas del Ecosistema y Fragmentos de Configuración Segura

A continuación se presentan configuraciones pragmáticas y mínimas para reducir o eliminar la confusión de dependencias. Preferir hacer cumplir estas en entornos de CI y desarrolladores.

JavaScript/TypeScript (npm, Yarn, pnpm)

  • Usar paquetes con alcance para todo el código interno y fijar el alcance a tu registro privado.
  • Mantener instalaciones inmutables en CI (archivo de bloqueo de npm, yarn install --immutable).

.npmrc (nivel de proyecto)

# Bind internal scope to private registry; do not allow public fallback for @company/*
@company:registry=https://registry.corp.example/npm/
# Always authenticate to the private registry
//registry.corp.example/npm/:_authToken=${NPM_TOKEN}
strict-ssl=true

package.json (para paquete interno)

{
"name": "@company/api-client",
"version": "1.2.3",
"private": false,
"publishConfig": {
"registry": "https://registry.corp.example/npm/",
"access": "restricted"
}
}

Yarn Berry (.yarnrc.yml)

npmScopes:
company:
npmRegistryServer: "https://registry.corp.example/npm/"
npmAlwaysAuth: true
# CI should fail if lockfile would change
enableImmutableInstalls: true

Consejos operativos:

  • Solo publica paquetes internos dentro del alcance @company.
  • Para paquetes de terceros, permite el registro público a través de tu proxy/espejo privado, no directamente desde los clientes.
  • Considera habilitar la procedencia de paquetes npm para los paquetes públicos que publiques para aumentar la trazabilidad (no previene la confusión por sí mismo).

Python (pip / Poetry)

Regla principal: No uses --extra-index-url para mezclar niveles de confianza. O bien:

  • Expón un único índice interno que proxy y almacene en caché los paquetes aprobados de PyPI, o
  • Usa selección de índice explícita y fijación de hash.

pip.conf

[global]
index-url = https://pypi.corp.example/simple
# Disallow source distributions when possible
only-binary = :all:
# Lock with hashes generated via pip-tools
require-hashes = true

Generar requisitos hash con pip-tools:

# From pyproject.toml or requirements.in
pip-compile --generate-hashes -o requirements.txt
pip install --require-hashes -r requirements.txt

Si debes acceder a PyPI público, hazlo a través de tu proxy interno y mantén una lista de permitidos explícita allí. Evita --extra-index-url en CI.

.NET (NuGet)

Utiliza el Mapeo de Fuentes de Paquetes para vincular patrones de ID de paquetes a fuentes explícitas y prevenir la resolución desde feeds inesperados.

nuget.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="corp" value="https://nuget.corp.example/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
<packageSource key="corp">
<package pattern="Company.*" />
<package pattern="Internal.Utilities" />
</packageSource>
</packageSourceMapping>
</configuration>

Java (Maven/Gradle)

Maven settings.xml (reflejar todo a interno; deshabilitar repos ad-hoc en POMs a través de Enforcer):

<settings>
<mirrors>
<mirror>
<id>internal-mirror</id>
<mirrorOf>*</mirrorOf>
<url>https://maven.corp.example/repository/group</url>
</mirror>
</mirrors>
</settings>

Agrega Enforcer para prohibir repositorios declarados en POMs y forzar el uso de tu espejo:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<id>enforce-no-repositories</id>
<goals><goal>enforce</goal></goals>
<configuration>
<rules>
<requireNoRepositories />
</rules>
</configuration>
</execution>
</executions>
</plugin>

Gradle: Centralizar y bloquear dependencias.

  • Hacer cumplir los repositorios en settings.gradle(.kts) solamente:
dependencyResolutionManagement {
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
repositories {
maven { url = uri("https://maven.corp.example/repository/group") }
}
}
  • Habilitar la verificación de dependencias (sumas de verificación/firmas) y confirmar gradle/verification-metadata.xml.

Módulos Go

Configurar módulos privados para que no se utilicen el proxy público y la base de datos de sumas de verificación para ellos.

# Use corporate proxy first, then public proxy as fallback
export GOPROXY=https://goproxy.corp.example,https://proxy.golang.org
# Mark private paths to skip proxy and checksum db
export GOPRIVATE=*.corp.example.com,github.com/your-org/*
export GONOSUMDB=*.corp.example.com,github.com/your-org/*

Rust (Cargo)

Reemplace crates.io con un espejo interno aprobado o un directorio de proveedores para las compilaciones; no permita una recuperación pública arbitraria.

.cargo/config.toml

[source.crates-io]
replace-with = "corp-mirror"

[source.corp-mirror]
registry = "https://crates-mirror.corp.example/index"

Para la publicación, sé explícito con --registry y mantén las credenciales limitadas al registro objetivo.

Ruby (Bundler)

Utiliza bloques de origen y desactiva los Gemfiles de múltiples fuentes para que los gems provengan solo del repositorio previsto.

Gemfile

source "https://gems.corp.example"

source "https://rubygems.org" do
gem "rails"
gem "pg"
end

source "https://gems.corp.example" do
gem "company-logging"
end

Hacer cumplir a nivel de configuración:

bundle config set disable_multisource true

CI/CD y controles de registro que ayudan

  • Registro privado como único ingreso:
  • Utilizar Artifactory/Nexus/CodeArtifact/GitHub Packages/Azure Artifacts como el único punto de acceso que los desarrolladores/CI pueden alcanzar.
  • Implementar reglas de bloqueo/permisos para que los espacios de nombres internos nunca se resuelvan desde fuentes públicas ascendentes.
  • Los lockfiles son inmutables en CI:
  • npm: confirmar package-lock.json, usar npm ci.
  • Yarn: confirmar yarn.lock, usar yarn install --immutable.
  • Python: confirmar requirements.txt con hash, hacer cumplir --require-hashes.
  • Gradle: confirmar verification-metadata.xml y fallar en artefactos desconocidos.
  • Control de salida: bloquear el acceso directo desde CI a registros públicos excepto a través del proxy aprobado.
  • Reserva de nombres: pre-registrar sus nombres/espacios de nombres internos en registros públicos donde sea compatible.
  • Procedencia de paquetes / atestaciones: al publicar paquetes públicos, habilitar procedencia/atestaciones para hacer que la manipulación sea más detectable a nivel inferior.

Referencias

tip

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

Apoya a HackTricks