Translated ['', 'src/pentesting-web/dependency-confusion.md'] to es

This commit is contained in:
Translator 2025-08-21 02:40:28 +00:00
parent d0b82edd14
commit 1631a239a3

View File

@ -5,41 +5,268 @@
## Información Básica
En resumen, una vulnerabilidad de confusión de dependencias ocurre cuando un proyecto está utilizando una biblioteca con un **nombre mal escrito**, **inexistente** o con una **versión no especificada** y el repositorio de dependencias utilizado permite **reunir versiones actualizadas de repositorios públicos**.
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.
- **Mal escrito**: Importar **`reqests`** en lugar de `requests`
- **Inexistente**: Importar `company-logging`, una biblioteca interna que **ya no existe**
- **Versión no especificada**: Importar una biblioteca `company-requests` **interna** **existente**, pero el repositorio verifica **repos públicos** para ver si hay **versiones mayores**.
## Explotación
> [!WARNING]
> En todos los casos, el atacante solo necesita publicar un **paquete malicioso con el nombre** de las bibliotecas utilizadas por la empresa víctima.
> 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
### Mal escrito e Inexistente
Si su empresa está tratando de **importar una biblioteca que no es interna**, es muy probable que el repositorio de bibliotecas esté buscando en **repositorios públicos**. Si un atacante la ha creado, su código y las máquinas en funcionamiento probablemente estarán comprometidos.
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
### 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.
Es muy común que los desarrolladores **no especifiquen ninguna versión** de la biblioteca utilizada, o que solo especifiquen una **versión mayor**. Luego, el intérprete intentará descargar la **última versión** que cumpla con esos requisitos.\
Si la biblioteca es una **biblioteca externa conocida** (como `requests` de python), un **atacante no puede hacer mucho**, ya que no podrá crear una biblioteca llamada `requests` (a menos que sea el autor original).\
Sin embargo, si la biblioteca es **interna**, como `requests-company` en este ejemplo, si el **repositorio de la biblioteca** permite **verificar nuevas versiones también externamente**, buscará una versión más nueva disponible públicamente.\
Así que si un **atacante sabe** que la empresa está utilizando la biblioteca `requests-company` **versión 1.0.1** (permitiendo actualizaciones menores). Puede **publicar** la biblioteca `requests-company` **versión 1.0.2** y la empresa **utilizará esa biblioteca en lugar** de la interna.
## Solución de AWS
Esta vulnerabilidad fue encontrada en AWS **CodeArtifact** (lea los [**detalles en esta publicación del blog**](https://zego.engineering/dependency-confusion-in-aws-codeartifact-86b9ff68963d)).\
AWS solucionó esto permitiendo especificar si una biblioteca es interna o externa, para evitar descargar dependencias internas de repositorios externos.
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.
## Encontrar Bibliotecas Vulnerables
En la [**publicación original sobre confusión de dependencias**](https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610), el autor buscó miles de archivos package.json expuestos que contenían las dependencias de proyectos de javascript.
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 en busca de 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`.
### Go Modules
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 un ú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 la procedencia/atestaciones para hacer que la manipulación sea más detectable en el futuro.
## Referencias
- [https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610](https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610)
- [https://zego.engineering/dependency-confusion-in-aws-codeartifact-86b9ff68963d](https://zego.engineering/dependency-confusion-in-aws-codeartifact-86b9ff68963d)
- [https://learn.microsoft.com/en-us/nuget/consume-packages/package-source-mapping](https://learn.microsoft.com/en-us/nuget/consume-packages/package-source-mapping)
- [https://yarnpkg.com/configuration/yarnrc/](https://yarnpkg.com/configuration/yarnrc/)
{{#include ../banners/hacktricks-training.md}}