Confusion de Dépendance
Reading time: 10 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Informations de Base
La Confusion de Dépendance (a.k.a. attaques de substitution) se produit lorsqu'un gestionnaire de paquets résout un nom de dépendance à partir d'un registre/source non intentionnel et moins fiable (généralement un registre public) au lieu du registre interne/privé prévu. Cela conduit généralement à l'installation d'un paquet contrôlé par un attaquant.
Causes racines courantes :
- Typosquatting/misspelling : Importer
reqests
au lieu derequests
(résout à partir du registre public). - Paquet interne inexistant/abandonné : Importer
company-logging
qui n'existe plus en interne, donc le résolveur cherche dans les registres publics et trouve un paquet de l'attaquant. - Préférence de version à travers plusieurs registres : Importer un
company-requests
interne alors que le résolveur est autorisé à interroger également des registres publics et préfère la version "meilleure"/plus récente publiée publiquement par un attaquant.
Idée clé : Si le résolveur peut voir plusieurs registres pour le même nom de paquet et est autorisé à choisir le "meilleur" candidat globalement, vous êtes vulnérable à moins de contraindre la résolution.
Exploitation
warning
Dans tous les cas, l'attaquant n'a besoin que de publier un paquet malveillant avec le même nom que la dépendance que votre build résout à partir d'un registre public. Les hooks au moment de l'installation (par exemple, les scripts npm) ou les chemins de code au moment de l'importation donnent souvent une exécution de code.
Mal Épelé & Inexistant
Si votre projet fait référence à une bibliothèque qui n'est pas disponible dans le registre privé, et que vos outils se rabattent sur un registre public, un attaquant peut semer un paquet malveillant avec ce nom dans le registre public. Vos machines de runners/CI/dev le récupéreront et l'exécuteront.
Version Non Spécifiée / Sélection de "Meilleure Version" à travers les Index
Les développeurs laissent souvent les versions non fixées ou permettent des plages larges. Lorsqu'un résolveur est configuré avec des index internes et publics, il peut sélectionner la version la plus récente indépendamment de la source. Pour des noms internes comme requests-company
, si l'index interne a 1.0.1
mais qu'un attaquant publie 1.0.2
dans le registre public et que votre résolveur considère les deux, le paquet public peut l'emporter.
Correction AWS
Cette vulnérabilité a été trouvée dans AWS CodeArtifact (lisez les détails dans cet article de blog). AWS a ajouté des contrôles pour marquer les dépendances/flux comme internes ou externes afin que le client ne récupère pas les noms "internes" des registres publics en amont.
Trouver des Bibliothèques Vulnérables
Dans le post original sur la confusion de dépendance, l'auteur a recherché des milliers de manifests exposés (par exemple, package.json
, requirements.txt
, fichiers de verrouillage) pour inférer des noms de paquets internes et a ensuite publié des paquets de version supérieure dans des registres publics.
Manuel Pratique de l'Attaquant (pour les équipes rouges lors de tests autorisés)
- Énumérer les noms :
- Grep les dépôts et les configurations CI pour des fichiers manifestes/fichiers de verrouillage et des espaces de noms internes.
- Rechercher des préfixes spécifiques à l'organisation (par exemple,
@company/*
,company-*
, groupIds internes, modèles d'ID NuGet, chemins de modules privés pour Go, etc.). - Vérifier la disponibilité dans les registres publics :
- Si le nom n'est pas enregistré publiquement, enregistrez-le ; s'il existe, tentez de détourner la sous-dépendance en ciblant des noms transitoires internes.
- Publier avec priorité :
- Choisissez un semver qui "gagne" (par exemple, une version très élevée) ou qui correspond aux règles du résolveur.
- Inclure une exécution minimale au moment de l'installation lorsque cela est applicable (par exemple, scripts npm
preinstall
/install
/postinstall
). Pour Python, préférez les chemins d'exécution au moment de l'importation, car les roues n'exécutent généralement pas de code arbitraire lors de l'installation. - Exfiltrer le contrôle :
- Assurez-vous que les sorties sont autorisées de CI vers votre point de terminaison contrôlé ; sinon, utilisez des requêtes DNS ou des messages d'erreur comme canal secondaire pour prouver l'exécution de code.
caution
Obtenez toujours une autorisation écrite, utilisez des noms/versions de paquets uniques pour l'engagement, et dépubliez immédiatement ou coordonnez le nettoyage lorsque les tests sont terminés.
Manuel du Défenseur (ce qui empêche réellement la confusion)
Stratégies de haut niveau qui fonctionnent à travers les écosystèmes :
- Utilisez des espaces de noms internes uniques et liez-les à un seul registre.
- Évitez de mélanger les niveaux de confiance au moment de la résolution. Préférez un seul registre interne qui proxy les paquets publics approuvés au lieu de donner aux gestionnaires de paquets à la fois des points de terminaison internes et publics.
- Pour les gestionnaires qui le supportent, mappez les paquets à des sources spécifiques (pas de "meilleure version" globale à travers les registres).
- Fixez et verrouillez :
- Utilisez des fichiers de verrouillage qui enregistrent les URL de registre résolues (npm/yarn/pnpm) ou utilisez le pinning par hachage/attestation (pip
--require-hashes
, vérification de dépendance Gradle). - Bloquez le retour public pour les noms internes au niveau du registre/réseau.
- Réservez vos noms internes dans les registres publics lorsque cela est possible pour prévenir les squats futurs.
Notes sur l'Écosystème et Extraits de Configuration Sécurisée
Voici des configurations pragmatiques et minimales pour réduire ou éliminer la confusion de dépendance. Préférez les appliquer dans les environnements CI et développeurs.
JavaScript/TypeScript (npm, Yarn, pnpm)
- Utilisez des paquets scoppés pour tout le code interne et fixez la portée à votre registre privé.
- Gardez les installations immuables dans CI (fichier de verrouillage npm,
yarn install --immutable
).
.npmrc (niveau projet)
# 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 (pour le package interne)
{
"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
Conseils opérationnels :
- Publiez uniquement des packages internes dans le scope
@company
. - Pour les packages tiers, autorisez le registre public via votre proxy/miroir privé, pas directement depuis les clients.
- Envisagez d'activer la provenance des packages npm pour les packages publics que vous publiez afin d'augmenter la traçabilité (cela ne prévient pas en soi la confusion).
Python (pip / Poetry)
Règle de base : N'utilisez pas --extra-index-url
pour mélanger les niveaux de confiance. Soit :
- Exposez un seul index interne qui proxy et met en cache les packages PyPI approuvés, soit
- Utilisez une sélection d'index explicite et un hachage fixe.
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
Générez des exigences hachées avec pip-tools :
# From pyproject.toml or requirements.in
pip-compile --generate-hashes -o requirements.txt
pip install --require-hashes -r requirements.txt
Si vous devez accéder à PyPI public, faites-le via votre proxy interne et maintenez une liste d'autorisation explicite. Évitez --extra-index-url
dans CI.
.NET (NuGet)
Utilisez le mappage des sources de packages pour lier les modèles d'ID de package à des sources explicites et empêcher la résolution à partir de flux inattendus.
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 (miroir tout vers interne ; interdire les dépôts ad-hoc dans les POMs via Enforcer) :
<settings>
<mirrors>
<mirror>
<id>internal-mirror</id>
<mirrorOf>*</mirrorOf>
<url>https://maven.corp.example/repository/group</url>
</mirror>
</mirrors>
</settings>
Ajoutez Enforcer pour interdire les dépôts déclarés dans les POM et forcer l'utilisation de votre miroir :
<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 : Centraliser et verrouiller les dépendances.
- Appliquer les dépôts uniquement dans
settings.gradle(.kts)
:
dependencyResolutionManagement {
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
repositories {
maven { url = uri("https://maven.corp.example/repository/group") }
}
}
- Activez la vérification des dépendances (checksums/signatures) et validez
gradle/verification-metadata.xml
.
Go Modules
Configurez des modules privés afin que le proxy public et la base de données de checksums ne soient pas utilisés pour eux.
# 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)
Remplacez crates.io par un miroir interne approuvé ou un répertoire de fournisseur pour les builds ; ne permettez pas de repli public arbitraire.
.cargo/config.toml
[source.crates-io]
replace-with = "corp-mirror"
[source.corp-mirror]
registry = "https://crates-mirror.corp.example/index"
Pour la publication, soyez explicite avec --registry
et gardez les identifiants limités au registre cible.
Ruby (Bundler)
Utilisez des blocs source et désactivez les Gemfiles multisource afin que les gems proviennent uniquement du dépôt prévu.
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
Appliquer au niveau de la configuration :
bundle config set disable_multisource true
CI/CD et contrôles de registre qui aident
- Registre privé comme unique point d'entrée :
- Utilisez Artifactory/Nexus/CodeArtifact/GitHub Packages/Azure Artifacts comme seul point d'accès pour les développeurs/CI.
- Mettez en œuvre des règles de blocage/autorisation afin que les espaces de noms internes ne se résolvent jamais à partir de sources publiques en amont.
- Les fichiers de verrouillage sont immuables dans CI :
- npm : validez
package-lock.json
, utiliseznpm ci
. - Yarn : validez
yarn.lock
, utilisezyarn install --immutable
. - Python : validez le
requirements.txt
haché, appliquez--require-hashes
. - Gradle : validez
verification-metadata.xml
et échouez sur les artefacts inconnus. - Contrôle de sortie : bloquez l'accès direct de CI aux registres publics sauf via le proxy approuvé.
- Réservation de nom : préenregistrez vos noms/noms d'espace internes dans les registres publics où cela est pris en charge.
- Provenance des paquets / attestations : lors de la publication de paquets publics, activez la provenance/attestations pour rendre la falsification plus détectable en aval.
Références
- https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
- https://zego.engineering/dependency-confusion-in-aws-codeartifact-86b9ff68963d
- https://learn.microsoft.com/en-us/nuget/consume-packages/package-source-mapping
- https://yarnpkg.com/configuration/yarnrc/
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.